motion_model 0.2.8 → 0.3.0
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/CHANGELOG +23 -4
- data/LICENSE +20 -0
- data/README.md +44 -5
- data/lib/motion_model/ext.rb +15 -19
- data/lib/motion_model/input_helpers.rb +40 -37
- data/lib/motion_model/model/column.rb +12 -7
- data/lib/motion_model/model/model.rb +313 -248
- data/lib/motion_model/model/persistence.rb +1 -1
- data/lib/motion_model/version.rb +1 -1
- data/spec/cascading_delete_spec.rb +100 -0
- data/spec/model_spec.rb +75 -61
- data/spec/relation_spec.rb +32 -25
- metadata +5 -2
data/lib/motion_model/version.rb
CHANGED
@@ -0,0 +1,100 @@
|
|
1
|
+
class Assignee
|
2
|
+
include MotionModel::Model
|
3
|
+
columns :assignee_name => :string
|
4
|
+
belongs_to :task
|
5
|
+
end
|
6
|
+
|
7
|
+
class Task
|
8
|
+
include MotionModel::Model
|
9
|
+
columns :name => :string,
|
10
|
+
:details => :string,
|
11
|
+
:some_day => :date
|
12
|
+
has_many :assignees
|
13
|
+
end
|
14
|
+
|
15
|
+
class CascadingTask
|
16
|
+
include MotionModel::Model
|
17
|
+
columns :name => :string,
|
18
|
+
:details => :string,
|
19
|
+
:some_day => :date
|
20
|
+
has_many :cascaded_assignees, :dependent => :delete
|
21
|
+
end
|
22
|
+
|
23
|
+
class CascadedAssignee
|
24
|
+
include MotionModel::Model
|
25
|
+
columns :assignee_name => :string
|
26
|
+
belongs_to :cascading_task
|
27
|
+
has_many :employees
|
28
|
+
end
|
29
|
+
|
30
|
+
class Employee
|
31
|
+
include MotionModel::Model
|
32
|
+
columns :name
|
33
|
+
belongs_to :CascadedAssignee
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "cascading deletes" do
|
37
|
+
# describe "when not marked for destruction" do
|
38
|
+
# it "leaves assignees alone when they are not marked for destruction" do
|
39
|
+
# Task.delete_all
|
40
|
+
# Assignee.delete_all
|
41
|
+
|
42
|
+
# task = Task.create :name => 'Walk the dog'
|
43
|
+
# task.assignees.create :assignee_name => 'Joe'
|
44
|
+
# lambda{task.destroy}.should.not.change{Assignee.length}
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
|
48
|
+
describe "when marked for destruction" do
|
49
|
+
before do
|
50
|
+
CascadingTask.delete_all
|
51
|
+
CascadedAssignee.delete_all
|
52
|
+
end
|
53
|
+
|
54
|
+
it "deletes assignees that belong to a destroyed task" do
|
55
|
+
task = CascadingTask.create(:name => 'cascading')
|
56
|
+
task.cascaded_assignees.create(:assignee_name => 'joe')
|
57
|
+
task.cascaded_assignees.create(:assignee_name => 'bill')
|
58
|
+
|
59
|
+
CascadingTask.count.should == 1
|
60
|
+
CascadedAssignee.count.should == 2
|
61
|
+
|
62
|
+
task.destroy
|
63
|
+
|
64
|
+
CascadingTask.count.should == 0
|
65
|
+
CascadedAssignee.count.should == 0
|
66
|
+
end
|
67
|
+
|
68
|
+
it "deletes all assignees when all tasks are destroyed" do
|
69
|
+
1.upto(3) do |item|
|
70
|
+
task = CascadingTask.create :name => "Task #{item}"
|
71
|
+
1.upto(3) do |assignee|
|
72
|
+
task.cascaded_assignees.create :name => "assignee #{assignee} for task #{task}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
CascadingTask.count.should == 3
|
76
|
+
CascadedAssignee.count.should == 9
|
77
|
+
|
78
|
+
CascadingTask.destroy_all
|
79
|
+
|
80
|
+
CascadingTask.count.should == 0
|
81
|
+
CascadedAssignee.count.should == 0
|
82
|
+
end
|
83
|
+
|
84
|
+
it "deletes only one level when a task is destroyed but dependent is delete" do
|
85
|
+
task = CascadingTask.create :name => 'dependent => :delete'
|
86
|
+
assignee = task.cascaded_assignees.create :assignee_name => 'deletable assignee'
|
87
|
+
assignee.employees.create :name => 'person who sticks around'
|
88
|
+
|
89
|
+
CascadingTask.count.should == 1
|
90
|
+
CascadedAssignee.count.should == 1
|
91
|
+
Employee.count.should == 1
|
92
|
+
|
93
|
+
task.destroy
|
94
|
+
|
95
|
+
CascadingTask.count.should == 0
|
96
|
+
CascadedAssignee.count.should == 0
|
97
|
+
Employee.count.should == 1
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/spec/model_spec.rb
CHANGED
@@ -3,6 +3,10 @@ class Task
|
|
3
3
|
columns :name => :string,
|
4
4
|
:details => :string,
|
5
5
|
:some_day => :date
|
6
|
+
|
7
|
+
def custom_attribute_by_method
|
8
|
+
"#{name} - #{details}"
|
9
|
+
end
|
6
10
|
end
|
7
11
|
|
8
12
|
class ATask
|
@@ -130,6 +134,12 @@ describe "Creating a model" do
|
|
130
134
|
Task.count.should.equal(1)
|
131
135
|
end
|
132
136
|
|
137
|
+
it 'instance variables have access to length and count' do
|
138
|
+
task = Task.create
|
139
|
+
task.length.should.equal(1)
|
140
|
+
task.count.should.equal(1)
|
141
|
+
end
|
142
|
+
|
133
143
|
it 'when there is more than one element, length returned is correct' do
|
134
144
|
10.times { Task.create }
|
135
145
|
Task.length.should.equal(10)
|
@@ -137,6 +147,30 @@ describe "Creating a model" do
|
|
137
147
|
|
138
148
|
end
|
139
149
|
|
150
|
+
describe 'adding or updating' do
|
151
|
+
before do
|
152
|
+
Task.delete_all
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'adds to the collection when a new task is saved' do
|
156
|
+
task = Task.new
|
157
|
+
lambda{task.save}.should.change{Task.count}
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'does not add to the collection when an existing task is saved' do
|
161
|
+
task = Task.create(:name => 'updateable')
|
162
|
+
task.name = 'updated'
|
163
|
+
lambda{task.save}.should.not.change{Task.count}
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'updates data properly' do
|
167
|
+
task = Task.create(:name => 'updateable')
|
168
|
+
task.name = 'updated'
|
169
|
+
Task.where(:name).eq('updated').should == 0
|
170
|
+
lambda{task.save}.should.change{Task.where(:name).eq('updated')}
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
140
174
|
describe 'deleting' do
|
141
175
|
before do
|
142
176
|
Task.delete_all
|
@@ -156,13 +190,45 @@ describe "Creating a model" do
|
|
156
190
|
target = Task.find(:name).eq('task 2').first
|
157
191
|
lambda{target.delete}.should.change{Task.length}
|
158
192
|
end
|
193
|
+
|
194
|
+
it 'undeleting a row restores it' do
|
195
|
+
target = Task.find(:name).eq('task 3').first
|
196
|
+
target.should.not == nil
|
197
|
+
target.delete
|
198
|
+
target.undelete
|
199
|
+
Task.find(:name).eq('task 3').count.should.equal 1
|
200
|
+
end
|
159
201
|
end
|
160
202
|
|
161
|
-
describe 'Handling Attribute
|
203
|
+
describe 'Handling Attribute Implementation' do
|
162
204
|
it 'raises a NoMethodError exception when an unknown attribute it referenced' do
|
163
205
|
task = Task.new
|
164
206
|
lambda{task.bar}.should.raise(NoMethodError)
|
165
207
|
end
|
208
|
+
|
209
|
+
it 'successfully retrieves by attribute' do
|
210
|
+
task = Task.create(:name => 'my task')
|
211
|
+
task.name.should == 'my task'
|
212
|
+
end
|
213
|
+
|
214
|
+
describe "dirty" do
|
215
|
+
before do
|
216
|
+
@new_task = Task.new
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'marks a new object as dirty' do
|
220
|
+
@new_task.should.be.dirty
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'marks a saved object as clean' do
|
224
|
+
lambda{@new_task.save}.should.change{@new_task.dirty?}
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'marks a modified object as dirty' do
|
228
|
+
@new_task.save
|
229
|
+
lambda{@new_task.name = 'now dirty'}.should.change{@new_task.dirty?}
|
230
|
+
end
|
231
|
+
end
|
166
232
|
end
|
167
233
|
|
168
234
|
describe 'Type casting' do
|
@@ -224,68 +290,16 @@ describe "Creating a model" do
|
|
224
290
|
it 'the date field should be the same as it was in string form' do
|
225
291
|
@convertible.a_date.to_s.should.match(/^2012-09-15/)
|
226
292
|
end
|
227
|
-
|
228
293
|
end
|
229
|
-
end
|
230
294
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
attr_accessor :notification_called, :notification_details
|
237
|
-
|
238
|
-
def hookup_events
|
239
|
-
@notification_id = NSNotificationCenter.defaultCenter.addObserver(self, selector:'dataDidChange:', name:'MotionModelDataDidChangeNotification', object:nil)
|
240
|
-
@notification_details = nil
|
241
|
-
end
|
242
|
-
|
243
|
-
def dataDidChange(notification)
|
244
|
-
@notification_called = true
|
245
|
-
@notification_details = notification.userInfo
|
246
|
-
end
|
247
|
-
|
248
|
-
def teardown_events
|
249
|
-
NSNotificationCenter.defaultCenter.removeObserver @notification_id
|
250
|
-
end
|
251
|
-
end
|
295
|
+
describe 'defining custom attributes' do
|
296
|
+
before do
|
297
|
+
Task.delete_all
|
298
|
+
@task = Task.create :name => 'Feed the Cat', :details => 'Get food, pour out'
|
299
|
+
end
|
252
300
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
@task.hookup_events
|
257
|
-
end
|
258
|
-
|
259
|
-
after do
|
260
|
-
@task.teardown_events
|
261
|
-
end
|
262
|
-
|
263
|
-
it "fires a change notification when an item is added" do
|
264
|
-
@task.notification_called = false
|
265
|
-
lambda{@task.save}.should.change{@task.notification_called}
|
266
|
-
end
|
267
|
-
|
268
|
-
it "contains an add notification for new objects" do
|
269
|
-
@task.save
|
270
|
-
@task.notification_details[:action].should == 'add'
|
271
|
-
end
|
272
|
-
|
273
|
-
it "contans an update notification for an updated object" do
|
274
|
-
@task.save
|
275
|
-
@task.name = "Bill"
|
276
|
-
@task.save
|
277
|
-
@task.notification_details[:action].should == 'update'
|
278
|
-
end
|
279
|
-
|
280
|
-
it "contains a delete notification for a deleted object" do
|
281
|
-
@task.save
|
282
|
-
@task.delete
|
283
|
-
@task.notification_details[:action].should == 'delete'
|
284
|
-
end
|
285
|
-
|
286
|
-
it "does not get a delete notification for delete_all" do
|
287
|
-
@task = NotifiableTask.create :name => 'Bob'
|
288
|
-
@task.notification_called = nil
|
289
|
-
lambda{NotifiableTask.delete_all}.should.not.change{@task.notification_called}
|
301
|
+
it 'uses a custom attribute by method' do
|
302
|
+
@task.custom_attribute_by_method.should == 'Feed the Cat - Get food, pour out'
|
303
|
+
end
|
290
304
|
end
|
291
305
|
end
|
data/spec/relation_spec.rb
CHANGED
@@ -6,7 +6,7 @@ end
|
|
6
6
|
|
7
7
|
class Task
|
8
8
|
include MotionModel::Model
|
9
|
-
columns :name => :string,
|
9
|
+
columns :name => :string,
|
10
10
|
:details => :string,
|
11
11
|
:some_day => :date
|
12
12
|
has_many :assignees
|
@@ -16,7 +16,7 @@ end
|
|
16
16
|
class User
|
17
17
|
include MotionModel::Model
|
18
18
|
columns :name => :string
|
19
|
-
|
19
|
+
|
20
20
|
has_many :email_accounts
|
21
21
|
end
|
22
22
|
|
@@ -25,7 +25,7 @@ class EmailAccount
|
|
25
25
|
columns :name => :string
|
26
26
|
belongs_to :user
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
Inflector.inflections.irregular 'assignees', 'assignee'
|
30
30
|
Inflector.inflections.irregular 'assignee', 'assignees'
|
31
31
|
|
@@ -45,33 +45,38 @@ describe 'related objects' do
|
|
45
45
|
end
|
46
46
|
|
47
47
|
describe 'has_many' do
|
48
|
+
before do
|
49
|
+
Task.delete_all
|
50
|
+
Assignee.delete_all
|
51
|
+
end
|
52
|
+
|
48
53
|
it "is wired up right" do
|
49
54
|
lambda {Task.new}.should.not.raise
|
50
55
|
lambda {Task.new.assignees}.should.not.raise
|
51
56
|
end
|
52
|
-
|
57
|
+
|
53
58
|
it 'relation objects are empty on initialization' do
|
54
59
|
a_task = Task.create
|
55
60
|
a_task.assignees.all.should.be.empty
|
56
61
|
end
|
57
|
-
|
62
|
+
|
58
63
|
it "supports creating related objects directly on parents" do
|
59
64
|
a_task = Task.create(:name => 'Walk the Dog')
|
60
65
|
a_task.assignees.create(:assignee_name => 'bob')
|
61
|
-
a_task.assignees.
|
66
|
+
a_task.assignees.count.should == 1
|
62
67
|
a_task.assignees.first.assignee_name.should == 'bob'
|
63
68
|
Assignee.count.should == 1
|
64
69
|
end
|
65
|
-
|
70
|
+
|
66
71
|
describe "supporting has_many" do
|
67
72
|
before do
|
68
73
|
Task.delete_all
|
69
74
|
Assignee.delete_all
|
70
|
-
|
75
|
+
|
71
76
|
@tasks = []
|
72
77
|
@assignees = []
|
73
78
|
1.upto(3) do |task|
|
74
|
-
t = Task.create(:name => "task #{task}")
|
79
|
+
t = Task.create(:name => "task #{task}", :id => task)
|
75
80
|
assignee_index = 1
|
76
81
|
@tasks << t
|
77
82
|
1.upto(task * 2) do |assignee|
|
@@ -83,26 +88,28 @@ describe 'related objects' do
|
|
83
88
|
|
84
89
|
it "is wired up right" do
|
85
90
|
Task.count.should == 3
|
86
|
-
Assignee.count.should == 12
|
91
|
+
Assignee.count.should == 12
|
87
92
|
end
|
88
|
-
|
93
|
+
|
89
94
|
it "has 2 assignees for the first task" do
|
90
95
|
Task.first.assignees.count.should == 2
|
91
96
|
end
|
92
|
-
|
97
|
+
|
93
98
|
it "the first assignee for the second task is employee 7" do
|
94
99
|
Task.find(2).name.should == @tasks[1].name
|
95
100
|
Task.find(2).assignees.first.assignee_name.should == @assignees[2].assignee_name
|
96
101
|
end
|
102
|
+
|
103
|
+
it 'supports adding related objects to parents' do
|
104
|
+
assignee = Assignee.new(:assignee_name => 'Zoe')
|
105
|
+
Task.count.should == 3
|
106
|
+
assignee_count = Task.find(3).assignees.count
|
107
|
+
Task.find(3).assignees.push(assignee)
|
108
|
+
Task.find(3).assignees.count.should == assignee_count + 1
|
109
|
+
end
|
110
|
+
|
97
111
|
end
|
98
|
-
|
99
|
-
it 'supports adding related objects to parents' do
|
100
|
-
assignee = Assignee.new(:assignee_name => 'Zoe')
|
101
|
-
assignee_count = Task.find(3).assignees.count
|
102
|
-
Task.find(3).assignees.push(assignee)
|
103
|
-
Task.find(3).assignees.count.should == assignee_count + 1
|
104
|
-
end
|
105
|
-
|
112
|
+
|
106
113
|
it "supports creating blank (empty) scratchpad associated objects" do
|
107
114
|
task = Task.create :name => 'watch a movie'
|
108
115
|
assignee = task.assignees.new
|
@@ -112,7 +119,7 @@ describe 'related objects' do
|
|
112
119
|
task.assignees.first.assignee_name.should == 'Chloe'
|
113
120
|
end
|
114
121
|
end
|
115
|
-
|
122
|
+
|
116
123
|
describe "supporting belongs_to" do
|
117
124
|
before do
|
118
125
|
Task.delete_all
|
@@ -172,15 +179,15 @@ describe 'related objects' do
|
|
172
179
|
@a1.task.name.should == "Feed the cat"
|
173
180
|
end
|
174
181
|
end
|
175
|
-
|
176
|
-
describe "directly assigning to child" do
|
182
|
+
|
183
|
+
describe "directly assigning to child" do
|
177
184
|
it "directly assigning a different task to an assignee changes the assignee's task" do
|
178
185
|
@a1.task = @t1.id
|
179
186
|
@a1.save
|
180
187
|
@t1.assignees.count.should == 1
|
181
188
|
@t1.assignees.first.assignee_name.should == @a1.assignee_name
|
182
189
|
end
|
183
|
-
|
190
|
+
|
184
191
|
it "directly assigning an instance of a task to an assignee changes the assignee's task" do
|
185
192
|
@a1.task = @t1
|
186
193
|
@a1.save
|
@@ -188,7 +195,7 @@ describe 'related objects' do
|
|
188
195
|
@t1.assignees.first.assignee_name.should == @a1.assignee_name
|
189
196
|
end
|
190
197
|
end
|
191
|
-
end
|
198
|
+
end
|
192
199
|
end
|
193
200
|
end
|
194
201
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: motion_model
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-12-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bubble-wrap
|
@@ -36,6 +36,7 @@ extra_rdoc_files: []
|
|
36
36
|
files:
|
37
37
|
- .gitignore
|
38
38
|
- CHANGELOG
|
39
|
+
- LICENSE
|
39
40
|
- README.md
|
40
41
|
- Rakefile
|
41
42
|
- app/app_delegate.rb
|
@@ -49,6 +50,7 @@ files:
|
|
49
50
|
- lib/motion_model/validatable.rb
|
50
51
|
- lib/motion_model/version.rb
|
51
52
|
- motion_model.gemspec
|
53
|
+
- spec/cascading_delete_spec.rb
|
52
54
|
- spec/ext_spec.rb
|
53
55
|
- spec/finder_spec.rb
|
54
56
|
- spec/model_spec.rb
|
@@ -80,6 +82,7 @@ signing_key:
|
|
80
82
|
specification_version: 3
|
81
83
|
summary: Simple model and validation mixins for RubyMotion
|
82
84
|
test_files:
|
85
|
+
- spec/cascading_delete_spec.rb
|
83
86
|
- spec/ext_spec.rb
|
84
87
|
- spec/finder_spec.rb
|
85
88
|
- spec/model_spec.rb
|