motion_model 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ 2102-03-15: Fixed bug where created_at and updated_at were being incorrectly
2
+ when restored from persistence (thanks Justin McPherson for finding
3
+ that).
4
+
5
+ Moved all NSCoder stuff out of Model to ArrayModelAdapter.
6
+
1
7
  2012-02-19: Included Doug Puchalski's great refactoring of Model that provides an
2
8
  adapter for ArrayModelAdapter. WARNING!!! This is a breaking change
3
9
  since version 0.3.8. You will have to include:
@@ -107,6 +107,15 @@ module MotionModel
107
107
  self.class.issue_notification(self, :action => 'add')
108
108
  end
109
109
 
110
+ # This adds to the ArrayStore without the magic date
111
+ # and id manipulation stuff
112
+ def add_to_store(*)
113
+ do_insert
114
+ @dirty = @new_record = false
115
+ end
116
+
117
+
118
+
110
119
  # Count of objects in the current collection
111
120
  def length
112
121
  collection.length
@@ -1,49 +1,67 @@
1
+ # The reason the String extensions are wrapped in
2
+ # conditional blocks is to reduce the likelihood
3
+ # of a namespace collision with other libraries.
4
+
1
5
  class String
2
- def humanize
3
- self.split(/_|-| /).join(' ')
6
+ unless String.instance_methods.include?(:humanize)
7
+ def humanize
8
+ self.split(/_|-| /).join(' ')
9
+ end
4
10
  end
5
11
 
6
- def titleize
7
- self.split(/_|-| /).each{|word| word[0...1] = word[0...1].upcase}.join(' ')
12
+ unless String.instance_methods.include?(:titleize)
13
+ def titleize
14
+ self.split(/_|-| /).each{|word| word[0...1] = word[0...1].upcase}.join(' ')
15
+ end
8
16
  end
9
17
 
10
- def empty?
11
- self.length < 1
18
+ unless String.instance_methods.include?(:empty?)
19
+ def empty?
20
+ self.length < 1
21
+ end
12
22
  end
13
23
 
14
- def pluralize
15
- Inflector.inflections.pluralize self
24
+ unless String.instance_methods.include?(:pluralize)
25
+ def pluralize
26
+ Inflector.inflections.pluralize self
27
+ end
16
28
  end
17
29
 
18
- def singularize
19
- Inflector.inflections.singularize self
30
+ unless String.instance_methods.include?(:singularize)
31
+ def singularize
32
+ Inflector.inflections.singularize self
33
+ end
20
34
  end
21
35
 
22
- def camelize(uppercase_first_letter = true)
23
- string = self.dup
24
- string.gsub!(/(?:_|(\/))([a-z\d]*)/i) do
25
- new_word = $2.downcase
26
- new_word[0] = new_word[0].upcase
27
- new_word = "/#{new_word}" if $1 == '/'
28
- new_word
36
+ unless String.instance_methods.include?(:camelize)
37
+ def camelize(uppercase_first_letter = true)
38
+ string = self.dup
39
+ string.gsub!(/(?:_|(\/))([a-z\d]*)/i) do
40
+ new_word = $2.downcase
41
+ new_word[0] = new_word[0].upcase
42
+ new_word = "/#{new_word}" if $1 == '/'
43
+ new_word
44
+ end
45
+ if uppercase_first_letter && uppercase_first_letter != :lower
46
+ string[0] = string[0].upcase
47
+ else
48
+ string[0] = string[0].downcase
49
+ end
50
+ string.gsub!('/', '::')
51
+ string
29
52
  end
30
- if uppercase_first_letter && uppercase_first_letter != :lower
31
- string[0] = string[0].upcase
32
- else
33
- string[0] = string[0].downcase
53
+ end
54
+
55
+ unless String.instance_methods.include?(:underscore)
56
+ def underscore
57
+ word = self.dup
58
+ word.gsub!(/::/, '/')
59
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
60
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
61
+ word.tr!("-", "_")
62
+ word.downcase!
63
+ word
34
64
  end
35
- string.gsub!('/', '::')
36
- string
37
- end
38
-
39
- def underscore
40
- word = self.dup
41
- word.gsub!(/::/, '/')
42
- word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
43
- word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
44
- word.tr!("-", "_")
45
- word.downcase!
46
- word
47
65
  end
48
66
  end
49
67
 
