Class: Model
- Inherits:
-
SiteRecord
show all
- Extended by:
- Forwardable
- Defined in:
- lib/yodel/models/core/model/model.rb
Constant Summary
AbstractRecord::CALLBACKS, AbstractRecord::FIELD_CALLBACKS, AbstractRecord::ORDERS
Instance Attribute Summary collapse
Attributes inherited from SiteRecord
#site
#changed, #errors, #stash, #typecast, #values
Instance Method Summary
collapse
-
#[](name) ⇒ Object
Simple lookup operator for models that have records with unique names.
-
#add_embed_many(name, options = {}, &block) ⇒ Object
TODO: modify versions of the association methods.
-
#add_embed_one(name, options = {}, &block) ⇒ Object
-
#add_field(name, type, options = {}) ⇒ Object
TODO: ensure field name != a public method name.
-
#add_index(name, *fields) ⇒ Object
-
#add_many(name, options = {}) ⇒ Object
-
#add_mixin(model) ⇒ Object
Add a new mixin to this model.
-
#add_one(name, options = {}) ⇒ Object
-
#all_record_fields ⇒ Object
-
#allowed_child?(other_model) ⇒ Boolean
-
#allowed_children_and_descendants ⇒ Object
---------------------------------------- Admin interface ----------------------------------------.
-
#allowed_parent?(other_model) ⇒ Boolean
Based on the list of allowed parents, returns true if the supplied model is a descendant of a valid parent of this model.
-
#ancestors ⇒ Object
---------------------------------------- Hierarchy ----------------------------------------.
-
#create_model(name, &block) ⇒ Object
Create a new model which inherits from the current model.
-
#deep_stringify_keys(hash) ⇒ Object
TODO: remove copy of this method when abstract_model is mixed in.
-
#destroy ⇒ Object
Destroys all records which are instances of this model, removes a reference to the model from the parent site, and repeats for any child models of the model.
-
#initialize(site, values = {}) ⇒ Model
constructor
-
#load(site, values) ⇒ Object
Load a record from a mongo document.
-
#modify(&block) ⇒ Object
---------------------------------------- Migrations ---------------------------------------- Convenience method for migrations, so modifications can be specified with site.model_name.modify { field ... etc. }.
-
#modify_field(name, options = {}, &block) ⇒ Object
-
#new(values = {}) ⇒ Object
-
#parents_and_mixins ⇒ Object
Combine the full set of parents and mixins in a way that doesn't duplicate models if mixins would cause a duplicate, and maintains the correct position of mixins in the inheritance tree for this model, any parents, and any mixins (and their mixins).
-
#query_association?(options) ⇒ Boolean
-
#remove_embed_many(name) ⇒ Object
-
#remove_embed_one(name) ⇒ Object
-
#remove_field(name) ⇒ Object
-
#remove_index(name) ⇒ Object
-
#remove_many(name) ⇒ Object
-
#remove_mixin(model) ⇒ Object
Remove a mixin from this model.
-
#remove_one(name) ⇒ Object
-
#root ⇒ Object
Scope to retrieve the first (or only) root record of a model under a site, e.g Page.root(site) will retrieve the root page of a site.
-
#roots ⇒ Object
Scope to retrieve all root records of a model type under a site, e.g Groups.roots(site).
-
#run_record_after_create_callbacks(record) ⇒ Object
-
#run_record_after_destroy_callbacks(record) ⇒ Object
-
#run_record_after_save_callbacks(record) ⇒ Object
-
#run_record_after_update_callbacks(record) ⇒ Object
-
#run_record_after_validation_callbacks(record) ⇒ Object
-
#run_record_before_create_callbacks(record) ⇒ Object
-
#run_record_before_destroy_callbacks(record) ⇒ Object
-
#run_record_before_save_callbacks(record) ⇒ Object
-
#run_record_before_update_callbacks(record) ⇒ Object
-
#run_record_before_validation_callbacks(record) ⇒ Object
---------------------------------------- Callbacks ---------------------------------------- TODO: use loops like in abstract record to write these functions.
-
#to_str ⇒ Object
-
#user_allowed_to?(user, action, record) ⇒ Boolean
---------------------------------------- Permissions ----------------------------------------.
-
#user_allowed_to_create?(user, record) ⇒ Boolean
-
#user_allowed_to_delete?(user, record) ⇒ Boolean
-
#user_allowed_to_update?(user, record) ⇒ Boolean
-
#user_allowed_to_view?(user, record) ⇒ Boolean
-
#valid_child?(other_model) ⇒ Boolean
-
#valid_children ⇒ Object
Returns an array of all allowed children and descendants of those children.
Methods inherited from SiteRecord
#default_values, #inspect_hash, #perform_reload, #prepare_reload_params, #site_id
Methods included from SiteModel
#scoped, #scoped_for
Methods included from MongoModel
#collection, #scoped
#embed_many, #embed_one, #field, #fields, #many, #one
Methods inherited from MongoRecord
#collection, #default_values, #fields, #id, #increment!, #inspect_hash, #load_from_mongo, #load_mongo_document, #perform_destroy, #perform_reload, #perform_save, #set_id
#changed!, #changed?, #clear_key, #default_values, #destroyed?, #eql?, #errors?, #field, #field?, #field_was, #fields, #from_json, #get, #get_meta, #get_raw, #hash, #id, #increment!, inherited, #inspect, #inspect_hash, #inspect_value, #method_missing, #new?, #prepare_reload_params, #present?, #reload, #save, #save_without_validation, #search_terms, #set, #set_meta, #set_raw, #to_json, #trigger_field_callback, #update, #valid?
Constructor Details
#initialize(site, values = {}) ⇒ Model
Returns a new instance of Model.
52
53
54
55
56
57
58
|
# File 'lib/yodel/models/core/model/model.rb', line 52
def initialize(site, values={})
@cached_records_by_name = {}
super
@unscoped = Record.scoped(site, self)
@scope = Record.scoped(site, self, 'model' => get_raw('descendants'))
@record_class = Object.module_eval(get_raw('record_class_name'))
end
|
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
in the class AbstractRecord
Instance Attribute Details
#record_class ⇒ Object
Returns the value of attribute record_class.
7
8
9
|
# File 'lib/yodel/models/core/model/model.rb', line 7
def record_class
@record_class
end
|
#unscoped ⇒ Object
Returns the value of attribute unscoped.
7
8
9
|
# File 'lib/yodel/models/core/model/model.rb', line 7
def unscoped
@unscoped
end
|
Instance Method Details
#[](name) ⇒ Object
Simple lookup operator for models that have records with unique names.
Used as if the model object was a hash: site.emails
147
148
149
150
151
152
153
154
|
# File 'lib/yodel/models/core/model/model.rb', line 147
def [](name)
unless @cached_records_by_name.key?(name)
record = self.where(name: name).first
@cached_records_by_name[name] = record
site.cached_records[record.id] = record unless record.nil?
end
@cached_records_by_name[name]
end
|
#add_embed_many(name, options = {}, &block) ⇒ Object
TODO: modify versions of the association methods
307
308
309
310
|
# File 'lib/yodel/models/core/model/model.rb', line 307
def add_embed_many(name, options={}, &block)
embedded_field = add_field(name, 'many_embedded', options)
embedded_field.instance_exec(embedded_field, &block) if block_given?
end
|
#add_embed_one(name, options = {}, &block) ⇒ Object
316
317
318
319
|
# File 'lib/yodel/models/core/model/model.rb', line 316
def add_embed_one(name, options={}, &block)
embedded_field = add_field(name, 'one_embedded', options)
embedded_field.instance_exec(embedded_field, &block) if block_given?
end
|
#add_field(name, type, options = {}) ⇒ Object
TODO: ensure field name != a public method name
269
270
271
272
273
274
275
276
277
278
279
280
281
282
|
# File 'lib/yodel/models/core/model/model.rb', line 269
def add_field(name, type, options={})
name = name.to_s
raise InvalidModelField.new("Duplicate field name") if record_fields.key?(name)
raise InvalidModelField.new("Type must be a known yodel field type") unless valid_type?(type)
raise InvalidModelField.new("Field name cannot start with an underscore") if name.start_with?('_')
field_type = Field.field_from_type(type.to_s)
field = field_type.new(name, deep_stringify_keys(options.merge(type: type.to_s)))
RecordIndex.add_index_for_field(self, field) if field.index?
record_fields[name] = field
end
|
#add_index(name, *fields) ⇒ Object
347
348
349
350
351
352
353
354
355
356
357
358
|
# File 'lib/yodel/models/core/model/model.rb', line 347
def add_index(name, *fields)
raise InvalidIndex, 'Indexes must be built on at least one field' if fields.empty?
spec = fields.collect do |field|
if field.is_a?(Array)
[field.first.to_s, (field.last == :desc) ? Mongo::DESCENDING : Mongo::ASCENDING]
else
[field.to_s, Mongo::ASCENDING]
end
end
RecordIndex.add_index_for_model(self, name, spec)
indexes << name
end
|
#add_many(name, options = {}) ⇒ Object
325
326
327
328
|
# File 'lib/yodel/models/core/model/model.rb', line 325
def add_many(name, options={})
type = query_association?(options) ? 'many_query' : 'many_store'
add_field(name, type, options)
end
|
#add_mixin(model) ⇒ Object
Add a new mixin to this model
397
398
399
400
401
402
403
404
405
|
# File 'lib/yodel/models/core/model/model.rb', line 397
def add_mixin(model)
raise InvalidMixin.new("#{model.name} already mixed in to this model") if mixins.include?(model)
raise InvalidMixin.new("Mixin cannot be a parent") if ancestors.include?(model)
model.add_descendant(self)
mixins << model
save
end
|
#add_one(name, options = {}) ⇒ Object
334
335
336
337
|
# File 'lib/yodel/models/core/model/model.rb', line 334
def add_one(name, options={})
type = query_association?(options) ? 'one_query' : 'one_store'
add_field(name, type, options)
end
|
#all_record_fields ⇒ Object
181
182
183
184
185
|
# File 'lib/yodel/models/core/model/model.rb', line 181
def all_record_fields
parents_and_mixins.each_with_object({}) do |ancestor, fields|
fields.merge! ancestor.record_fields end
end
|
#allowed_child?(other_model) ⇒ Boolean
195
196
197
|
# File 'lib/yodel/models/core/model/model.rb', line 195
def allowed_child?(other_model)
allowed_children_and_descendants.include?(other_model)
end
|
#allowed_children_and_descendants ⇒ Object
191
192
193
|
# File 'lib/yodel/models/core/model/model.rb', line 191
def allowed_children_and_descendants
allowed_children.collect(&:descendants).flatten.uniq
end
|
#allowed_parent?(other_model) ⇒ Boolean
Based on the list of allowed parents, returns true if the supplied
model is a descendant of a valid parent of this model.
201
202
203
204
|
# File 'lib/yodel/models/core/model/model.rb', line 201
def allowed_parent?(other_model)
other_model_ancestors = other_model.ancestors.to_a
allowed_parents.any? {|parent| other_model_ancestors.include?(parent)}
end
|
#ancestors ⇒ Object
160
161
162
163
164
165
166
167
168
|
# File 'lib/yodel/models/core/model/model.rb', line 160
def ancestors
next_parent = self
Enumerator.new do |models|
while next_parent
models.yield next_parent
next_parent = next_parent.parent
end
end
end
|
#create_model(name, &block) ⇒ Object
Create a new model which inherits from the current model. If supplied, a block
is run and passed a reference to the new model.
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
|
# File 'lib/yodel/models/core/model/model.rb', line 367
def create_model(name, &block)
name = name.to_s.tableize
raise "Model name '#{name}' is not unique" if site.model_types.key?(name)
child = self.class.new(site)
child.name = name.camelcase.singularize
child.parent = self
fields.each do |name, field|
child.set(name, get(name)) if field.inherited?
end
class_name = name.classify
site.model_types[name] = child.id
site.model_plural_names[class_name] = name
site.save
child.tap do |child|
child.add_descendant(child)
child.instance_exec(child, &block) if block_given?
child.save
end
end
|
#deep_stringify_keys(hash) ⇒ Object
TODO: remove copy of this method when abstract_model is mixed in
298
299
300
301
302
|
# File 'lib/yodel/models/core/model/model.rb', line 298
def deep_stringify_keys(hash)
hash.each_with_object({}) do |(key, value), new_hash|
new_hash[key.to_s] = (value.respond_to?(:to_hash) ? deep_stringify_keys(value) : value)
end
end
|
#destroy ⇒ Object
Destroys all records which are instances of this model, removes a reference to
the model from the parent site, and repeats for any child models of the model.
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
|
# File 'lib/yodel/models/core/model/model.rb', line 416
def destroy
parent.try(:remove_descendant, self)
mixins.each {|mixin| mixin.remove_descendant(self)}
children.each(&:destroy)
all.each(&:destroy)
site.model_types.delete(name.underscore.pluralize)
site.model_plural_names.delete(name)
site.save
indexes.each do |name|
RecordIndex.remove_index_for_model(self, name)
end
record_fields.each do |name, field|
RecordIndex.remove_index_for_field(self, field) if field.index?
end
super
end
|
#load(site, values) ⇒ Object
Load a record from a mongo document. If this model is not the model
of the record, the appropriate model is found and used instead.
120
121
122
123
124
125
126
127
|
# File 'lib/yodel/models/core/model/model.rb', line 120
def load(site, values)
return nil if values.nil?
if values['model'] != id
site.models.find(values['model']).load(site, values)
else
record_class.new(self, site, values, false)
end
end
|
#modify(&block) ⇒ Object
Migrations
Convenience method for migrations, so modifications can be specified with
site.model_name.modify { field ... etc. }
262
263
264
265
|
# File 'lib/yodel/models/core/model/model.rb', line 262
def modify(&block)
instance_eval &block
save
end
|
#modify_field(name, options = {}, &block) ⇒ Object
290
291
292
293
294
295
|
# File 'lib/yodel/models/core/model/model.rb', line 290
def modify_field(name, options={}, &block)
field = record_fields[name.to_s]
field.options = field.options.dup.merge(deep_stringify_keys(options))
field.instance_exec(field, &block) if block_given?
changed!('record_fields')
end
|
#new(values = {}) ⇒ Object
129
130
131
|
# File 'lib/yodel/models/core/model/model.rb', line 129
def new(values={})
record_class.new(self, site).tap {|record| record.update(values, false)}
end
|
#parents_and_mixins ⇒ Object
Combine the full set of parents and mixins in a way that doesn't duplicate models
if mixins would cause a duplicate, and maintains the correct position of mixins in
the inheritance tree for this model, any parents, and any mixins (and their mixins)
173
174
175
176
177
178
179
|
# File 'lib/yodel/models/core/model/model.rb', line 173
def parents_and_mixins
models = parent.try(:parents_and_mixins) || []
mixins.each do |mixin_model|
models |= mixin_model.parents_and_mixins
end
models << self
end
|
#query_association?(options) ⇒ Boolean
343
344
345
|
# File 'lib/yodel/models/core/model/model.rb', line 343
def query_association?(options)
options[:store] == false || [:foreign_key, :extends, :through].any? {|opt| options[opt].present?}
end
|
#remove_embed_many(name) ⇒ Object
312
313
314
|
# File 'lib/yodel/models/core/model/model.rb', line 312
def remove_embed_many(name)
remove_field(name)
end
|
#remove_embed_one(name) ⇒ Object
321
322
323
|
# File 'lib/yodel/models/core/model/model.rb', line 321
def remove_embed_one(name)
remove_field(name)
end
|
#remove_field(name) ⇒ Object
284
285
286
287
288
|
# File 'lib/yodel/models/core/model/model.rb', line 284
def remove_field(name)
field = record_fields.delete(name.to_s)
raise InvalidModelField.new("Unknown field name") if field.nil?
RecordIndex.remove_index_for_field(self, field) if field.index?
end
|
#remove_index(name) ⇒ Object
360
361
362
363
|
# File 'lib/yodel/models/core/model/model.rb', line 360
def remove_index(name)
RecordIndex.remove_index_for_model(self, name)
indexes.delete(name)
end
|
#remove_many(name) ⇒ Object
330
331
332
|
# File 'lib/yodel/models/core/model/model.rb', line 330
def remove_many(name)
remove_field(name)
end
|
#remove_mixin(model) ⇒ Object
Remove a mixin from this model
408
409
410
411
412
|
# File 'lib/yodel/models/core/model/model.rb', line 408
def remove_mixin(model)
model.remove_descendant(self)
mixins.delete(model)
save
end
|
#remove_one(name) ⇒ Object
339
340
341
|
# File 'lib/yodel/models/core/model/model.rb', line 339
def remove_one(name)
remove_field(name)
end
|
#root ⇒ Object
Scope to retrieve the first (or only) root record of a model under a
site, e.g Page.root(site) will retrieve the root page of a site
141
142
143
|
# File 'lib/yodel/models/core/model/model.rb', line 141
def root
self.where(parent: nil).order('index asc').first
end
|
#roots ⇒ Object
Scope to retrieve all root records of a model type under a site, e.g
Groups.roots(site). Returns all records with a nil parent.
135
136
137
|
# File 'lib/yodel/models/core/model/model.rb', line 135
def roots
self.where(parent: nil).order('index asc')
end
|
#run_record_after_create_callbacks(record) ⇒ Object
89
90
91
|
# File 'lib/yodel/models/core/model/model.rb', line 89
def run_record_after_create_callbacks(record)
record_after_create_callbacks.each {|fn| Function.new(fn).execute(record)}
end
|
#run_record_after_destroy_callbacks(record) ⇒ Object
105
106
107
|
# File 'lib/yodel/models/core/model/model.rb', line 105
def run_record_after_destroy_callbacks(record)
record_after_destroy_callbacks.each {|fn| Function.new(fn).execute(record)}
end
|
#run_record_after_save_callbacks(record) ⇒ Object
81
82
83
|
# File 'lib/yodel/models/core/model/model.rb', line 81
def run_record_after_save_callbacks(record)
record_after_save_callbacks.each {|fn| Function.new(fn).execute(record)}
end
|
#run_record_after_update_callbacks(record) ⇒ Object
97
98
99
|
# File 'lib/yodel/models/core/model/model.rb', line 97
def run_record_after_update_callbacks(record)
record_after_update_callbacks.each {|fn| Function.new(fn).execute(record)}
end
|
#run_record_after_validation_callbacks(record) ⇒ Object
73
74
75
|
# File 'lib/yodel/models/core/model/model.rb', line 73
def run_record_after_validation_callbacks(record)
record_after_validation_callbacks.each {|fn| Function.new(fn).execute(record)}
end
|
#run_record_before_create_callbacks(record) ⇒ Object
85
86
87
|
# File 'lib/yodel/models/core/model/model.rb', line 85
def run_record_before_create_callbacks(record)
record_before_create_callbacks.each {|fn| Function.new(fn).execute(record)}
end
|
#run_record_before_destroy_callbacks(record) ⇒ Object
101
102
103
|
# File 'lib/yodel/models/core/model/model.rb', line 101
def run_record_before_destroy_callbacks(record)
record_before_destroy_callbacks.each {|fn| Function.new(fn).execute(record)}
end
|
#run_record_before_save_callbacks(record) ⇒ Object
77
78
79
|
# File 'lib/yodel/models/core/model/model.rb', line 77
def run_record_before_save_callbacks(record)
record_before_save_callbacks.each {|fn| Function.new(fn).execute(record)}
end
|
#run_record_before_update_callbacks(record) ⇒ Object
93
94
95
|
# File 'lib/yodel/models/core/model/model.rb', line 93
def run_record_before_update_callbacks(record)
record_before_update_callbacks.each {|fn| Function.new(fn).execute(record)}
end
|
#run_record_before_validation_callbacks(record) ⇒ Object
Callbacks
TODO: use loops like in abstract record to write these functions
69
70
71
|
# File 'lib/yodel/models/core/model/model.rb', line 69
def run_record_before_validation_callbacks(record)
record_before_validation_callbacks.each {|fn| Function.new(fn).execute(record)}
end
|
#to_str ⇒ Object
60
61
62
|
# File 'lib/yodel/models/core/model/model.rb', line 60
def to_str
"#<Model: #{name}>"
end
|
#user_allowed_to?(user, action, record) ⇒ Boolean
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
|
# File 'lib/yodel/models/core/model/model.rb', line 224
def user_allowed_to?(user, action, record)
case action
when :view
group = view_group
when :update
group = update_group
when :delete
group = delete_group
when :create
group = create_group
end
return true if group.nil?
group.permitted?(user, record)
end
|
#user_allowed_to_create?(user, record) ⇒ Boolean
252
253
254
|
# File 'lib/yodel/models/core/model/model.rb', line 252
def user_allowed_to_create?(user, record)
user_allowed_to?(user, :create, record)
end
|
#user_allowed_to_delete?(user, record) ⇒ Boolean
248
249
250
|
# File 'lib/yodel/models/core/model/model.rb', line 248
def user_allowed_to_delete?(user, record)
user_allowed_to?(user, :delete, record)
end
|
#user_allowed_to_update?(user, record) ⇒ Boolean
244
245
246
|
# File 'lib/yodel/models/core/model/model.rb', line 244
def user_allowed_to_update?(user, record)
user_allowed_to?(user, :update, record)
end
|
#user_allowed_to_view?(user, record) ⇒ Boolean
240
241
242
|
# File 'lib/yodel/models/core/model/model.rb', line 240
def user_allowed_to_view?(user, record)
user_allowed_to?(user, :view, record)
end
|
#valid_child?(other_model) ⇒ Boolean
216
217
218
|
# File 'lib/yodel/models/core/model/model.rb', line 216
def valid_child?(other_model)
valid_children.include?(other_model)
end
|
#valid_children ⇒ Object
Returns an array of all allowed children and descendants of those
children. This list respects both allowed_children and allowed_parents
restrictions, so Page (which allows children that are
descendants of Page) won't include Article which can only
exist under a Blog page, even though Article is a
descendant of Page.
212
213
214
|
# File 'lib/yodel/models/core/model/model.rb', line 212
def valid_children
allowed_children_and_descendants.select {|child| child.allowed_parent?(self)}
end
|