motion_model 0.5.4 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9fa1c9fe578bfec914279fad0ced2ff4f6df49a1
4
- data.tar.gz: 9952292bdef965d7ab76ab1f8a8c906a2f69fd67
3
+ metadata.gz: df4d3bdf11fc759be47d10723d1f84ddae0e43fb
4
+ data.tar.gz: 2624cbb78924e42b3c2d1e008f2c4e75f2427598
5
5
  SHA512:
6
- metadata.gz: 41cb26133a685d546300513985f57e41fa58b4b8eb678689311d2b23bfddb6d9394d66f6f849ad4faff27369f3f95945c1525dcd7fa0105dfd801812236f2c8f
7
- data.tar.gz: 8a1a29e8a90468f7392b1c9f54281fa3038956ad8776be4e668d5286057fbc164af9d25ca6b59efefdf2b0eb0daedff3481506264d33e9831fc64a1105d70d41
6
+ metadata.gz: 8e77f43b0997215ae37c366d2a5eb8f69c27cebe40bc5902dcd0c07c219d9a2e31120c0bb7da6859d530779f934c034e98819351696649dbb7c691d9c4dd6627
7
+ data.tar.gz: 45029f00583ddbb5e839d5ba173c0228d8e4ddad0bb87773c4adf92c761df3949ad79679fe0c9c21e4c30ff7ab46bb85454606584624efccc22b875ee8c9558a
data/README.md CHANGED
@@ -10,7 +10,7 @@ data validation and actually quite a bit more.
10
10
  File | Module | Description
11
11
  ---------------------|---------------------------|------------------------------------
12
12
  **ext.rb** | N/A | Core Extensions that provide a few Rails-like niceties. Nothing new here, moving on...
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
+ **model.rb** | MotionModel::Model | You should read about it in "[What MotionModel Can Do](#what-motionmodel-can-do)". Model is the raison d'etre and the centerpiece of MotionModel.
14
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.
15
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*.
16
16
  **formotion.rb** | MotionModel::Formotion | Provides an interface between MotionModel and Formotion
@@ -21,7 +21,7 @@ you like with it. See the LICENSE file in this project.
21
21
 