@@ -378,9 +378,15 @@ module MotionModel
378
378
  self.send("#{field_name}=", Time.now) if self.respond_to? field_name
379
379
  end
380
380
 
381
+ # Stub methods for hook protocols
382
+ def before_save(*); end
383
+ def after_save(*); end
384
+ def before_delete(*); end
385
+ def after_delete(*); end
386
+
381
387
  def call_hook(hook_name, postfix)
382
388
  hook = "#{hook_name}_#{postfix}"
383
- self.send(hook, self) if respond_to? hook.to_sym
389
+ self.send(hook, self)
384
390
  end
385
391
 
386
392
  def call_hooks(hook_name, &block)
@@ -442,13 +448,6 @@ module MotionModel
442
448
  column_named(column_name).options
443
449
  end
444
450
 
445
- # True if this object responds to the method or
446
- # property, otherwise false.
447
- alias_method :old_respond_to?, :respond_to?
448
- def respond_to?(method)
449
- column_named(method) || old_respond_to?(method)
450
- end
451
-
452
451
  def dirty?
453
452
  @dirty
454
453
  end
@@ -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.4.0"
7
+ VERSION = "0.4.1"
8
8
  end
data/motion_model.gemspec CHANGED
@@ -13,5 +13,6 @@ Gem::Specification.new do |gem|
13
13
  gem.name = "motion_model"
14
14
  gem.require_paths = ["lib"]
15
15
  gem.add_dependency 'bubble-wrap', '~> 1.1.4'
16
+ gem.add_dependency 'motion-support', '>=0.1.0'
16
17
  gem.version = MotionModel::VERSION
17
18
  end
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.0
4
+ version: 0.4.1
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: 2013-02-19 00:00:00.000000000 Z
12
+ date: 2013-03-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bubble-wrap
@@ -27,6 +27,22 @@ dependencies:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
29
  version: 1.1.4
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
37
+ version: 0.1.0
38
+ type: :runtime
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
30
46
  description: Simple model and validation mixins for RubyMotion
31
47
  email:
32
48
  - sxross@gmail.com
@@ -49,7 +65,6 @@ files:
49
65
  - lib/motion_model/model/formotion.rb
50
66
  - lib/motion_model/model/model.rb
51
67
  - lib/motion_model/model/model_casts.rb
52
- - lib/motion_model/model/persistence.rb
53
68
  - lib/motion_model/model/transaction.rb
54
69
  - lib/motion_model/validatable.rb
55
70
  - lib/motion_model/version.rb
@@ -64,7 +79,6 @@ files:
64
79
  - spec/model_hook_spec.rb
65
80
  - spec/model_spec.rb
66
81
  - spec/notification_spec.rb
67
- - spec/persistence_spec.rb
68
82
  - spec/relation_spec.rb
69
83
  - spec/transaction_spec.rb
70
84
  - spec/validation_spec.rb
@@ -103,7 +117,6 @@ test_files:
103
117
  - spec/model_hook_spec.rb
104
118
  - spec/model_spec.rb
105
119
  - spec/notification_spec.rb
106
- - spec/persistence_spec.rb
107
120
  - spec/relation_spec.rb
108
121
  - spec/transaction_spec.rb
109
122
  - spec/validation_spec.rb
