|
| 1 | +require 'sequel' |
| 2 | +require 'sequel/extensions/pagination' |
| 3 | + |
| 4 | +class Sequel::Model |
| 5 | +=begin |
| 6 | + # Intialize each column to the default value for new model objects |
| 7 | + def after_initialize |
| 8 | + super |
| 9 | + model.columns.each do |x| |
| 10 | + if !@values.include?(x) && db_schema[x][:allow_null] |
| 11 | + send("#{x}=", db_schema[x][:ruby_default]) |
| 12 | + end |
| 13 | + end |
| 14 | + end |
| 15 | +=end |
| 16 | + |
| 17 | + # Return an empty array for *_to_many association methods for new model objects |
| 18 | + def _load_associated_objects(opts) |
| 19 | + opts.returns_array? && new? ? [] : super |
| 20 | + end |
| 21 | +end |
| 22 | + |
| 23 | +module MerbAdmin |
| 24 | + class AbstractModel |
| 25 | + module SequelSupport |
| 26 | + def get(id) |
| 27 | + model.first(:id => id).extend(InstanceMethods) |
| 28 | + end |
| 29 | + |
| 30 | + def count(options = {}) |
| 31 | + if options[:conditions] && !options[:conditions].empty? |
| 32 | + # If options[:conditions] isn't cloned, Sequel eats the first condition! |
| 33 | + model.where(options[:conditions].clone).count |
| 34 | + else |
| 35 | + model.count |
| 36 | + end |
| 37 | + end |
| 38 | + |
| 39 | + def first(options = {}) |
| 40 | + sort = options.delete(:sort) || :id |
| 41 | + sort_order = options.delete(:sort_reverse) ? :desc : :asc |
| 42 | + |
| 43 | + if options[:conditions] && !options[:conditions].empty? |
| 44 | + # If options[:conditions] isn't cloned, Sequel eats the first condition! |
| 45 | + model.order(sort.to_sym.send(sort_order)).first(options[:conditions].clone).extend(InstanceMethods) |
| 46 | + else |
| 47 | + model.order(sort.to_sym.send(sort_order)).first.extend(InstanceMethods) |
| 48 | + end |
| 49 | + end |
| 50 | + |
| 51 | + def all(options = {}) |
| 52 | + offset = options.delete(:offset) |
| 53 | + limit = options.delete(:limit) |
| 54 | + |
| 55 | + sort = options.delete(:sort) || :id |
| 56 | + sort_order = options.delete(:sort_reverse) ? :desc : :asc |
| 57 | + |
| 58 | + if options[:conditions] && !options[:conditions].empty? |
| 59 | + # If options[:conditions] isn't cloned, Sequel eats the first condition! |
| 60 | + model.where(options[:conditions].clone).order(sort.to_sym.send(sort_order)) |
| 61 | + else |
| 62 | + model.order(sort.to_sym.send(sort_order)) |
| 63 | + end |
| 64 | + end |
| 65 | + |
| 66 | + def paginated(options = {}) |
| 67 | + page = options.delete(:page) || 1 |
| 68 | + per_page = options.delete(:per_page) || MerbAdmin[:per_page] |
| 69 | + page_count = (count(options).to_f / per_page).ceil |
| 70 | + |
| 71 | + sort = options.delete(:sort) || :id |
| 72 | + sort_order = options.delete(:sort_reverse) ? :desc : :asc |
| 73 | + |
| 74 | + if options[:conditions] && !options[:conditions].empty? |
| 75 | + # If options[:conditions] isn't cloned, Sequel eats the first condition! |
| 76 | + [page_count, model.paginate(page.to_i, per_page).where(options[:conditions].clone).order(sort.to_sym.send(sort_order))] |
| 77 | + else |
| 78 | + [page_count, model.paginate(page.to_i, per_page).order(sort.to_sym.send(sort_order))] |
| 79 | + end |
| 80 | + end |
| 81 | + |
| 82 | + def create(params = {}) |
| 83 | + model.create(params) |
| 84 | + end |
| 85 | + |
| 86 | + def new(params = {}) |
| 87 | + model.new(params).extend(InstanceMethods) |
| 88 | + end |
| 89 | + |
| 90 | + def destroy_all! |
| 91 | + model.all.each do |object| |
| 92 | + object.destroy |
| 93 | + end |
| 94 | + end |
| 95 | + |
| 96 | + def has_many_associations |
| 97 | + associations.select do |association| |
| 98 | + association[:type] == :has_many |
| 99 | + end |
| 100 | + end |
| 101 | + |
| 102 | + def has_one_associations |
| 103 | + associations.select do |association| |
| 104 | + association[:type] == :has_one |
| 105 | + end |
| 106 | + end |
| 107 | + |
| 108 | + def belongs_to_associations |
| 109 | + associations.select do |association| |
| 110 | + association[:type] == :belongs_to |
| 111 | + end |
| 112 | + end |
| 113 | + |
| 114 | + def associations |
| 115 | + model.all_association_reflections.map do |association| |
| 116 | + { |
| 117 | + :name => association_name_lookup(association), |
| 118 | + :pretty_name => association_pretty_name_lookup(association), |
| 119 | + :type => association_type_lookup(association), |
| 120 | + :parent_model => association_parent_model_lookup(association), |
| 121 | + :parent_key => association_parent_key_lookup(association), |
| 122 | + :child_model => association_child_model_lookup(association), |
| 123 | + :child_key => association_child_key_lookup(association), |
| 124 | + } |
| 125 | + end |
| 126 | + end |
| 127 | + |
| 128 | + def properties |
| 129 | + model.columns.map do |property| |
| 130 | + { |
| 131 | + :name => property, |
| 132 | + :pretty_name => property.to_s.gsub(/_id$/, "").gsub("_", " ").capitalize, |
| 133 | + :type => property_type_lookup(property), |
| 134 | + :length => property_length_lookup(property), |
| 135 | + :nullable? => model.db_schema[property][:allow_null], |
| 136 | + :serial? => model.db_schema[property][:primary_key], |
| 137 | + } |
| 138 | + end |
| 139 | + end |
| 140 | + |
| 141 | + private |
| 142 | + |
| 143 | + def property_type_lookup(property) |
| 144 | + case model.db_schema[property][:db_type] |
| 145 | + when /\A(?:medium|small)?int(?:eger)?(?:\((?:\d+)\))?\z/io |
| 146 | + :integer |
| 147 | + when /\Atinyint(?:\((\d+)\))?\z/io |
| 148 | + :boolean |
| 149 | + when /\Abigint(?:\((?:\d+)\))?\z/io |
| 150 | + :integer |
| 151 | + when /\A(?:real|float|double(?: precision)?)\z/io |
| 152 | + :float |
| 153 | + when 'boolean' |
| 154 | + :boolean |
| 155 | + when /\A(?:(?:tiny|medium|long|n)?text|clob)\z/io |
| 156 | + :text |
| 157 | + when 'date' |
| 158 | + :date |
| 159 | + when /\A(?:small)?datetime\z/io |
| 160 | + :datetime |
| 161 | + when /\Atimestamp(?: with(?:out)? time zone)?\z/io |
| 162 | + :datetime |
| 163 | + when /\Atime(?: with(?:out)? time zone)?\z/io |
| 164 | + :time |
| 165 | + when /\An?char(?:acter)?(?:\((\d+)\))?\z/io |
| 166 | + :string |
| 167 | + when /\A(?:n?varchar|character varying|bpchar|string)(?:\((\d+)\))?\z/io |
| 168 | + :string |
| 169 | + when /\A(?:small)?money\z/io |
| 170 | + :big_decimal |
| 171 | + when /\A(?:decimal|numeric|number)(?:\((\d+)(?:,\s*(\d+))?\))?\z/io |
| 172 | + :big_decimal |
| 173 | + when 'year' |
| 174 | + :integer |
| 175 | + else |
| 176 | + :string |
| 177 | + end |
| 178 | + end |
| 179 | + |
| 180 | + def property_length_lookup(property) |
| 181 | + case model.db_schema[property][:db_type] |
| 182 | + when /\An?char(?:acter)?(?:\((\d+)\))?\z/io |
| 183 | + $1 ? $1.to_i : 255 |
| 184 | + when /\A(?:n?varchar|character varying|bpchar|string)(?:\((\d+)\))?\z/io |
| 185 | + $1 ? $1.to_i : 255 |
| 186 | + else |
| 187 | + nil |
| 188 | + end |
| 189 | + end |
| 190 | + |
| 191 | + def association_name_lookup(association) |
| 192 | + case association[:type] |
| 193 | + when :one_to_many |
| 194 | + if association[:one_to_one] |
| 195 | + association[:name].to_s.singularize.to_sym |
| 196 | + else |
| 197 | + association[:name] |
| 198 | + end |
| 199 | + when :many_to_one |
| 200 | + association[:name] |
| 201 | + else |
| 202 | + raise "Unknown association type" |
| 203 | + end |
| 204 | + end |
| 205 | + |
| 206 | + def association_pretty_name_lookup(association) |
| 207 | + case association[:type] |
| 208 | + when :one_to_many |
| 209 | + if association[:one_to_one] |
| 210 | + association[:name].to_s.singularize.gsub('_', ' ').capitalize |
| 211 | + else |
| 212 | + association[:name].to_s.gsub('_', ' ').capitalize |
| 213 | + end |
| 214 | + when :many_to_one |
| 215 | + association[:name].to_s.gsub('_', ' ').capitalize |
| 216 | + else |
| 217 | + raise "Unknown association type" |
| 218 | + end |
| 219 | + end |
| 220 | + |
| 221 | + def association_type_lookup(association) |
| 222 | + case association[:type] |
| 223 | + when :one_to_many |
| 224 | + if association[:one_to_one] |
| 225 | + :has_one |
| 226 | + else |
| 227 | + :has_many |
| 228 | + end |
| 229 | + when :many_to_one |
| 230 | + :belongs_to |
| 231 | + else |
| 232 | + raise "Unknown association type" |
| 233 | + end |
| 234 | + end |
| 235 | + |
| 236 | + def association_parent_model_lookup(association) |
| 237 | + case association[:type] |
| 238 | + when :one_to_many |
| 239 | + association[:model] |
| 240 | + when :many_to_one |
| 241 | + Object.const_get(association[:class_name]) |
| 242 | + else |
| 243 | + raise "Unknown association type" |
| 244 | + end |
| 245 | + end |
| 246 | + |
| 247 | + def association_parent_key_lookup(association) |
| 248 | + [:id] |
| 249 | + end |
| 250 | + |
| 251 | + def association_child_model_lookup(association) |
| 252 | + case association[:type] |
| 253 | + when :one_to_many |
| 254 | + Object.const_get(association[:class_name]) |
| 255 | + when :many_to_one |
| 256 | + association[:model] |
| 257 | + else |
| 258 | + raise "Unknown association type" |
| 259 | + end |
| 260 | + end |
| 261 | + |
| 262 | + def association_child_key_lookup(association) |
| 263 | + case association[:type] |
| 264 | + when :one_to_many |
| 265 | + association[:keys] |
| 266 | + when :many_to_one |
| 267 | + ["#{association[:class_name].snake_case}_id".to_sym] |
| 268 | + else |
| 269 | + raise "Unknown association type" |
| 270 | + end |
| 271 | + end |
| 272 | + |
| 273 | + module InstanceMethods |
| 274 | + def id |
| 275 | + super |
| 276 | + end |
| 277 | + |
| 278 | + def save |
| 279 | + super |
| 280 | + end |
| 281 | + |
| 282 | + def destroy |
| 283 | + super |
| 284 | + end |
| 285 | + |
| 286 | + def update_attributes(attributes) |
| 287 | + # NOTE: Not sure why calling update(attributes) raises |
| 288 | + # Argument Error: wrong number of arguments (1 for 0) |
| 289 | + # but this seems to work: |
| 290 | + set(attributes) |
| 291 | + save |
| 292 | + end |
| 293 | + |
| 294 | + def errors |
| 295 | + super |
| 296 | + end |
| 297 | + |
| 298 | + def clear_association(association) |
| 299 | + association.clear # FIXME! |
| 300 | + end |
| 301 | + |
| 302 | + def reset |
| 303 | + super |
| 304 | + end |
| 305 | + end |
| 306 | + |
| 307 | + end |
| 308 | + end |
| 309 | +end |
0 commit comments