motion_model 0.4.2 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +5 -0
- data/README.md +25 -13
- data/lib/motion_model/adapters/array_model_adapter.rb +14 -16
- data/lib/motion_model/adapters/array_model_persistence.rb +1 -1
- data/lib/motion_model/model/column.rb +60 -15
- data/lib/motion_model/model/formotion.rb +1 -1
- data/lib/motion_model/model/model.rb +213 -179
- data/lib/motion_model/validatable.rb +8 -0
- data/lib/motion_model/version.rb +1 -1
- data/motion_model.gemspec +1 -1
- data/spec/array_model_persistence_spec.rb +2 -2
- data/spec/cascading_delete_spec.rb +6 -6
- data/spec/model_spec.rb +5 -0
- data/spec/relation_spec.rb +9 -9
- metadata +52 -62
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,15 +1,16 @@
|
|
1
|
-
[](https://codeclimate.com/github/sxross/MotionModel)
|
1
|
+
[](https://codeclimate.com/github/sxross/MotionModel)[](https://travis-ci.org/sxross]/MotionModel)
|
2
2
|
|
3
|
-
MotionModel
|
3
|
+
MotionModel: Models, Relations, and Validation for RubyMotion
|
4
4
|
================
|
5
5
|
|
6
6
|
MotionModel is a DSL for cases where Core Data is too heavy to lift but you are
|
7
|
-
still intending to work with your data, its types, and its relations.
|
7
|
+
still intending to work with your data, its types, and its relations. It also provides for
|
8
|
+
data validation and actually quite a bit more.
|
8
9
|
|
9
10
|
File | Module | Description
|
10
11
|
---------------------|---------------------------|------------------------------------
|
11
12
|
**ext.rb** | N/A | Core Extensions that provide a few Rails-like niceties. Nothing new here, moving on...
|
12
|
-
**model.rb** | MotionModel::Model | You
|
13
|
+
**model.rb** | MotionModel::Model | You should read about it in "[What Model Can Do](#what-model-can-do)". Model is the raison d'etre and the centerpiece of MotionModel.
|
13
14
|
**validatable.rb** | MotionModel::Validatable | Provides a basic validation framework for any arbitrary class. You can also create custom validations to suit your app's unique needs.
|
14
15
|
**input_helpers** | MotionModel::InputHelpers | Helps hook a collection up to a data form, populate the form, and retrieve the data afterwards. Note: *MotionModel supports Formotion for input handling as well as these input helpers*.
|
15
16
|
**formotion.rb** | MotionModel::Formotion | Provides an interface between MotionModel and Formotion
|
@@ -31,7 +32,7 @@ you like with it. See the LICENSE file in this project.
|
|
31
32
|
* [Problems/Comments](#problemscomments)
|
32
33
|
* [Submissions/Patches](#submissionspatches)
|
33
34
|
|
34
|
-
|
35
|
+
Changes for Existing Users to Be Aware Of
|
35
36
|
=================
|
36
37
|
|
37
38
|
Please see the CHANGELOG for update on changes.
|
@@ -61,6 +62,8 @@ or if you are using bundler:
|
|
61
62
|
gem motion_model, "0.3.8"
|
62
63
|
```
|
63
64
|
|
65
|
+
Version 0.3.8 was the last that did not separate the model and persistence concerns.
|
66
|
+
|
64
67
|
Getting Going
|
65
68
|
================
|
66
69
|
|
@@ -88,8 +91,15 @@ then put this in your Rakefile after requiring `motion/project`:
|
|
88
91
|
require 'motion_model'
|
89
92
|
```
|
90
93
|
|
94
|
+
If you want to use Bundler from `master`, put this in your Gemfile:
|
95
|
+
|
96
|
+
```
|
97
|
+
gem 'motion_model', :git => 'git@github.com:sxross/MotionModel.git'
|
98
|
+
```
|
99
|
+
|
100
|
+
Note that in the above construct, Ruby 1.8.x hash keys are used. That's because Apple's System Ruby is 1.8.7 and won't recognize keen new 1.9.x hash syntax.
|
91
101
|
|
92
|
-
What
|
102
|
+
What MotionModel Can Do
|
93
103
|
================
|
94
104
|
|
95
105
|
You can define your models and their schemas in Ruby. For example:
|
@@ -100,19 +110,21 @@ class Task
|
|
100
110
|
include MotionModel::ArrayModelAdapter
|
101
111
|
|
102
112
|
columns :name => :string,
|
103
|
-
:
|
113
|
+
:long_name => :string,
|
104
114
|
:due_date => :date
|
105
115
|
end
|
106
116
|
|
107
117
|
class MyCoolController
|
108
118
|
def some_method
|
109
119
|
@task = Task.create :name => 'walk the dog',
|
110
|
-
:
|
111
|
-
:due_date
|
120
|
+
:long_name => 'get plenty of exercise. pick up the poop',
|
121
|
+
:due_date => '2012-09-15'
|
112
122
|
end
|
113
123
|
end
|
114
124
|
```
|
115
125
|
|
126
|
+
Side note: The original documentation on this used `description` for the column that is now `long_name`. It turns out Apple reserves `description` so MotionModel saves you the trouble of finding that particular bug by not allowing you to use it for a column name.
|
127
|
+
|
116
128
|
Models support default values, so if you specify your model like this, you get defaults:
|
117
129
|
|
118
130
|
```ruby
|
@@ -134,16 +146,16 @@ class Task
|
|
134
146
|
include MotionModel::Validatable
|
135
147
|
|
136
148
|
columns :name => :string,
|
137
|
-
:
|
149
|
+
:long_name => :string,
|
138
150
|
:due_date => :date
|
139
151
|
validates :name => :presence => true
|
140
152
|
end
|
141
153
|
|
142
154
|
class MyCoolController
|
143
155
|
def some_method
|
144
|
-
@task = Task.new :name
|
145
|
-
:
|
146
|
-
:due_date
|
156
|
+
@task = Task.new :name => 'walk the dog',
|
157
|
+
:long_name => 'get plenty of exercise. pick up the poop',
|
158
|
+
:due_date => '2012-09-15'
|
147
159
|
|
148
160
|
show_scary_warning unless @task.valid?
|
149
161
|
end
|
@@ -125,17 +125,12 @@ module MotionModel
|
|
125
125
|
@dirty = @new_record = false
|
126
126
|
end
|
127
127
|
|
128
|
-
|
129
|
-
|
130
128
|
# Count of objects in the current collection
|
131
129
|
def length
|
132
130
|
collection.length
|
133
131
|
end
|
134
132
|
alias_method :count, :length
|
135
133
|
|
136
|
-
def rebuild_relation_for(name, instance_or_collection) # nodoc
|
137
|
-
end
|
138
|
-
|
139
134
|
private
|
140
135
|
|
141
136
|
def _next_id
|
@@ -147,18 +142,21 @@ module MotionModel
|
|
147
142
|
increment_next_id(options[:id])
|
148
143
|
end
|
149
144
|
|
150
|
-
def
|
151
|
-
col
|
152
|
-
|
145
|
+
def belongs_to_relation(col) # nodoc
|
146
|
+
col.classify.find(_get_attr(col.foreign_key))
|
147
|
+
end
|
153
148
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
149
|
+
def has_many_relation(col) # nodoc
|
150
|
+
_has_many_has_one_relation(col)
|
151
|
+
end
|
152
|
+
|
153
|
+
def has_one_relation(col) # nodoc
|
154
|
+
_has_many_has_one_relation(col)
|
155
|
+
end
|
156
|
+
|
157
|
+
def _has_many_has_one_relation(col) # nodoc
|
158
|
+
related_klass = col.classify
|
159
|
+
related_klass.find(col.inverse_column.foreign_key).belongs_to(self, related_klass).eq(_get_attr(:id))
|
162
160
|
end
|
163
161
|
|
164
162
|
def do_insert(options = {})
|
@@ -128,7 +128,7 @@ module MotionModel
|
|
128
128
|
def encodeWithCoder(coder)
|
129
129
|
columns.each do |attr|
|
130
130
|
# Serialize attributes except the proxy has_many and belongs_to ones.
|
131
|
-
unless [:belongs_to, :has_many].include?
|
131
|
+
unless [:belongs_to, :has_many].include? column(attr).type
|
132
132
|
value = self.send(attr)
|
133
133
|
unless value.nil?
|
134
134
|
coder.encodeObject(value, forKey: attr.to_s)
|
@@ -1,31 +1,51 @@
|
|
1
1
|
module MotionModel
|
2
2
|
module Model
|
3
3
|
class Column
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
attr_reader :name
|
5
|
+
attr_reader :owner
|
6
|
+
attr_reader :type
|
7
|
+
attr_reader :options
|
8
8
|
|
9
|
-
|
9
|
+
OPTION_ATTRS = [:as, :conditions, :default, :dependent, :foreign_key, :inverse_of, :joined_class_name,
|
10
|
+
:polymorphic, :symbolize, :through]
|
11
|
+
|
12
|
+
OPTION_ATTRS.each do |key|
|
13
|
+
define_method(key) { @options[key] }
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(owner, name = nil, type = nil, options = {})
|
17
|
+
raise RuntimeError.new "columns need a type declared." if type.nil?
|
18
|
+
@owner = owner
|
10
19
|
@name = name
|
11
20
|
@type = type
|
12
|
-
|
13
|
-
@default = options.delete :default
|
14
|
-
@dependent = options.delete :dependent
|
21
|
+
@klass = options.delete(:class)
|
15
22
|
@options = options
|
16
23
|
end
|
17
24
|
|
18
|
-
def
|
19
|
-
|
25
|
+
def class_name
|
26
|
+
joined_class_name || name
|
20
27
|
end
|
21
28
|
|
22
|
-
def
|
23
|
-
|
29
|
+
def primary_key
|
30
|
+
:id
|
31
|
+
end
|
32
|
+
|
33
|
+
def foreign_name
|
34
|
+
as || name
|
35
|
+
end
|
36
|
+
|
37
|
+
def foreign_polymorphic_type
|
38
|
+
"#{foreign_name}_type".to_sym
|
39
|
+
end
|
40
|
+
|
41
|
+
def foreign_key
|
42
|
+
@options[:foreign_key] || "#{foreign_name.to_s.singularize}_id".to_sym
|
24
43
|
end
|
25
44
|
|
26
45
|
def classify
|
27
|
-
if
|
28
|
-
|
46
|
+
fail "Column#classify indeterminate for polymorphic associations" if type == :belongs_to && polymorphic
|
47
|
+
if @klass
|
48
|
+
@klass
|
29
49
|
else
|
30
50
|
case @type
|
31
51
|
when :belongs_to
|
@@ -43,8 +63,33 @@ module MotionModel
|
|
43
63
|
end
|
44
64
|
|
45
65
|
def through_class
|
46
|
-
Kernel::const_get(
|
66
|
+
Kernel::const_get(through.to_s.classify)
|
67
|
+
end
|
68
|
+
|
69
|
+
def inverse_foreign_key
|
70
|
+
inverse_column.foreign_key
|
47
71
|
end
|
72
|
+
|
73
|
+
def inverse_name
|
74
|
+
if as
|
75
|
+
as
|
76
|
+
elsif inverse_of
|
77
|
+
inverse_of
|
78
|
+
elsif type == :belongs_to
|
79
|
+
# Check for a singular and a plural relationship
|
80
|
+
name = owner.name.singularize.underscore
|
81
|
+
col = classify.column(name)
|
82
|
+
col ||= classify.column(name.pluralize)
|
83
|
+
col.name
|
84
|
+
else
|
85
|
+
owner.name.singularize.underscore.to_sym
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def inverse_column
|
90
|
+
classify.column(inverse_name)
|
91
|
+
end
|
92
|
+
|
48
93
|
end
|
49
94
|
end
|
50
95
|
end
|
@@ -47,13 +47,19 @@ module MotionModel
|
|
47
47
|
def self.included(base)
|
48
48
|
base.extend(PrivateClassMethods)
|
49
49
|
base.extend(PublicClassMethods)
|
50
|
+
|
51
|
+
base.instance_eval do
|
52
|
+
unless self.respond_to?(:id)
|
53
|
+
add_field(:id, :integer)
|
54
|
+
end
|
55
|
+
end
|
50
56
|
end
|
51
57
|
|
52
58
|
module PublicClassMethods
|
53
59
|
|
54
60
|
def new(options = {})
|
55
61
|
object_class = options[:inheritance_type] ? Kernel.const_get(options[:inheritance_type]) : self
|
56
|
-
object_class.
|
62
|
+
object_class.allocate.instance_eval do
|
57
63
|
initialize(options)
|
58
64
|
self
|
59
65
|
end
|
@@ -130,10 +136,6 @@ module MotionModel
|
|
130
136
|
add_field relation, :has_one, options # Relation must be plural
|
131
137
|
end
|
132
138
|
|
133
|
-
def generate_belongs_to_id(relation)
|
134
|
-
(relation.to_s.singularize.underscore + '_id').to_sym
|
135
|
-
end
|
136
|
-
|
137
139
|
# Use at class level, as follows
|
138
140
|
#
|
139
141
|
# class Assignee
|
@@ -150,13 +152,17 @@ module MotionModel
|
|
150
152
|
end
|
151
153
|
|
152
154
|
# Returns true if a column exists on this model, otherwise false.
|
153
|
-
def column?(
|
154
|
-
!
|
155
|
+
def column?(col)
|
156
|
+
!column(col).nil?
|
155
157
|
end
|
156
158
|
|
157
159
|
# Returns type of this column.
|
158
|
-
def column_type(
|
159
|
-
|
160
|
+
def column_type(col)
|
161
|
+
column(col).type || nil
|
162
|
+
end
|
163
|
+
|
164
|
+
def column(col)
|
165
|
+
col.is_a?(Column) ? col : _column_hashes[col.to_sym]
|
160
166
|
end
|
161
167
|
|
162
168
|
def has_many_columns
|
@@ -176,9 +182,9 @@ module MotionModel
|
|
176
182
|
end
|
177
183
|
|
178
184
|
# returns default value for this column or nil.
|
179
|
-
def default(
|
180
|
-
|
181
|
-
|
185
|
+
def default(col)
|
186
|
+
_col = column(col)
|
187
|
+
_col.nil? ? nil : _col.default
|
182
188
|
end
|
183
189
|
|
184
190
|
# Build an instance that represents a saved object from the persistence layer.
|
@@ -249,8 +255,10 @@ module MotionModel
|
|
249
255
|
@_column_hashes ||= {}
|
250
256
|
end
|
251
257
|
|
258
|
+
# BUGBUG: This appears not to be executed, therefore @_issue_notifications is always nil to begin with.
|
252
259
|
@_issue_notifications = true
|
253
260
|
def _issue_notifications
|
261
|
+
@_issue_notifications = true if @_issue_notifications.nil?
|
254
262
|
@_issue_notifications
|
255
263
|
end
|
256
264
|
|
@@ -302,135 +310,42 @@ module MotionModel
|
|
302
310
|
end
|
303
311
|
|
304
312
|
def define_accessor_methods(name, type, options = {}) #nodoc
|
305
|
-
unless
|
306
|
-
|
307
|
-
return nil if @data[name].nil?
|
308
|
-
if options[:symbolize]
|
309
|
-
@data[name].to_sym
|
310
|
-
else
|
311
|
-
@data[name]
|
312
|
-
end
|
313
|
-
}
|
314
|
-
end
|
315
|
-
define_method("#{name}=".to_sym) { |value|
|
316
|
-
old_value = @data[name]
|
317
|
-
new_value = cast_to_type(name, value)
|
318
|
-
if new_value != old_value
|
319
|
-
@data[name] = new_value
|
320
|
-
@dirty = true
|
321
|
-
end
|
322
|
-
}
|
313
|
+
define_method(name.to_sym) { _get_attr(name) } unless allocate.respond_to?(name)
|
314
|
+
define_method("#{name}=".to_sym) { |v| _set_attr(name, v) }
|
323
315
|
end
|
324
316
|
|
325
317
|
def define_belongs_to_methods(name) #nodoc
|
326
|
-
col =
|
327
|
-
|
328
|
-
define_method(name)
|
329
|
-
return @data[name] if @data[name]
|
330
|
-
if col.options[:polymorphic]
|
331
|
-
if (owner_class_name = send("#{name}_type"))
|
332
|
-
owner_class = Kernel::deep_const_get(owner_class_name.classify)
|
333
|
-
parent_id = send("#{name}_id")
|
334
|
-
end
|
335
|
-
else
|
336
|
-
owner_class = col.classify
|
337
|
-
parent_id = send(self.class.generate_belongs_to_id(col.name))
|
338
|
-
end
|
339
|
-
parent_id.nil? ? nil : owner_class.find_by_id(parent_id)
|
340
|
-
}
|
341
|
-
|
342
|
-
define_method("#{name}_relation") {
|
343
|
-
relation_for(name)
|
344
|
-
}
|
345
|
-
|
346
|
-
# Associate the parent and delegate the inverse assignment
|
347
|
-
define_method("#{name}=") { |parent|
|
348
|
-
rebuild_relation_for(name, parent)
|
349
|
-
send("set_#{name}", parent)
|
350
|
-
if col.options[:polymorphic]
|
351
|
-
foreign_column_name = parent.column_as_name(col.options[:as] || col.name)
|
352
|
-
else
|
353
|
-
foreign_column_name = self.class.name.underscore.to_sym
|
354
|
-
end
|
355
|
-
parent.rebuild_relation_for(foreign_column_name, self) if parent
|
356
|
-
}
|
357
|
-
|
358
|
-
# Associate the parent but without delegating the inverse assignment
|
359
|
-
define_method("set_#{name}") { |parent|
|
360
|
-
@data[name] = parent
|
361
|
-
if col.options[:polymorphic]
|
362
|
-
send("#{name}_type=", parent.class.name)
|
363
|
-
send("#{name}_id=", parent.id)
|
364
|
-
else
|
365
|
-
parent_id_name = self.class.generate_belongs_to_id(col.name)
|
366
|
-
send("#{parent_id_name}=", parent ? parent.id : nil)
|
367
|
-
end
|
368
|
-
}
|
318
|
+
col = column(name)
|
319
|
+
define_method(name) { get_belongs_to_attr(col) }
|
320
|
+
define_method("#{name}=") { |owner| set_belongs_to_attr(col, owner) }
|
369
321
|
|
370
322
|
# TODO also define #{name}+id= methods....
|
371
323
|
|
372
|
-
if col.
|
373
|
-
add_field
|
374
|
-
add_field
|
324
|
+
if col.polymorphic
|
325
|
+
add_field col.foreign_polymorphic_type, :belongs_to_type
|
326
|
+
add_field col.foreign_key, :belongs_to_id
|
375
327
|
else
|
376
|
-
add_field
|
328
|
+
add_field col.foreign_key, :belongs_to_id # a relation is singular.
|
377
329
|
end
|
378
330
|
end
|
379
331
|
|
380
332
|
def define_has_many_methods(name) #nodoc
|
381
|
-
col =
|
382
|
-
|
383
|
-
define_method("#{name}
|
384
|
-
relation_for(name)
|
385
|
-
}
|
386
|
-
|
387
|
-
define_method(name) {
|
388
|
-
send("#{name}_relation").to_a
|
389
|
-
}
|
390
|
-
|
391
|
-
define_method("#{name}=") do |collection|
|
392
|
-
rebuild_relation_for(name, collection)
|
393
|
-
collection.each do |instance|
|
394
|
-
if col.options[:polymorphic]
|
395
|
-
foreign_column_name = col.options[:as] || col.name
|
396
|
-
else
|
397
|
-
foreign_column_name = self.class.name.underscore.to_sym
|
398
|
-
end
|
399
|
-
instance.send("set_#{foreign_column_name}", self)
|
400
|
-
instance.rebuild_relation_for(foreign_column_name, self)
|
401
|
-
end
|
402
|
-
end
|
403
|
-
|
333
|
+
col = column(name)
|
334
|
+
define_method(name) { get_has_many_attr(col) }
|
335
|
+
define_method("#{name}=") { |collection| set_has_many_attr(col, *collection) }
|
404
336
|
end
|
405
337
|
|
406
338
|
def define_has_one_methods(name) #nodoc
|
407
|
-
col =
|
408
|
-
|
409
|
-
define_method("#{name}
|
410
|
-
relation_for(name)
|
411
|
-
}
|
412
|
-
|
413
|
-
define_method(name) {
|
414
|
-
send("#{name}_relation").instance
|
415
|
-
}
|
416
|
-
|
417
|
-
define_method("#{name}=") do |instance|
|
418
|
-
relation_for(name).instance = instance
|
419
|
-
if instance
|
420
|
-
if col.options[:polymorphic]
|
421
|
-
foreign_column_name = col.options[:as] || col.name
|
422
|
-
else
|
423
|
-
foreign_column_name = self.class.name.underscore.to_sym
|
424
|
-
end
|
425
|
-
instance.rebuild_relation_for(foreign_column_name, self)
|
426
|
-
end
|
427
|
-
end
|
339
|
+
col = column(name)
|
340
|
+
define_method(name) { get_has_one_attr(col) }
|
341
|
+
define_method("#{name}=") { |instance| set_has_one_attr(col, instance) }
|
428
342
|
end
|
429
343
|
|
430
344
|
def add_field(name, type, options = {:default => nil}) #nodoc
|
431
|
-
|
345
|
+
name = name.to_sym
|
346
|
+
col = Column.new(self, name, type, options)
|
432
347
|
|
433
|
-
_column_hashes[col.name
|
348
|
+
_column_hashes[col.name] = col
|
434
349
|
|
435
350
|
case type
|
436
351
|
when :has_many then define_has_many_methods(name)
|
@@ -440,36 +355,28 @@ module MotionModel
|
|
440
355
|
end
|
441
356
|
end
|
442
357
|
|
443
|
-
# Returns a column denoted by +name+
|
444
|
-
def column_named(name) #nodoc
|
445
|
-
_column_hashes[name.to_sym]
|
446
|
-
end
|
447
|
-
|
448
358
|
# Returns the column that has the name as its :as option
|
449
|
-
def column_as(
|
450
|
-
|
359
|
+
def column_as(col) #nodoc
|
360
|
+
_col = column(col)
|
361
|
+
_column_hashes.values.find{ |c| c.as == _col.name }
|
451
362
|
end
|
452
363
|
|
453
364
|
# All relation columns, including type and id columns for polymorphic associations
|
454
|
-
def relation_column?(
|
455
|
-
|
365
|
+
def relation_column?(col) #nodoc
|
366
|
+
_col = column(col)
|
367
|
+
[:belongs_to, :belongs_to_id, :belongs_to_type, :has_many, :has_one].include?(_col.type)
|
456
368
|
end
|
457
369
|
|
458
370
|
# Polymorphic association columns that are not stored in DB
|
459
|
-
def virtual_polymorphic_relation_column?(
|
460
|
-
|
371
|
+
def virtual_polymorphic_relation_column?(col) #nodoc
|
372
|
+
_col = column(col)
|
373
|
+
[:belongs_to, :has_many, :has_one].include?(_col.type)
|
461
374
|
end
|
462
375
|
|
463
376
|
def has_relation?(col) #nodoc
|
464
377
|
return false if col.nil?
|
465
|
-
|
466
|
-
|
467
|
-
when MotionModel::Model::Column
|
468
|
-
column_named(col.name)
|
469
|
-
else
|
470
|
-
column_named(col)
|
471
|
-
end
|
472
|
-
[:has_many, :has_one, :belongs_to].include?(col.type)
|
378
|
+
_col = column(col)
|
379
|
+
[:has_many, :has_one, :belongs_to].include?(_col.type)
|
473
380
|
end
|
474
381
|
|
475
382
|
end
|
@@ -523,7 +430,7 @@ module MotionModel
|
|
523
430
|
def ==(comparison_object)
|
524
431
|
super ||
|
525
432
|
comparison_object.instance_of?(self.class) &&
|
526
|
-
id.
|
433
|
+
!id.nil? &&
|
527
434
|
comparison_object.id == id
|
528
435
|
end
|
529
436
|
alias :eql? :==
|
@@ -533,7 +440,7 @@ module MotionModel
|
|
533
440
|
end
|
534
441
|
|
535
442
|
def attributes=(attrs)
|
536
|
-
attrs.each { |k, v|
|
443
|
+
attrs.each { |k, v| set_attr(k, v) }
|
537
444
|
end
|
538
445
|
|
539
446
|
def update_attributes(attrs)
|
@@ -552,10 +459,14 @@ module MotionModel
|
|
552
459
|
@data[:id].to_i
|
553
460
|
end
|
554
461
|
|
555
|
-
# Default
|
556
|
-
#
|
462
|
+
# Default inspect implementation returns identifier and ID
|
463
|
+
# Need to keep this short, i.e. for running specs as the output could be very large
|
464
|
+
def inspect
|
465
|
+
object_identifier
|
466
|
+
end
|
467
|
+
|
557
468
|
def to_s
|
558
|
-
columns.each{|c| "#{c}: #{
|
469
|
+
columns.each{|c| "#{c}: #{get_attr(c)}\n"}
|
559
470
|
end
|
560
471
|
|
561
472
|
def save!(options = {})
|
@@ -604,6 +515,8 @@ module MotionModel
|
|
604
515
|
def after_save(sender); end
|
605
516
|
def before_delete(sender); end
|
606
517
|
def after_delete(sender); end
|
518
|
+
def before_destroy(sender); end
|
519
|
+
def after_destroy(sender); end
|
607
520
|
|
608
521
|
def call_hook(hook_name, postfix)
|
609
522
|
hook = "#{hook_name}_#{postfix}"
|
@@ -644,7 +557,7 @@ module MotionModel
|
|
644
557
|
options[:omit_model_identifiers] ||= {}
|
645
558
|
options[:omit_model_identifiers][model_identifier] = self
|
646
559
|
self.class.association_columns.each do |name, col|
|
647
|
-
delete_candidates =
|
560
|
+
delete_candidates = get_attr(name)
|
648
561
|
Array(delete_candidates).each do |candidate|
|
649
562
|
next if options[:omit_model_identifiers][candidate.model_identifier]
|
650
563
|
if col.dependent == :destroy
|
@@ -660,8 +573,12 @@ module MotionModel
|
|
660
573
|
end
|
661
574
|
|
662
575
|
# True if the column exists, otherwise false
|
663
|
-
def column?(
|
664
|
-
self.class.column?(
|
576
|
+
def column?(col)
|
577
|
+
self.class.column?(col)
|
578
|
+
end
|
579
|
+
|
580
|
+
def column(col)
|
581
|
+
self.class.column(col)
|
665
582
|
end
|
666
583
|
|
667
584
|
# Returns list of column names as an array
|
@@ -670,8 +587,8 @@ module MotionModel
|
|
670
587
|
end
|
671
588
|
|
672
589
|
# Type of a given column
|
673
|
-
def column_type(
|
674
|
-
self.class.column_type(
|
590
|
+
def column_type(col)
|
591
|
+
self.class.column_type(col)
|
675
592
|
end
|
676
593
|
|
677
594
|
# Options hash for column, excluding the core
|
@@ -682,8 +599,8 @@ module MotionModel
|
|
682
599
|
# example:
|
683
600
|
#
|
684
601
|
# columns :date => {:type => :date, :formotion => {:picker_type => :date_time}}
|
685
|
-
def options(
|
686
|
-
|
602
|
+
def options(col)
|
603
|
+
column(col).options
|
687
604
|
end
|
688
605
|
|
689
606
|
def dirty?
|
@@ -694,8 +611,141 @@ module MotionModel
|
|
694
611
|
@dirty = true
|
695
612
|
end
|
696
613
|
|
697
|
-
def
|
698
|
-
|
614
|
+
def get_attr(name)
|
615
|
+
send(name)
|
616
|
+
end
|
617
|
+
|
618
|
+
def _attr_present?(name)
|
619
|
+
@data.has_key?(name)
|
620
|
+
end
|
621
|
+
|
622
|
+
def _get_attr(col)
|
623
|
+
_col = column(col)
|
624
|
+
return nil if @data[_col.name].nil?
|
625
|
+
if _col.symbolize
|
626
|
+
@data[_col.name].to_sym
|
627
|
+
else
|
628
|
+
@data[_col.name]
|
629
|
+
end
|
630
|
+
end
|
631
|
+
|
632
|
+
def set_attr(name, value)
|
633
|
+
send("#{name}=", value)
|
634
|
+
end
|
635
|
+
|
636
|
+
def _set_attr(name, value)
|
637
|
+
name = name.to_sym
|
638
|
+
old_value = @data[name]
|
639
|
+
new_value = relation_column?(name) ? value : cast_to_type(name, value)
|
640
|
+
if new_value != old_value
|
641
|
+
@data[name] = new_value
|
642
|
+
@dirty = true
|
643
|
+
end
|
644
|
+
end
|
645
|
+
|
646
|
+
def get_belongs_to_attr(col)
|
647
|
+
belongs_to_relation(col)
|
648
|
+
end
|
649
|
+
|
650
|
+
def get_has_many_attr(col)
|
651
|
+
_has_many_has_one_relation(col)
|
652
|
+
end
|
653
|
+
|
654
|
+
def get_has_one_attr(col)
|
655
|
+
_has_many_has_one_relation(col)
|
656
|
+
end
|
657
|
+
|
658
|
+
# Associate the owner but without rebuilding the inverse assignment
|
659
|
+
def set_belongs_to_attr(col, owner, options = {})
|
660
|
+
_col = column(col)
|
661
|
+
unless belongs_to_synced?(_col, owner)
|
662
|
+
_set_attr(_col.name, owner)
|
663
|
+
rebuild_relation(_col, owner, set_inverse: options[:set_inverse])
|
664
|
+
if _col.polymorphic
|
665
|
+
set_polymorphic_attr(_col.name, owner)
|
666
|
+
else
|
667
|
+
_set_attr(_col.foreign_key, owner ? owner.id : nil)
|
668
|
+
end
|
669
|
+
end
|
670
|
+
|
671
|
+
owner
|
672
|
+
end
|
673
|
+
|
674
|
+
# Determine if the :belongs_to relationship is synchronized. Checks the instance and the DB column attributes.
|
675
|
+
def belongs_to_synced?(col, owner)
|
676
|
+
# The :belongs_to that points to the instance has changed
|
677
|
+
return false if get_belongs_to_attr(col) != owner
|
678
|
+
|
679
|
+
# The polymorphic reference (_type, _id) columns do not match, maybe it was just saved
|
680
|
+
return false if col.polymorphic && !polymorphic_attr_matches?(col, owner)
|
681
|
+
|
682
|
+
# The key reference (_id) column does not match, maybe it was just saved
|
683
|
+
return false if _get_attr(col.foreign_key) != owner.try(:id)
|
684
|
+
|
685
|
+
true
|
686
|
+
end
|
687
|
+
|
688
|
+
def push_has_many_attr(col, *instances)
|
689
|
+
_col = column(col)
|
690
|
+
collection = get_has_many_attr(_col)
|
691
|
+
_collection = []
|
692
|
+
instances.each do |instance|
|
693
|
+
next if collection.include?(instance)
|
694
|
+
_collection << instance
|
695
|
+
end
|
696
|
+
push_relation(_col, *_collection)
|
697
|
+
instances
|
698
|
+
end
|
699
|
+
|
700
|
+
# TODO clean up existing reference, check rails
|
701
|
+
def set_has_many_attr(col, *instances)
|
702
|
+
_col = column(col)
|
703
|
+
unload_relation(_col)
|
704
|
+
push_has_many_attr(_col, *instances)
|
705
|
+
instances
|
706
|
+
end
|
707
|
+
|
708
|
+
def set_has_one_attr(col, instance)
|
709
|
+
_col = column(col)
|
710
|
+
if get_has_one_attr(_col) != instance
|
711
|
+
rebuild_relation(_col, instance)
|
712
|
+
end
|
713
|
+
instance
|
714
|
+
end
|
715
|
+
|
716
|
+
def get_polymorphic_attr(col)
|
717
|
+
_col = column(col)
|
718
|
+
owner_class = nil
|
719
|
+
id = _get_attr(_col.foreign_key)
|
720
|
+
unless id.nil?
|
721
|
+
owner_class_name = _get_attr(_col.foreign_polymorphic_type)
|
722
|
+
owner_class_name = String(owner_class_name) # RubyMotion issue, String#classify might fail otherwise
|
723
|
+
owner_class = Kernel::deep_const_get(owner_class_name.classify)
|
724
|
+
end
|
725
|
+
[owner_class, id]
|
726
|
+
end
|
727
|
+
|
728
|
+
|
729
|
+
def polymorphic_attr_matches?(col, instance)
|
730
|
+
klass, id = get_polymorphic_attr(col)
|
731
|
+
klass == instance.class && id == instance.id
|
732
|
+
end
|
733
|
+
|
734
|
+
def set_polymorphic_attr(col, instance)
|
735
|
+
_col = column(col)
|
736
|
+
_set_attr(_col.foreign_polymorphic_type, instance.class.name)
|
737
|
+
_set_attr(_col.foreign_key, instance.id)
|
738
|
+
instance
|
739
|
+
end
|
740
|
+
|
741
|
+
def foreign_column_name(col)
|
742
|
+
if col.polymorphic
|
743
|
+
col.as || col.name
|
744
|
+
elsif col.foreign_key
|
745
|
+
col.foreign_key
|
746
|
+
else
|
747
|
+
self.class.name.underscore.to_sym
|
748
|
+
end
|
699
749
|
end
|
700
750
|
|
701
751
|
private
|
@@ -716,20 +766,15 @@ module MotionModel
|
|
716
766
|
self.class.send(:has_relation?, col)
|
717
767
|
end
|
718
768
|
|
719
|
-
def
|
720
|
-
self.attributes = {column => value || self.class.default(column)}
|
721
|
-
end
|
722
|
-
|
723
|
-
def column_named(name) #nodoc
|
724
|
-
self.class.send(:column_named, name.to_sym)
|
769
|
+
def rebuild_relation(column_name, instance_or_collection, options = {}) # nodoc
|
725
770
|
end
|
726
771
|
|
727
|
-
def
|
728
|
-
self.class.
|
772
|
+
def initialize_data_columns(column, value) #nodoc
|
773
|
+
self.attributes = {column => value || self.class.default(column)}
|
729
774
|
end
|
730
775
|
|
731
|
-
def
|
732
|
-
self.class.
|
776
|
+
def column_as(col) #nodoc
|
777
|
+
self.class.send(:column_as, col)
|
733
778
|
end
|
734
779
|
|
735
780
|
def issue_notification(info) #nodoc
|
@@ -737,19 +782,8 @@ module MotionModel
|
|
737
782
|
end
|
738
783
|
|
739
784
|
def method_missing(sym, *args, &block)
|
740
|
-
if sym.to_s[-1]
|
741
|
-
|
742
|
-
return args.first
|
743
|
-
else
|
744
|
-
return @data[sym] if @data && @data.has_key?(sym)
|
745
|
-
end
|
746
|
-
begin
|
747
|
-
r = super
|
748
|
-
rescue NoMethodError => exc
|
749
|
-
unless exc.to_s =~ /undefined method `(?:before|after)_/
|
750
|
-
raise
|
751
|
-
end
|
752
|
-
end
|
785
|
+
return @data[sym] if sym.to_s[-1] != '=' && @data && @data.has_key?(sym)
|
786
|
+
super
|
753
787
|
end
|
754
788
|
|
755
789
|
end
|
@@ -60,6 +60,9 @@ module MotionModel
|
|
60
60
|
# * First, it triggers validations.
|
61
61
|
#
|
62
62
|
# * Second, it returns the result of performing the validations.
|
63
|
+
def before_validation(sender); end
|
64
|
+
def after_validation(sender); end
|
65
|
+
|
63
66
|
def valid?
|
64
67
|
call_hooks 'validation' do
|
65
68
|
@messages = []
|
@@ -181,5 +184,10 @@ module MotionModel
|
|
181
184
|
def add_message(field, message)
|
182
185
|
@messages.push({field.to_sym => message})
|
183
186
|
end
|
187
|
+
|
188
|
+
# Stub methods for hook protocols
|
189
|
+
def before_validation(sender); end
|
190
|
+
def after_validation(sender); end
|
191
|
+
|
184
192
|
end
|
185
193
|
end
|
data/lib/motion_model/version.rb
CHANGED
data/motion_model.gemspec
CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |gem|
|
|
12
12
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
13
13
|
gem.name = "motion_model"
|
14
14
|
gem.require_paths = ["lib"]
|
15
|
-
gem.add_dependency 'bubble-wrap', '
|
15
|
+
gem.add_dependency 'bubble-wrap', '1.3.0.osx'
|
16
16
|
gem.add_dependency 'motion-support', '>=0.1.0'
|
17
17
|
gem.version = MotionModel::VERSION
|
18
18
|
end
|
@@ -205,8 +205,8 @@ end
|
|
205
205
|
describe "serialization of relations" do
|
206
206
|
before do
|
207
207
|
parent = Parent.create(:name => 'BoB')
|
208
|
-
parent.
|
209
|
-
parent.
|
208
|
+
parent.children.create :name => 'Fergie'
|
209
|
+
parent.children.create :name => 'Will I Am'
|
210
210
|
end
|
211
211
|
|
212
212
|
it "is wired up right" do
|
@@ -35,7 +35,7 @@ class Employee
|
|
35
35
|
include MotionModel::Model
|
36
36
|
include MotionModel::ArrayModelAdapter
|
37
37
|
columns :name
|
38
|
-
belongs_to :
|
38
|
+
belongs_to :cascaded_assignee
|
39
39
|
end
|
40
40
|
|
41
41
|
describe "cascading deletes" do
|
@@ -58,8 +58,8 @@ describe "cascading deletes" do
|
|
58
58
|
|
59
59
|
it "deletes assignees that belong to a destroyed task" do
|
60
60
|
task = CascadingTask.create(:name => 'cascading')
|
61
|
-
task.
|
62
|
-
task.
|
61
|
+
task.cascaded_assignees.create(:assignee_name => 'joe')
|
62
|
+
task.cascaded_assignees.create(:assignee_name => 'bill')
|
63
63
|
|
64
64
|
CascadingTask.count.should == 1
|
65
65
|
CascadedAssignee.count.should == 2
|
@@ -74,7 +74,7 @@ describe "cascading deletes" do
|
|
74
74
|
1.upto(3) do |item|
|
75
75
|
task = CascadingTask.create :name => "Task #{item}"
|
76
76
|
1.upto(3) do |assignee|
|
77
|
-
task.
|
77
|
+
task.cascaded_assignees.create :assignee_name => "assignee #{assignee} for task #{task}"
|
78
78
|
end
|
79
79
|
end
|
80
80
|
CascadingTask.count.should == 3
|
@@ -88,8 +88,8 @@ describe "cascading deletes" do
|
|
88
88
|
|
89
89
|
it "deletes only one level when a task is destroyed but dependent is delete" do
|
90
90
|
task = CascadingTask.create :name => 'dependent => :delete'
|
91
|
-
assignee = task.
|
92
|
-
assignee.
|
91
|
+
assignee = task.cascaded_assignees.create :assignee_name => 'deletable assignee'
|
92
|
+
assignee.employees.create :name => 'person who sticks around'
|
93
93
|
|
94
94
|
CascadingTask.count.should == 1
|
95
95
|
CascadedAssignee.count.should == 1
|
data/spec/model_spec.rb
CHANGED
@@ -205,6 +205,11 @@ describe "Creating a model" do
|
|
205
205
|
lambda{task.bar}.should.raise(NoMethodError)
|
206
206
|
end
|
207
207
|
|
208
|
+
it 'raises a NoMethodError exception when an unknown attribute receives an assignment' do
|
209
|
+
task = Task.new
|
210
|
+
lambda{task.bar = 'foo'}.should.raise(NoMethodError)
|
211
|
+
end
|
212
|
+
|
208
213
|
it 'successfully retrieves by attribute' do
|
209
214
|
task = Task.create(:name => 'my task')
|
210
215
|
task.name.should == 'my task'
|
data/spec/relation_spec.rb
CHANGED
@@ -39,7 +39,7 @@ describe 'related objects' do
|
|
39
39
|
|
40
40
|
it "camelcased style" do
|
41
41
|
t = User.create(:name => "Arkan")
|
42
|
-
t.
|
42
|
+
t.email_accounts.create(:name => "Gmail")
|
43
43
|
EmailAccount.first.user.name.should == "Arkan"
|
44
44
|
User.last.email_accounts.last.name.should == "Gmail"
|
45
45
|
end
|
@@ -58,12 +58,12 @@ describe 'related objects' do
|
|
58
58
|
|
59
59
|
it 'relation objects are empty on initialization' do
|
60
60
|
a_task = Task.create
|
61
|
-
a_task.
|
61
|
+
a_task.assignees.all.should.be.empty
|
62
62
|
end
|
63
63
|
|
64
64
|
it "supports creating related objects directly on parents" do
|
65
65
|
a_task = Task.create(:name => 'Walk the Dog')
|
66
|
-
a_task.
|
66
|
+
a_task.assignees.create(:assignee_name => 'bob')
|
67
67
|
a_task.assignees.count.should == 1
|
68
68
|
a_task.assignees.first.assignee_name.should == 'bob'
|
69
69
|
Assignee.count.should == 1
|
@@ -81,7 +81,7 @@ describe 'related objects' do
|
|
81
81
|
assignee_index = 1
|
82
82
|
@tasks << t
|
83
83
|
1.upto(task * 2) do |assignee|
|
84
|
-
@assignees << t.
|
84
|
+
@assignees << t.assignees.create(:assignee_name => "employee #{assignee_index}_assignee_for_task_#{t.id}")
|
85
85
|
assignee_index += 1
|
86
86
|
end
|
87
87
|
end
|
@@ -105,7 +105,7 @@ describe 'related objects' do
|
|
105
105
|
assignee = Assignee.new(:assignee_name => 'Zoe')
|
106
106
|
Task.count.should == 3
|
107
107
|
assignee_count = Task.find(3).assignees.count
|
108
|
-
Task.find(3).
|
108
|
+
Task.find(3).assignees.push(assignee)
|
109
109
|
Task.find(3).assignees.count.should == assignee_count + 1
|
110
110
|
end
|
111
111
|
|
@@ -113,7 +113,7 @@ describe 'related objects' do
|
|
113
113
|
|
114
114
|
it "supports creating blank (empty) scratchpad associated objects" do
|
115
115
|
task = Task.create :name => 'watch a movie'
|
116
|
-
assignee = task.
|
116
|
+
assignee = task.assignees.new # TODO per Rails convention, this should really be #build, not #new
|
117
117
|
assignee.assignee_name = 'Chloe'
|
118
118
|
assignee.save
|
119
119
|
task.assignees.count.should == 1
|
@@ -129,7 +129,7 @@ describe 'related objects' do
|
|
129
129
|
|
130
130
|
it "allows a child to back-reference its parent" do
|
131
131
|
t = Task.create(:name => "Walk the Dog")
|
132
|
-
t.
|
132
|
+
t.assignees.create(:assignee_name => "Rihanna")
|
133
133
|
Assignee.first.task.name.should == "Walk the Dog"
|
134
134
|
end
|
135
135
|
|
@@ -143,7 +143,7 @@ describe 'related objects' do
|
|
143
143
|
|
144
144
|
describe "basic wiring" do
|
145
145
|
before do
|
146
|
-
@t1.
|
146
|
+
@t1.assignees << @a1
|
147
147
|
end
|
148
148
|
|
149
149
|
it "pushing a created assignee gives a task count of 1" do
|
@@ -161,7 +161,7 @@ describe 'related objects' do
|
|
161
161
|
|
162
162
|
describe "when pushing assignees onto two different tasks" do
|
163
163
|
before do
|
164
|
-
@t2.
|
164
|
+
@t2.assignees << @a1
|
165
165
|
end
|
166
166
|
|
167
167
|
it "pushing assignees to two different tasks lets the last task have the assignee (count)" do
|
metadata
CHANGED
@@ -1,61 +1,57 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: motion_model
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 4
|
8
|
-
- 2
|
9
|
-
version: 0.4.2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.4
|
5
|
+
prerelease:
|
10
6
|
platform: ruby
|
11
|
-
authors:
|
7
|
+
authors:
|
12
8
|
- Steve Ross
|
13
9
|
autorequire:
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
dependencies:
|
20
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2013-05-10 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
21
15
|
name: bubble-wrap
|
22
|
-
|
23
|
-
|
24
|
-
requirements:
|
25
|
-
- -
|
26
|
-
- !ruby/object:Gem::Version
|
27
|
-
|
28
|
-
- 1
|
29
|
-
- 1
|
30
|
-
- 4
|
31
|
-
version: 1.1.4
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - '='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.3.0.osx
|
32
22
|
type: :runtime
|
33
|
-
version_requirements: *id001
|
34
|
-
- !ruby/object:Gem::Dependency
|
35
|
-
name: motion-support
|
36
23
|
prerelease: false
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - '='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.3.0.osx
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: motion-support
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
45
37
|
version: 0.1.0
|
46
38
|
type: :runtime
|
47
|
-
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.1.0
|
48
46
|
description: Simple model and validation mixins for RubyMotion
|
49
|
-
email:
|
47
|
+
email:
|
50
48
|
- sxross@gmail.com
|
51
49
|
executables: []
|
52
|
-
|
53
50
|
extensions: []
|
54
|
-
|
55
51
|
extra_rdoc_files: []
|
56
|
-
|
57
|
-
files:
|
52
|
+
files:
|
58
53
|
- .gitignore
|
54
|
+
- .travis.yml
|
59
55
|
- CHANGELOG
|
60
56
|
- Gemfile
|
61
57
|
- LICENSE
|
@@ -91,37 +87,31 @@ files:
|
|
91
87
|
- spec/relation_spec.rb
|
92
88
|
- spec/transaction_spec.rb
|
93
89
|
- spec/validation_spec.rb
|
94
|
-
has_rdoc: true
|
95
90
|
homepage: https://github.com/sxross/MotionModel
|
96
91
|
licenses: []
|
97
|
-
|
98
92
|
post_install_message:
|
99
93
|
rdoc_options: []
|
100
|
-
|
101
|
-
require_paths:
|
94
|
+
require_paths:
|
102
95
|
- lib
|
103
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
requirements:
|
112
|
-
- -
|
113
|
-
- !ruby/object:Gem::Version
|
114
|
-
|
115
|
-
- 0
|
116
|
-
version: "0"
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
none: false
|
104
|
+
requirements:
|
105
|
+
- - ! '>='
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
117
108
|
requirements: []
|
118
|
-
|
119
109
|
rubyforge_project:
|
120
|
-
rubygems_version: 1.
|
110
|
+
rubygems_version: 1.8.24
|
121
111
|
signing_key:
|
122
112
|
specification_version: 3
|
123
113
|
summary: Simple model and validation mixins for RubyMotion
|
124
|
-
test_files:
|
114
|
+
test_files:
|
125
115
|
- spec/adapter_spec.rb
|
126
116
|
- spec/array_model_persistence_spec.rb
|
127
117
|
- spec/cascading_delete_spec.rb
|