@@ -1,102 +0,0 @@
1
- module MotionModel
2
- class PersistFileError < Exception; end
3
-
4
- module Model
5
- module PublicClassMethods
6
- # Returns the unarchived object if successful, otherwise false
7
- #
8
- # Note that subsequent calls to serialize/deserialize methods
9
- # will remember the file name, so they may omit that argument.
10
- #
11
- # Raises a +MotionModel::PersistFileFailureError+ on failure.
12
- def deserialize_from_file(file_name = nil)
13
- @file_name = file_name if file_name
14
-
15
- if File.exist? documents_file(@file_name)
16
- error_ptr = Pointer.new(:object)
17
-
18
- data = NSData.dataWithContentsOfFile(documents_file(@file_name), options:NSDataReadingMappedIfSafe, error:error_ptr)
19
-
20
- if data.nil?
21
- error = error_ptr[0]
22
- raise MotionModel::PersistFileFailureError.new "Error when reading the data: #{error}"
23
- else
24
- bulk_update do
25
- collection = NSKeyedUnarchiver.unarchiveObjectWithData(data)
26
- end
27
- return self
28
- end
29
- else
30
- return false
31
- end
32
- end
33
- # Serializes data to a persistent store (file, in this
34
- # terminology). Serialization is synchronous, so this
35
- # will pause your run loop until complete.
36
- #
37
- # +file_name+ is the name of the persistent store you
38
- # want to use. If you omit this, it will use the last
39
- # remembered file name.
40
- #
41
- # Raises a +MotionModel::PersistFileError+ on failure.
42
- def serialize_to_file(file_name = nil)
43
- @file_name = file_name if file_name
44
- error_ptr = Pointer.new(:object)
45
-
46
- data = NSKeyedArchiver.archivedDataWithRootObject @collection
47
- unless data.writeToFile(documents_file(@file_name), options: NSDataWritingAtomic, error: error_ptr)
48
- # De-reference the pointer.
49
- error = error_ptr[0]
50
-
51
- # Now we can use the `error' object.
52
- raise MotionModel::PersistFileError.new "Error when writing data: #{error}"
53
- end
54
- end
55
-
56
-
57
- def documents_file(file_name)
58
- file_path = File.join NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true), file_name
59
- file_path
60
- end
61
- end
62
-
63
- def initWithCoder(coder)
64
- self.init
65
-
66
- new_tag_id = 1
67
- columns.each do |attr|
68
- next if self.class.has_relation?(attr)
69
- # If a model revision has taken place, don't try to decode
70
- # something that's not there.
71
- if coder.containsValueForKey(attr.to_s)
72
- value = coder.decodeObjectForKey(attr.to_s)
73
- self.send("#{attr}=", value)
74
- else
75
- self.send("#{attr}=", nil)
76
- end
77
-
78
- # re-issue tags to make sure they are unique
79
- @tag = new_tag_id
80
- new_tag_id += 1
81
- end
82
- save
83
-
84
- self
85
- end
86
-
87
- # Follow Apple's recommendation not to encode missing
88
- # values.
89
- def encodeWithCoder(coder)
90
- columns.each do |attr|
91
- # Serialize attributes except the proxy has_many and belongs_to ones.
92
- unless [:belongs_to, :has_many].include? column_named(attr).type
93
- value = self.send(attr)
94
- unless value.nil?
95
- coder.encodeObject(value, forKey: attr.to_s)
96
- end
97
- end
98
- end
99
- end
100
-
101
- end
102
- end
@@ -1,187 +0,0 @@
1
- class PersistTask
2
- include MotionModel::Model
3
- include MotionModel::ArrayModelAdapter
4
- columns :name, :desc
5
- end
6
-
7
- describe 'persistence' do
8
- before do
9
- PersistTask.delete_all
10
- %w(one two three).each do |task|
11
- @tasks = PersistTask.create(:name => "name #{task}")
12
- end
13
- end
14
-
15
- it "serializes data" do
16
- lambda{PersistTask.serialize_to_file('test.dat')}.should.not.raise
17
- end
18
-
19
- it 'reads persisted model data' do
20
- PersistTask.serialize_to_file('test.dat')
21
-
22
- PersistTask.delete_all
23
-
24
- PersistTask.count.should == 0
25
-
26
- tasks = PersistTask.deserialize_from_file('test.dat')
27
-
28
- PersistTask.count.should == 3
29
- PersistTask.first.name.should == 'name one'
30
- PersistTask.last.name.should == 'name three'
31
- end
32
-
33
- describe 'model change resiliency' do
34
- it 'column addition' do
35
- class Foo
36
- include MotionModel::Model
37
- include MotionModel::ArrayModelAdapter
38
- columns :name => :string
39
- end
40
- @foo = Foo.create(:name=> 'Bob')
41
- Foo.serialize_to_file('test.dat')
42
-
43
- @foo.should.not.respond_to :address
44
-
45
- class Foo
46
- include MotionModel::Model
47
- include MotionModel::ArrayModelAdapter
48
- columns :name => :string,
49
- :address => :string
50
- end
51
- Foo.deserialize_from_file('test.dat')
52
-
53
- @foo = Foo.first
54
-
55
- @foo.name.should == 'Bob'
56
- @foo.address.should == nil
57
- @foo.should.respond_to :address
58
- Foo.length.should == 1
59
- end
60
-
61
- it "column removal" do
62
- class Foo
63
- include MotionModel::Model
64
- include MotionModel::ArrayModelAdapter
65
- columns :name => :string, :desc => :string
66
- end
67
-
68
- @foo = Foo.create(:name=> 'Bob', :desc => 'who cares anyway?')
69
- Foo.serialize_to_file('test.dat')
70
-
71
- @foo.should.respond_to :desc
72
-
73
- Object.send(:remove_const, :Foo)
74
- class Foo
75
- include MotionModel::Model
76
- include MotionModel::ArrayModelAdapter
77
- columns :name => :string,
78
- :address => :string
79
- end
80
- Foo.deserialize_from_file('test.dat')
81
-
82
- # @foo = Foo.first
83
- # @foo.name.should == 'Bob'
84
- # @foo.address.should == nil
85
- # @foo.should.not.respond_to :desc
86
- # @foo.should.respond_to :address
87
- # Foo.length.should == 1
88
- end
89
- end
90
-
91
- describe "remembering filename" do
92
- class Foo
93
- include MotionModel::Model
94
- include MotionModel::ArrayModelAdapter
95
- columns :name => :string
96
- end
97
-
98
- before do
99
- Foo.delete_all
100
- @foo = Foo.create(:name => 'Bob')
101
- end
102
-
103
- it "deserializes from last file if no filename given (previous method serialize)" do
104
- Foo.serialize_to_file('test.dat')
105
- Foo.delete_all
106
- Foo.count.should == 0
107
- Foo.deserialize_from_file
108
- Foo.count.should == 1
109
- end
110
-
111
- it "deserializes from last file if no filename given (previous method deserialize)" do
112
- Foo.serialize_to_file('test.dat')
113
- Foo.serialize_to_file('bogus.dat') # serialize sets default filename to something bogus
114
- File.delete Foo.documents_file('bogus.dat') # and we get rid of that file
115
- Foo.deserialize_from_file('test.dat') # so we'll be sure the default filename last was set by deserialize
116
- Foo.delete_all
117
- Foo.count.should == 0
118
- Foo.deserialize_from_file
119
- Foo.count.should == 1
120
- end
121
-
122
- it "serializes to last file if no filename given (previous method serialize)" do
123
- Foo.serialize_to_file('test.dat')
124
- Foo.create(:name => 'Ted')
125
- Foo.serialize_to_file
126
- Foo.delete_all
127
- Foo.count.should == 0
128
- Foo.deserialize_from_file('test.dat')
129
- Foo.count.should == 2
130
- end
131
-
132
- it "serializes to last file if no filename given (previous method deserialize)" do
133
- Foo.serialize_to_file('test.dat')
134
- Foo.delete_all
135
- Foo.serialize_to_file('bogus.dat') # serialize sets default filename to something bogus
136
- File.delete Foo.documents_file('bogus.dat') # and we get rid of that file
137
- Foo.deserialize_from_file('test.dat') # so we'll be sure the default filename was last set by deserialize
138
- Foo.create(:name => 'Ted')
139
- Foo.serialize_to_file
140
- Foo.delete_all
141
- Foo.count.should == 0
142
- Foo.deserialize_from_file('test.dat')
143
- Foo.count.should == 2
144
- end
145
-
146
- end
147
- end
148
-
149
- class Parent
150
- include MotionModel::Model
151
- include MotionModel::ArrayModelAdapter
152
- columns :name
153
- has_many :children
154
- end
155
-
156
- class Child
157
- include MotionModel::Model
158
- include MotionModel::ArrayModelAdapter
159
- columns :name
160
- belongs_to :parent
161
- end
162
-
163
- describe "serialization of relations" do
164
- before do
165
- parent = Parent.create(:name => 'BoB')
166
- parent.children.create :name => 'Fergie'
167
- parent.children.create :name => 'Will I Am'
168
- end
169
-
170
- it "is wired up right" do
171
- Parent.first.name.should == 'BoB'
172
- Parent.first.children.count.should == 2
173
- end
174
-
175
- it "serializes and deserializes properly" do
176
- Parent.serialize_to_file('parents.dat')
177
- Child.serialize_to_file('children.dat')
178
- Parent.delete_all
179
- Child.delete_all
180
- Parent.deserialize_from_file('parents.dat')
181
- Child.deserialize_from_file('children.dat')
182
- Parent.first.name.should == 'BoB'
183
- Parent.first.children.count.should == 2
184
- Parent.first.children.first.name.should == 'Fergie'
185
- end
186
- end
187
-