22
22
  * [Getting Going](#getting-going)
23
23
  * [Bugs, Features, and Issues, Oh My!](#bugs-features-and-issues-oh-my)
24
- * [What Model Can Do](#what-model-can-do)
24
+ * [What MotionModel Can Do](#what-motionmodel-can-do)
25
25
  * [Model Data Types](#model-data-types)
26
26
  * [Validation Methods](#validation-methods)
27
27
  * [Model Instances and Unique IDs](#model-instances-and-unique-ids)
@@ -274,6 +274,65 @@ Currently supported types are:
274
274
  You are really not encouraged to stuff big things in your models, which is why a blob type
275
275
  is not implemented. The smaller your data, the less overhead involved in saving/loading.
276
276
 
277
+ ### User Defined Types
278
+
279
+ *New as of 0.6*: THIS FEATURE IS EXPERIMENTAL. You can use a class name instead of the
280
+ symbols to specify your types. Thus, you can choose more complex types in much the same
281
+ way as you might in a NOSQL database. Consider these examples:
282
+
283
+ ```ruby
284
+ class Address
285
+ include MotionModel::Model
286
+ include MotionModel::ArrayModelAdapter
287
+ columns street: String,
288
+ city: String,
289
+ state: String,
290
+ zip: Integer
291
+ end
292
+
293
+ class Person
294
+ include MotionModel::Model
295
+ include MotionModel::ArrayModelAdapter
296
+
297
+ columns name: String,
298
+ address: Address
299
+ end
300
+ ```
301
+
302
+ You can now use this `Person` class as follows:
303
+
304
+ ```ruby
305
+ p = Person.find(:name).eq('Laurent').first
306
+ p.address.country = 'Belgium'
307
+ p.save
308
+ ```
309
+
310
+ > **Note** Address includes all the MotionModel goodness. That is so that any class that embeds it can also persist it. You may be able to get away with defining `encodeWithCoder(coder)` and `initWithCoder(coder)` -- it's your choice, but not currently supported.
311
+
312
+ ### Native Ruby Types
313
+
314
+ You can also use native Ruby types (limited to those supported by RubyMotion so no `DateTime`):
315
+
316
+ ```ruby
317
+ class Person
318
+ include MotionModel::Model
319
+ include MotionModel::ArrayModelAdapter
320
+
321
+ columns name: String,
322
+ address: Hash
323
+ end
324
+ ```
325
+
326
+ and you can use the class as follows:
327
+
328
+ ```ruby
329
+ Person.create(name: 'Miss Piggy', address: {street: '111 Swine Ave', city: 'Muppetville', state: 'IN'})
330
+ ```
331
+
332
+ etc.
333
+
334
+ > Special note: This is a bleeding edge feature as of 0.6, and because of the semantics of copying in Ruby, there are some deep copy situations that may cause objects to be copied or updated incompletely. These should create 100% reproducible bugs in your code so they should not sneak up on you. Nevertheless, understand that the guarantee is a shallow copy. Tests show that an array inside a class copies properly. YMMV.
335
+
277
336
  ### Special Columns
278
337
 
279
338
  The two column names, `created_at` and `updated_at` will be adjusted automatically if they
@@ -1,5 +1,6 @@
1
1
  module DateParser
2
2
  @@isoDateFormatter = nil
3
+ @@detector = nil
3
4
 
4
5
  # Parse a date string: E.g.:
5
6
  #
@@ -58,6 +59,16 @@ module DateParser
58
59
  date = @@isoDateFormatter.dateFromString date_string
59
60
  return date
60
61
  end
62
+
63
+ def self.allocate_data_detector
64
+ error = Pointer.new(:object)
65
+ @@detector = NSDataDetector.dataDetectorWithTypes(NSTextCheckingTypeDate, error:error)
66
+ end
67
+
68
+ def self.detector
69
+ allocate_data_detector if @@detector.nil?
70
+ return @@detector
71
+ end
61
72
  end
62
73
 
63
74
 
@@ -317,7 +317,7 @@ module MotionModel
317
317
  raise ArgumentError.new("you cannot use `description' as a column name because of a conflict with Cocoa.") if name.to_s == 'description'
318
318
 
319
319
  case options
320
- when Symbol, String
320
+ when Symbol, String, Class
321
321
  add_field(name, options)
322
322
  when Hash
323
323
  add_field(name, options.delete(:type), options)
@@ -448,6 +448,10 @@ module MotionModel
448
448
  "#{self.class.name}##{id}"
449
449
  end
450
450
 
451
+ def motion_model?
452
+ true
453
+ end
454
+
451
455
  def new_record?
452
456
  @new_record
453
457
  end
@@ -486,6 +490,11 @@ module MotionModel
486
490
  @data[name]
487
491
  end
488
492
 
493
+ def write_attribute(attr_name, value)
494
+ @data[attr_name] = value
495
+ @dirty = true
496
+ end
497
+
489
498
  # Default to_i implementation returns value of id column, much as
490
499
  # in Rails.
491
500
 
@@ -825,7 +834,7 @@ module MotionModel
825
834
  when Symbol
826
835
  {column => self.send(column)}
827
836
  else
828
- {column => default}
837
+ {column => (value.nil? ? default : value)}
829
838
  end
830
839
  end
831
840
 
@@ -44,6 +44,42 @@ module MotionModel
44
44
  String(arg)
45
45
  end
46
46
 
47
+ def cast_to_arbitrary_class(arg, klass)
48
+ # This little oddity is because a number of built-in
49
+ # Ruby classes cannot be dup'ed. Not only that, they
50
+ # respond_to?(:dup) but raise an exception when you
51
+ # actually do it. Not only that, the behavior can be
52
+ # different depending on architecture (32- versus 64-bit).
53
+ #
54
+ # This is Ruby, folks, not just RubyMotion.
55
+ #
56
+ # We don't have to worry if it's a MotionModel, because
57
+ # using a reference to the data is ok. The by-reference
58
+ # copy is fine.
59
+
60
+ return arg if arg.respond_to?(:motion_model?)
61
+
62
+ # Another case is where there is a predefined cast rule
63
+ # on the type declared as a class level method motion_model_cast:
64
+
65
+ if klass.respond_to?(:motion_model_cast)
66
+ return klass.send(:motion_model_cast, arg)
67
+ end
68
+
69
+ # But if it is not a MotionModel, we either need to dup
70
+ # it (for most cases), or just assign it (for built-in
71
+ # types like Integer, Fixnum, Float, NilClass, etc.)
72
+
73
+ result = nil
74
+ begin
75
+ result = arg.dup
76
+ rescue
77
+ result = arg
78
+ end
79
+
80
+ result
81
+ end
82
+
47
83
  def cast_to_type(column_name, arg) #nodoc
48
84
  return nil if arg.nil? && ![ :boolean, :bool ].include?(column_type(column_name))
49
85
 
@@ -56,9 +92,11 @@ module MotionModel
56
92
  when :text then cast_to_string(arg)
57
93
  when :array then cast_to_array(arg)
58
94
  when :hash then cast_to_hash(arg)
95
+ when Class then cast_to_arbitrary_class(arg, column_type(column_name))
59
96
  else
60
97
  raise ArgumentError.new("type #{column_name} : #{column_type(column_name)} is not possible to cast.")
61
98
  end
62
99
  end
63
100
  end
64
101
  end
102
+
data/motion/version.rb CHANGED
@@ -4,5 +4,5 @@
4
4
  # or forward port their code to take advantage
5
5
  # of adapters.
6
6
  module MotionModel
7
- VERSION = "0.5.4"
7
+ VERSION = "0.6.0"
8
8
  end
data/spec/date_spec.rb CHANGED
@@ -66,6 +66,13 @@ describe "time conversions" do
66
66
  end
67
67
  end
68
68
 
69
+ describe "date parser data detector reuse" do
70
+ it "creates a data detector if none is present" do
71
+ DateParser.class_variable_get(:@@detector).should.be.nil
72
+ DateParser.detector.class.should == NSDataDetector
73
+ end
74
+ end
75
+
69
76
  describe "parsing ISO8601 date formats" do
70
77
  class Model
71
78
  include MotionModel::Model
@@ -22,7 +22,7 @@ describe 'Type casting' do
22
22
  @convertible.a_date = '2012-09-15'
23
23
  @convertible.an_array = 1..10
24
24
  end
25
-
25
+
26
26
  it 'does the type casting on instantiation' do
27
27
  @convertible.a_boolean.should.is_a FalseClass
28
28
  @convertible.an_int.should.is_a Integer
@@ -108,7 +108,7 @@ describe 'Type casting' do
108
108
  it 'returns a NSDate for a date field' do
109
109
  @convertible.a_date.should.is_a(NSDate)
110
110
  end
111
-
111
+
112
112
  it 'the date field should be the same as it was in string form' do
113
113
  @convertible.a_date.to_s.should.match(/^2012-09-15/)
114
114
  end
@@ -125,4 +125,125 @@ describe 'Type casting' do
125
125
  it 'the array field should be the same as the range form' do
126
126
  (@convertible.an_array.first..@convertible.an_array.last).should.equal(1..10)
127
127
  end
128
+
129
+ describe 'can cast to an arbitrary type' do
130
+ class HasArbitraryTypes
131
+ include MotionModel::Model
132
+ include MotionModel::ArrayModelAdapter
133
+ columns name: String,
134
+ properties: Hash
135
+ end
136
+
137
+ class EmbeddedAddress
138
+ include MotionModel::Model
139
+ include MotionModel::ArrayModelAdapter
140
+ columns street: String,
141
+ city: String,
142
+ state: String,
143
+ zip: Integer,
144
+ pets: Array
145
+ # attr_accessor :street
146
+ # attr_accessor :city
147
+ # attr_accessor :state
148
+ # attr_accessor :zip
149
+ # attr_accessor :pets
150
+
151
+ # def initialize(options = {})
152
+ # @street = options[:street] if options[:street]
153
+ # @city = options[:city] if options[:city]
154
+ # @state = options[:state] if options[:state]
155
+ # @zip = options[:zip] if options[:zip]
156
+ # @pets = options[:pets] if options[:pets]
157
+ # end
158
+ end
159
+
160
+ class EmbeddingClass
161
+ include MotionModel::Model
162
+ include MotionModel::ArrayModelAdapter
163
+ columns name: String,
164
+ address: EmbeddedAddress,
165
+ pets: Array
166
+ end
167
+
168
+ before do
169
+ EmbeddingClass.delete_all
170
+ HasArbitraryTypes.delete_all
171
+ end
172
+
173
+ it "creation works" do
174
+ arb = HasArbitraryTypes.create(name: 'A Name', properties: {address: '123 Main Street', city: 'Seattle', state: 'WA'})
175
+ arb.name.should == 'A Name'
176
+ arb.properties.class.should == Hash
177
+ arb.properties[:address].should == '123 Main Street'
178
+ end
179
+
180
+ it "updating works" do
181
+ HasArbitraryTypes.create(name: 'Another Name', properties: {address: '123 Main Street', city: 'Seattle', state: 'WA'})
182
+ arb = HasArbitraryTypes.first
183
+ arb.properties[:address] = '234 Main Street'
184
+ arb.save
185
+ arb.properties[:address].should == '234 Main Street'
186
+ arb = HasArbitraryTypes.find(:name).eq('Another Name').first
187
+ arb.properties[:address].should == '234 Main Street'
188
+ end
189
+
190
+ it "creating objects with embedded documents works" do
191
+ addr = EmbeddedAddress.new(street: '2211 First', city: 'Seattle', state: 'WA', zip: 98104)
192
+ emb = EmbeddingClass.create(name: 'On Class', address: addr)
193
+ emb.address.class.should == EmbeddedAddress
194
+ emb.address.street.should == '2211 First'
195
+ end
196
+
197
+ it "copies embedded types" do
198
+ addr = EmbeddedAddress.new(street: '2211 First', city: 'Seattle', state: 'WA', zip: 98104, pets: ['rover', 'fido', 'barney'])
199
+ emb = EmbeddingClass.create(name: 'On Class', address: addr)
200
+ emb.address.pets.class.should == Array
201
+ emb.address.pets.should.include?('barney')
202
+ EmbeddingClass.first.address.pets.should.include?('barney')
203
+ end
204
+
205
+ it "updates embedded types" do
206
+ addr = EmbeddedAddress.new(street: '3322 First', city: 'Seattle', state: 'WA', zip: 98104, pets: ['rover', 'fido', 'barney'])
207
+ emb = EmbeddingClass.create(name: 'On Class', address: addr)
208
+ emb.address.pets.should.include?('barney')
209
+ found = EmbeddingClass.find(:name).eq('On Class').first
210
+ found.address.pets.should.include?('barney')
211
+ found.address.pets.delete('barney')
212
+ found.save
213
+ EmbeddingClass.find(:name).eq('On Class').first.address.pets.should.not.include?('barney')
214
+ end
215
+
216
+ it "serializes with arbitrary Ruby types without error" do
217
+ HasArbitraryTypes.create(name: 'A Name', properties: {address: '123 Main Street', city: 'Seattle', state: 'WA'})
218
+ lambda{HasArbitraryTypes.serialize_to_file('test.dat')}.should.not.raise
219
+ end
220
+
221
+ it "deserializes arbitrary Ruby types with correct values" do
222
+ HasArbitraryTypes.create(name: 'A Name', properties: {address: '123 Main Street', city: 'Seattle', state: 'WA'})
223
+ HasArbitraryTypes.serialize_to_file('test.dat')
224
+ HasArbitraryTypes.deserialize_from_file('test.dat')
225
+ result = HasArbitraryTypes.find(:name).eq('A Name').first
226
+ result.should.not.be.nil
227
+ result.properties.class.should == Hash
228
+ result.properties[:city].should == 'Seattle'
229
+ end
230
+
231
+ it "serializes arbitrary user-defined classes without error" do
232
+ addr = EmbeddedAddress.new(street: '2211 First', city: 'Seattle', state: 'WA', zip: 98104)
233
+ emb = EmbeddingClass.create(name: 'On Class', address: addr)
234
+ lambda{EmbeddingClass.serialize_to_file('test.dat')}.should.not.raise
235
+ end
236
+
237
+ it "deserializes arbitrary user-defined classes with correct values" do
238
+ addr = EmbeddedAddress.new(street: '2211 First', city: 'Seattle', state: 'WA', zip: 98104, pets: ['Katniss', 'Peeta'])
239
+ emb = EmbeddingClass.create(name: 'On Class', address: addr)
240
+ lambda{EmbeddingClass.serialize_to_file('test.dat')}.should.not.raise
241
+ EmbeddingClass.deserialize_from_file('test.dat')
242
+ result = EmbeddingClass.find(:name).eq('On Class').first
243
+ result.should.not.be.nil
244
+ result.address.class.should == EmbeddedAddress
245
+ result.address.city.should == 'Seattle'
246
+ result.address.pets.should.include?('Katniss')
247
+ end
248
+ end
128
249
  end
data/spec/model_spec.rb CHANGED
@@ -1,21 +1,31 @@
1
- class Task
1
+ class ModelSpecTask
2
2
  include MotionModel::Model
3
3
  include MotionModel::ArrayModelAdapter
4
4
  columns :name => :string,
5
5
  :details => :string,
6
- :some_day => :date
6
+ :some_day => :date,
7
+ :enabled => {:type => :boolean, :default => false}
7
8
 
8
9
  def custom_attribute_by_method
9
10
  "#{name} - #{details}"
10
11
  end
11
12
  end
12
13
 
13
- class ATask
14
+ class AModelSpecTask
14
15
  include MotionModel::Model
15
16
  include MotionModel::ArrayModelAdapter
16
17
  columns :name, :details, :some_day
17
18
  end
18
19
 
20
+ class BModelSpecTask
21
+ include MotionModel::Model
22
+ include MotionModel::ArrayModelAdapter
23
+ columns :name, :details
24
+ def details=(value)
25
+ write_attribute(:details, "overridden")
26
+ end
27
+ end
28
+
19
29
  class TypeCast
20
30
  include MotionModel::Model
21
31
  include MotionModel::ArrayModelAdapter
@@ -32,16 +42,16 @@ end
32
42
  describe "Creating a model" do
33
43
  describe 'column macro behavior' do
34
44
  before do
35
- Task.delete_all
45
+ ModelSpecTask.delete_all
36
46
  end
37
47
 
38
48
  it 'succeeds when creating a valid model from attributes' do
39
- a_task = Task.new(:name => 'name', :details => 'details')
49
+ a_task = ModelSpecTask.new(:name => 'name', :details => 'details')
40
50
  a_task.name.should.equal('name')
41
51
  end
42
52
 
43
53
  it 'creates a model with all attributes even if some omitted' do
44
- atask = Task.create(:name => 'bob')
54
+ atask = ModelSpecTask.create(:name => 'bob')
45
55
  atask.should.respond_to(:details)
46
56
  end
47
57
 
@@ -49,175 +59,185 @@ describe "Creating a model" do
49
59
  a_type_test = TypeCast.new
50
60
  a_type_test.an_int.should.equal(3)
51
61
  end
62
+
63
+ it "on initialization uses supplied value instead of default value, if supplied" do
64
+ a_task = ModelSpecTask.new(:enabled => true)
65
+ a_task.enabled.should.be.true
66
+ end
67
+
68
+ it "on creation uses supplied value instead of default value, if supplied" do
69
+ a_task = ModelSpecTask.create(:enabled => true)
70
+ a_task.enabled.should.be.true
71
+ end
52
72
 
53
73
  it "can check for a column's existence on a model" do
54
- Task.column?(:name).should.be.true
74
+ ModelSpecTask.column?(:name).should.be.true
55
75
  end
56
76
 
57
77
  it "can check for a column's existence on an instance" do
58
- a_task = Task.new(:name => 'name', :details => 'details')
78
+ a_task = ModelSpecTask.new(:name => 'name', :details => 'details')
59
79
  a_task.column?(:name).should.be.true
60
80
  end
61
81
 
62
82
  it "gets a list of columns on a model" do
63
- cols = Task.columns
83
+ cols = ModelSpecTask.columns
64
84
  cols.should.include(:name)
65
85
  cols.should.include(:details)
66
86
  end
67
87
 
68
88
  it "gets a list of columns on an instance" do
69
- a_task = Task.new
89
+ a_task = ModelSpecTask.new
70
90
  cols = a_task.columns
71
91
  cols.should.include(:name)
72
92
  cols.should.include(:details)
73
93
  end
74
94
 
75
95
  it "columns can be specified as a Hash" do
76
- lambda{Task.new}.should.not.raise
77
- Task.new.column?(:name).should.be.true
96
+ lambda{ModelSpecTask.new}.should.not.raise
97
+ ModelSpecTask.new.column?(:name).should.be.true
78
98
  end
79
99
 
80
100
  it "columns can be specified as an Array" do
81
- lambda{ATask.new}.should.not.raise
82
- Task.new.column?(:name).should.be.true
101
+ lambda{AModelSpecTask.new}.should.not.raise
102
+ ModelSpecTask.new.column?(:name).should.be.true
83
103
  end
84
104
 
85
105
  it "the type of a column can be retrieved" do
86
- Task.new.column_type(:some_day).should.equal(:date)
106
+ ModelSpecTask.new.column_type(:some_day).should.equal(:date)
87
107
  end
88
108
 
89
109
  end
90
110
 
91
111
  describe "ID handling" do
92
112
  before do
93
- Task.delete_all
113
+ ModelSpecTask.delete_all
94
114
  end
95
115
 
96
116
 
97
117
  it 'creates an id if none present' do
98
- task = Task.create
118
+ task = ModelSpecTask.create
99
119
  task.should.respond_to(:id)
100
120
  end
101
121
 
102
122
  it 'does not overwrite an existing ID' do
103
- task = Task.create(:id => 999)
123
+ task = ModelSpecTask.create(:id => 999)
104
124
  task.id.should.equal(999)
105
125
  end
106
126
 
107
127
  it 'creates multiple objects with unique ids' do
108
- Task.create.id.should.not.equal(Task.create.id)
128
+ ModelSpecTask.create.id.should.not.equal(ModelSpecTask.create.id)
109
129
  end
110
130
 
111
131
  end
112
132
 
113
133
  describe 'count and length methods' do
114
134
  before do
115
- Task.delete_all
135
+ ModelSpecTask.delete_all
116
136
  end
117
137
 
118
138
  it 'has a length method' do
119
- Task.should.respond_to(:length)
139
+ ModelSpecTask.should.respond_to(:length)
120
140
  end
121
141
 
122
142
  it 'has a count method' do
123
- Task.should.respond_to(:count)
143
+ ModelSpecTask.should.respond_to(:count)
124
144
  end
125
145
 
126
146
  it 'when there is one element, length returns 1' do
127
- task = Task.create
128
- Task.length.should.equal(1)
147
+ task = ModelSpecTask.create
148
+ ModelSpecTask.length.should.equal(1)
129
149
  end
130
150
 
131
151
  it 'when there is one element, count returns 1' do
132
- task = Task.create
133
- Task.count.should.equal(1)
152
+ task = ModelSpecTask.create
153
+ ModelSpecTask.count.should.equal(1)
134
154
  end
135
155
 
136
156
  it 'instance variables have access to length and count' do
137
- task = Task.create
157
+ task = ModelSpecTask.create
138
158
  task.length.should.equal(1)
139
159
  task.count.should.equal(1)
140
160
  end
141
161
 
142
162
  it 'when there is more than one element, length returned is correct' do
143
- 10.times { Task.create }
144
- Task.length.should.equal(10)
163
+ 10.times { ModelSpecTask.create }
164
+ ModelSpecTask.length.should.equal(10)
145
165
  end
146
166
 
147
167
  end
148
168
 
149
169
  describe 'adding or updating' do
150
170
  before do
151
- Task.delete_all
171
+ ModelSpecTask.delete_all
152
172
  end
153
173
 
154
174
  it 'adds to the collection when a new task is saved' do
155
- task = Task.new
156
- lambda{task.save}.should.change{Task.count}
175
+ task = ModelSpecTask.new
176
+ lambda{task.save}.should.change{ModelSpecTask.count}
157
177
  end
158
178
 
159
179
  it 'does not add to the collection when an existing task is saved' do
160
- task = Task.create(:name => 'updateable')
180
+ task = ModelSpecTask.create(:name => 'updateable')
161
181
  task.name = 'updated'
162
- lambda{task.save}.should.not.change{Task.count}
182
+ lambda{task.save}.should.not.change{ModelSpecTask.count}
163
183
  end
164
184
 
165
185
  it 'updates data properly' do
166
- task = Task.create(:name => 'updateable')
186
+ task = ModelSpecTask.create(:name => 'updateable')
167
187
  task.name = 'updated'
168
- Task.where(:name).eq('updated').should == 0
169
- lambda{task.save}.should.change{Task.where(:name).eq('updated')}
188
+ ModelSpecTask.where(:name).eq('updated').should == 0
189
+ lambda{task.save}.should.change{ModelSpecTask.where(:name).eq('updated')}
170
190
  end
171
191
  end
172
192
 
173
193
  describe 'deleting' do
174
194
  before do
175
- Task.delete_all
176
- Task.bulk_update do
177
- 1.upto(10) {|i| Task.create(:name => "task #{i}")}
195
+ ModelSpecTask.delete_all
196
+ ModelSpecTask.bulk_update do
197
+ 1.upto(10) {|i| ModelSpecTask.create(:name => "task #{i}")}
178
198
  end
179
199
  end
180
200
 
181
201
  it 'deletes a row' do
182
- target = Task.find(:name).eq('task 3').first
202
+ target = ModelSpecTask.find(:name).eq('task 3').first
183
203
  target.should.not == nil
184
204
  target.delete
185
- Task.find(:name).eq('task 3').count.should.equal 0
205
+ ModelSpecTask.find(:name).eq('task 3').count.should.equal 0
186
206
  end
187
207
 
188
208
  it 'deleting a row changes length' do
189
- target = Task.find(:name).eq('task 2').first
190
- lambda{target.delete}.should.change{Task.length}
209
+ target = ModelSpecTask.find(:name).eq('task 2').first
210
+ lambda{target.delete}.should.change{ModelSpecTask.length}
191
211
  end
192
212
 
193
213
  it 'undeleting a row restores it' do
194
- target = Task.find(:name).eq('task 3').first
214
+ target = ModelSpecTask.find(:name).eq('task 3').first
195
215
  target.should.not == nil
196
216
  target.delete
197
217
  target.undelete
198
- Task.find(:name).eq('task 3').count.should.equal 1
218
+ ModelSpecTask.find(:name).eq('task 3').count.should.equal 1
199
219
  end
200
220
  end
201
221
 
202
222
  describe 'Handling Attribute Implementation' do
203
223
  it 'raises a NoMethodError exception when an unknown attribute it referenced' do
204
- task = Task.new
224
+ task = ModelSpecTask.new
205
225
  lambda{task.bar}.should.raise(NoMethodError)
206
226
  end
207
227
 
208
228
  it 'raises a NoMethodError exception when an unknown attribute receives an assignment' do
209
- task = Task.new
229
+ task = ModelSpecTask.new
210
230
  lambda{task.bar = 'foo'}.should.raise(NoMethodError)
211
231
  end
212
232
 
213
233
  it 'successfully retrieves by attribute' do
214
- task = Task.create(:name => 'my task')
234
+ task = ModelSpecTask.create(:name => 'my task')
215
235
  task.name.should == 'my task'
216
236
  end
217
237
 
218
238
  describe "dirty" do
219
239
  before do
220
- @new_task = Task.new
240
+ @new_task = ModelSpecTask.new
221
241
  end
222
242
 
223
243
  it 'marks a new object as dirty' do
@@ -246,8 +266,8 @@ describe "Creating a model" do
246
266
 
247
267
  describe 'defining custom attributes' do
248
268
  before do
249
- Task.delete_all
250
- @task = Task.create :name => 'Feed the Cat', :details => 'Get food, pour out'
269
+ ModelSpecTask.delete_all
270
+ @task = ModelSpecTask.create :name => 'Feed the Cat', :details => 'Get food, pour out'
251
271
  end
252
272
 
253
273
  it 'uses a custom attribute by method' do
@@ -255,6 +275,25 @@ describe "Creating a model" do
255
275
  end
256
276
  end
257
277
 
278
+ describe 'overloading accessors using write_attribute' do
279
+ before do
280
+ BModelSpecTask.delete_all
281
+ end
282
+
283
+ it 'updates the attribute on creation' do
284
+ @task = BModelSpecTask.create :name => 'foo', :details => 'bar'
285
+ @task.details.should.equal('overridden')
286
+ @task.should.not.be.dirty
287
+ end
288
+
289
+ it 'updates the attribute but does not save a new instance' do
290
+ @task = BModelSpecTask.new :name => 'foo', :details => 'bar'
291
+ @task.details.should.equal('overridden')
292
+ @task.should.be.dirty
293
+ end
294
+
295
+ end
296
+
258
297
  describe 'protecting timestamps' do
259
298
  class NoTimestamps
260
299
  include MotionModel::Model
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: motion_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.4
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Ross
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-24 00:00:00.000000000 Z
11
+ date: 2014-11-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bubble-wrap
@@ -105,7 +105,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
105
105
  version: '0'
106
106
  requirements: []
107
107
  rubyforge_project:
108
- rubygems_version: 2.3.0
108
+ rubygems_version: 2.2.2
109
109
  signing_key:
110
110
  specification_version: 4
111
111
  summary: Simple model and validation mixins for RubyMotion
@@ -127,3 +127,4 @@ test_files:
127
127
  - spec/relation_spec.rb
128
128
  - spec/transaction_spec.rb
129
129
  - spec/validation_spec.rb
130
+ has_rdoc: