motion_model 0.4.1 → 0.4.2
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 +16 -5
- data/Gemfile +4 -0
- data/README.md +46 -0
- data/Rakefile +4 -1
- data/lib/motion_model/adapters/array_model_adapter.rb +52 -28
- data/lib/motion_model/adapters/array_model_persistence.rb +141 -0
- data/lib/motion_model/ext.rb +17 -0
- data/lib/motion_model/model/array_finder_query.rb +6 -1
- data/lib/motion_model/model/column.rb +24 -8
- data/lib/motion_model/model/formotion.rb +4 -4
- data/lib/motion_model/model/model.rb +360 -92
- data/lib/motion_model/model/model_casts.rb +13 -8
- data/lib/motion_model/model/transaction.rb +1 -1
- data/lib/motion_model/validatable.rb +29 -15
- data/lib/motion_model/version.rb +1 -1
- data/spec/adapter_spec.rb +30 -0
- data/spec/array_model_persistence_spec.rb +229 -0
- data/spec/cascading_delete_spec.rb +5 -5
- data/spec/date_spec.rb +26 -13
- data/spec/finder_spec.rb +1 -0
- data/spec/formotion_spec.rb +1 -0
- data/spec/model_hook_spec.rb +5 -4
- data/spec/model_spec.rb +1 -7
- data/spec/relation_spec.rb +10 -13
- metadata +66 -49
data/CHANGELOG
CHANGED
@@ -1,10 +1,21 @@
|
|
1
|
-
|
1
|
+
2013-04-13: WARNING: Possible breaking change. Hook methods changed to send
|
2
|
+
affected object. So, if you have:
|
3
|
+
|
4
|
+
def after_create
|
5
|
+
|
6
|
+
You will get an error about expecting 1 argument, got 0
|
7
|
+
|
8
|
+
The correct signature is:
|
9
|
+
|
10
|
+
def after_create(sender)
|
11
|
+
|
12
|
+
2013-03-15: Fixed bug where created_at and updated_at were being incorrectly
|
2
13
|
when restored from persistence (thanks Justin McPherson for finding
|
3
14
|
that).
|
4
15
|
|
5
16
|
Moved all NSCoder stuff out of Model to ArrayModelAdapter.
|
6
17
|
|
7
|
-
|
18
|
+
2013-02-19: Included Doug Puchalski's great refactoring of Model that provides an
|
8
19
|
adapter for ArrayModelAdapter. WARNING!!! This is a breaking change
|
9
20
|
since version 0.3.8. You will have to include:
|
10
21
|
|
@@ -14,9 +25,9 @@
|
|
14
25
|
Failure to include an adapter (note: spelling counts :) will result
|
15
26
|
in an exception so this will not quietly fail.
|
16
27
|
|
17
|
-
|
28
|
+
2013-01-24: Added block-structured transactions.
|
18
29
|
|
19
|
-
|
30
|
+
2013-01-14: Fixed problem where data returned from forms was of type NSString, which
|
20
31
|
confused some monkey-patching code.
|
21
32
|
Changed before_ hooks such that handlers returning false would terminate
|
22
33
|
the process. So, if before_save returns anything other than false, the
|
@@ -24,7 +35,7 @@
|
|
24
35
|
interrupted.
|
25
36
|
Fixed immutable string issue in validations.
|
26
37
|
|
27
|
-
|
38
|
+
2013-01-09: Added automatic date/timestamp support for created_at and updated_at columns
|
28
39
|
Added Hash extension except, Array introspection methods has_hash_key? and
|
29
40
|
has_hash_value?
|
30
41
|
Commit of Formotion module including optional inclusion/suppression of the
|
data/Gemfile
ADDED
data/README.md
CHANGED
@@ -97,6 +97,7 @@ You can define your models and their schemas in Ruby. For example:
|
|
97
97
|
```ruby
|
98
98
|
class Task
|
99
99
|
include MotionModel::Model
|
100
|
+
include MotionModel::ArrayModelAdapter
|
100
101
|
|
101
102
|
columns :name => :string,
|
102
103
|
:description => :string,
|
@@ -117,6 +118,7 @@ Models support default values, so if you specify your model like this, you get d
|
|
117
118
|
```ruby
|
118
119
|
class Task
|
119
120
|
include MotionModel::Model
|
121
|
+
include MotionModel::ArrayModelAdapter
|
120
122
|
|
121
123
|
columns :name => :string,
|
122
124
|
:due_date => {:type => :date, :default => '2012-09-15'}
|
@@ -128,6 +130,7 @@ You can also include the `Validatable` module to get field validation. For examp
|
|
128
130
|
```ruby
|
129
131
|
class Task
|
130
132
|
include MotionModel::Model
|
133
|
+
include MotionModel::ArrayModelAdapter
|
131
134
|
include MotionModel::Validatable
|
132
135
|
|
133
136
|
columns :name => :string,
|
@@ -188,6 +191,7 @@ To use validations in your model, declare your model as follows:
|
|
188
191
|
```ruby
|
189
192
|
class MyValidatableModel
|
190
193
|
include MotionModel::Model
|
194
|
+
include MotionModel::ArrayModelAdapter
|
191
195
|
include MotionModel::Validatable
|
192
196
|
|
193
197
|
# All other model-y stuff here
|
@@ -317,12 +321,14 @@ Using MotionModel
|
|
317
321
|
```ruby
|
318
322
|
class Task
|
319
323
|
include MotionModel::Model
|
324
|
+
include MotionModel::ArrayModelAdapter
|
320
325
|
columns :name => :string
|
321
326
|
has_many :assignees
|
322
327
|
end
|
323
328
|
|
324
329
|
class Assignee
|
325
330
|
include MotionModel::Model
|
331
|
+
include MotionModel::ArrayModelAdapter
|
326
332
|
columns :assignee_name => :string
|
327
333
|
belongs_to :task
|
328
334
|
end
|
@@ -361,6 +367,7 @@ The key to how the `destroy` variants work in how the relation is declared. You
|
|
361
367
|
```ruby
|
362
368
|
class Task
|
363
369
|
include MotionModel::Model
|
370
|
+
include MotionModel::ArrayModelAdapter
|
364
371
|
columns :name => :string
|
365
372
|
has_many :assignees
|
366
373
|
end
|
@@ -388,6 +395,43 @@ related to the assignees remains intact.
|
|
388
395
|
|
389
396
|
Note: This syntax is modeled on the Rails `:dependent => :destroy` options in `ActiveRecord`.
|
390
397
|
|
398
|
+
## Hook Methods
|
399
|
+
|
400
|
+
During a save or delete operation, hook methods are called to allow you a chance to modify the
|
401
|
+
object at that point. These hook methods are:
|
402
|
+
|
403
|
+
```ruby
|
404
|
+
before_save(sender)
|
405
|
+
after_save(sender)
|
406
|
+
before_delete(sender)
|
407
|
+
after_delete(sender)
|
408
|
+
```
|
409
|
+
|
410
|
+
MotionModel makes no distinction between destroy and delete when calling hook methods, as it only
|
411
|
+
calls them when the actual object is deleted. In a destroy operation, during the cascading delete,
|
412
|
+
the delete hooks are called (again) at the point of object deletion.
|
413
|
+
|
414
|
+
Note that the method signatures may be different from previous implementations. No longer can you
|
415
|
+
declare a hook method without the `sender` argument.
|
416
|
+
|
417
|
+
Finally, contrasting hook methods with notifications, the hook methods `before_save` and `after_save`
|
418
|
+
are called before the save operation begins and after it completes. However, the notification (covered
|
419
|
+
below) is only issued after the save operation. However... the notification understands whether the
|
420
|
+
operation was a save or update. Rule of thumb: If you want to catch an operation before it begins,
|
421
|
+
use the hook. If you just want to know about it when it happens, use the notification.
|
422
|
+
|
423
|
+
The delete hooks happen around the delete operation and, again, allow you the option to mess with the
|
424
|
+
object before you allow the process to go forward (pretty much, the `before_delete` hook does this).
|
425
|
+
|
426
|
+
*IMPORTANT*: Returning false in a before hook stops the rest of the operation. So, for example, you
|
427
|
+
could prevent the deletion of the last admin by writing something like this:
|
428
|
+
|
429
|
+
```ruby
|
430
|
+
def before_delete(sender)
|
431
|
+
return false if sender.find(:privilege_level).eq('admin').count < 2
|
432
|
+
end
|
433
|
+
```
|
434
|
+
|
391
435
|
## Transactions and Undo/Cancel
|
392
436
|
|
393
437
|
MotionModel is not ActiveRecord. MotionModel is not a database-backed mapper. The bottom line is that when you change a field in a model, even if you don't save it, you are partying on the central object store. In part, this is because Ruby copies objects by reference, so when you do a find, you get a reference to the object *in the central object store*.
|
@@ -460,12 +504,14 @@ end
|
|
460
504
|
```ruby
|
461
505
|
class Task
|
462
506
|
include MotionModel::Model
|
507
|
+
include MotionModel::ArrayModelAdapter
|
463
508
|
has_many :assignees
|
464
509
|
# etc
|
465
510
|
end
|
466
511
|
|
467
512
|
class Assignee
|
468
513
|
include MotionModel::Model
|
514
|
+
include MotionModel::ArrayModelAdapter
|
469
515
|
belongs_to :task
|
470
516
|
# etc
|
471
517
|
end
|
data/Rakefile
CHANGED
@@ -2,10 +2,13 @@
|
|
2
2
|
require "bundler/gem_tasks"
|
3
3
|
$:.unshift("/Library/RubyMotion/lib")
|
4
4
|
require 'motion/project'
|
5
|
+
require 'bundler'
|
6
|
+
Bundler.require
|
5
7
|
|
6
8
|
Motion::Project::App.setup do |app|
|
7
9
|
# Use `rake config' to see complete project settings.
|
10
|
+
app.name = 'MotionModel'
|
8
11
|
app.delegate_class = 'FakeDelegate'
|
9
|
-
app.files = Dir.glob('./lib/motion_model/**/*.rb')
|
12
|
+
app.files = Dir.glob('./lib/motion_model/**/*.rb') + app.files
|
10
13
|
app.files = (app.files + Dir.glob('./app/**/*.rb')).uniq
|
11
14
|
end
|
@@ -7,18 +7,20 @@ module MotionModel
|
|
7
7
|
def self.included(base)
|
8
8
|
base.extend(PrivateClassMethods)
|
9
9
|
base.extend(PublicClassMethods)
|
10
|
-
base.
|
10
|
+
base.instance_eval do
|
11
|
+
_reset_next_id
|
12
|
+
end
|
11
13
|
end
|
12
14
|
|
13
15
|
module PublicClassMethods
|
14
|
-
|
15
|
-
def collection
|
16
|
+
def collection
|
16
17
|
@collection ||= []
|
17
18
|
end
|
18
19
|
|
19
20
|
def insert(object)
|
20
21
|
collection << object
|
21
22
|
end
|
23
|
+
alias :<< :insert
|
22
24
|
|
23
25
|
def length
|
24
26
|
collection.length
|
@@ -32,11 +34,8 @@ module MotionModel
|
|
32
34
|
# Do each delete so any on_delete and
|
33
35
|
# cascades are called, then empty the
|
34
36
|
# collection and compact the array.
|
35
|
-
bulk_update
|
36
|
-
|
37
|
-
end
|
38
|
-
@collection = []
|
39
|
-
@_next_id = 1
|
37
|
+
bulk_update { collection.pop.delete until collection.empty? }
|
38
|
+
_reset_next_id
|
40
39
|
end
|
41
40
|
|
42
41
|
# Finds row(s) within the data store. E.g.,
|
@@ -59,36 +58,40 @@ module MotionModel
|
|
59
58
|
return collection.select{|element| element.id == target_id}.first
|
60
59
|
end
|
61
60
|
|
62
|
-
ArrayFinderQuery.new(args[0].to_sym,
|
61
|
+
ArrayFinderQuery.new(args[0].to_sym, collection)
|
63
62
|
end
|
64
63
|
alias_method :where, :find
|
65
64
|
|
65
|
+
def find_by_id(id)
|
66
|
+
find(:id).eq(id).first
|
67
|
+
end
|
68
|
+
|
66
69
|
# Returns query result as an array
|
67
70
|
def all
|
68
71
|
collection
|
69
72
|
end
|
70
73
|
|
71
74
|
def order(field_name = nil, &block)
|
72
|
-
ArrayFinderQuery.new(
|
75
|
+
ArrayFinderQuery.new(collection).order(field_name, &block)
|
73
76
|
end
|
74
77
|
|
75
78
|
end
|
76
79
|
|
77
80
|
module PrivateClassMethods
|
81
|
+
private
|
78
82
|
|
79
83
|
# Returns next available id
|
80
|
-
def
|
84
|
+
def _next_id #nodoc
|
81
85
|
@_next_id
|
82
86
|
end
|
83
87
|
|
84
|
-
|
85
|
-
|
86
|
-
@_next_id = value
|
88
|
+
def _reset_next_id
|
89
|
+
@_next_id = 1
|
87
90
|
end
|
88
91
|
|
89
92
|
# Increments next available id
|
90
|
-
def
|
91
|
-
@_next_id
|
93
|
+
def increment_next_id(other_id) #nodoc
|
94
|
+
@_next_id = [@_next_id, other_id.to_i].max + 1
|
92
95
|
end
|
93
96
|
|
94
97
|
end
|
@@ -97,6 +100,10 @@ module MotionModel
|
|
97
100
|
assign_id(options)
|
98
101
|
end
|
99
102
|
|
103
|
+
def increment_next_id(other_id)
|
104
|
+
self.class.send(:increment_next_id, other_id)
|
105
|
+
end
|
106
|
+
|
100
107
|
# Undelete does pretty much as its name implies. However,
|
101
108
|
# the natural sort order is not preserved. IMPORTANT: If
|
102
109
|
# you are trying to undo a cascading delete, this will not
|
@@ -104,7 +111,11 @@ module MotionModel
|
|
104
111
|
|
105
112
|
def undelete
|
106
113
|
collection << self
|
107
|
-
|
114
|
+
issue_notification(:action => 'add')
|
115
|
+
end
|
116
|
+
|
117
|
+
def collection #nodoc
|
118
|
+
self.class.collection
|
108
119
|
end
|
109
120
|
|
110
121
|
# This adds to the ArrayStore without the magic date
|
@@ -122,32 +133,45 @@ module MotionModel
|
|
122
133
|
end
|
123
134
|
alias_method :count, :length
|
124
135
|
|
136
|
+
def rebuild_relation_for(name, instance_or_collection) # nodoc
|
137
|
+
end
|
138
|
+
|
125
139
|
private
|
126
140
|
|
141
|
+
def _next_id
|
142
|
+
self.class.send(:_next_id)
|
143
|
+
end
|
144
|
+
|
127
145
|
def assign_id(options) #nodoc
|
128
|
-
|
129
|
-
|
130
|
-
else
|
131
|
-
self.class.next_id = [options[:id].to_i, self.class.next_id].max
|
132
|
-
end
|
133
|
-
self.class.increment_id
|
146
|
+
options[:id] ||= _next_id
|
147
|
+
increment_next_id(options[:id])
|
134
148
|
end
|
135
149
|
|
136
|
-
def
|
137
|
-
|
150
|
+
def relation_for(col) # nodoc
|
151
|
+
col = column_named(col)
|
152
|
+
related_klass = col.classify
|
153
|
+
|
154
|
+
case col.type
|
155
|
+
when :belongs_to
|
156
|
+
related_klass.find(@data[:id])
|
157
|
+
when :has_many
|
158
|
+
related_klass.find(generate_belongs_to_id(self.class)).belongs_to(self, related_klass).eq(@data[:id])
|
159
|
+
else
|
160
|
+
nil
|
161
|
+
end
|
138
162
|
end
|
139
163
|
|
140
|
-
def do_insert
|
164
|
+
def do_insert(options = {})
|
141
165
|
collection << self
|
142
166
|
end
|
143
167
|
|
144
|
-
def do_update
|
168
|
+
def do_update(options = {})
|
145
169
|
end
|
146
170
|
|
147
171
|
def do_delete
|
148
172
|
target_index = collection.index{|item| item.id == self.id}
|
149
173
|
collection.delete_at(target_index) unless target_index.nil?
|
150
|
-
|
174
|
+
issue_notification(:action => 'delete')
|
151
175
|
end
|
152
176
|
|
153
177
|
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
module MotionModel
|
2
|
+
module ArrayModelAdapter
|
3
|
+
class PersistFileError < Exception; end
|
4
|
+
class VersionNumberError < ArgumentError; end
|
5
|
+
|
6
|
+
module PublicClassMethods
|
7
|
+
|
8
|
+
def validate_schema_version(version_number)
|
9
|
+
raise MotionModel::ArrayModelAdapter::VersionNumberError.new('version number must be a string') unless version_number.is_a?(String)
|
10
|
+
if version_number !~ /^[\d.]+$/
|
11
|
+
raise MotionModel::ArrayModelAdapter::VersionNumberError.new('version number string must contain only numbers and periods')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Declare a version number for this schema. For example:
|
16
|
+
#
|
17
|
+
# class Task
|
18
|
+
# include MotionModel::Model
|
19
|
+
# include MotionModel::ArrayModelAdapter
|
20
|
+
#
|
21
|
+
# version_number 1.0.1
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# When a version number mismatch occurs as an individual row is loaded
|
25
|
+
# from persistent storage, the migrate method is invoked, allowing
|
26
|
+
# you to programmatically migrate on a per-row basis.
|
27
|
+
|
28
|
+
def schema_version(*version_number)
|
29
|
+
if version_number.empty?
|
30
|
+
return @schema_version
|
31
|
+
else
|
32
|
+
validate_schema_version(version_number[0])
|
33
|
+
@schema_version = version_number[0]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def migrate
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
# Returns the unarchived object if successful, otherwise false
|
42
|
+
#
|
43
|
+
# Note that subsequent calls to serialize/deserialize methods
|
44
|
+
# will remember the file name, so they may omit that argument.
|
45
|
+
#
|
46
|
+
# Raises a +MotionModel::PersistFileFailureError+ on failure.
|
47
|
+
def deserialize_from_file(file_name = nil)
|
48
|
+
if schema_version != '1.0.0'
|
49
|
+
migrate
|
50
|
+
end
|
51
|
+
|
52
|
+
@file_name = file_name if file_name
|
53
|
+
|
54
|
+
if File.exist? documents_file(@file_name)
|
55
|
+
error_ptr = Pointer.new(:object)
|
56
|
+
|
57
|
+
data = NSData.dataWithContentsOfFile(documents_file(@file_name), options:NSDataReadingMappedIfSafe, error:error_ptr)
|
58
|
+
|
59
|
+
if data.nil?
|
60
|
+
error = error_ptr[0]
|
61
|
+
raise MotionModel::PersistFileError.new "Error when reading the data: #{error}"
|
62
|
+
else
|
63
|
+
bulk_update do
|
64
|
+
NSKeyedUnarchiver.unarchiveObjectWithData(data)
|
65
|
+
end
|
66
|
+
return self
|
67
|
+
end
|
68
|
+
else
|
69
|
+
return false
|
70
|
+
end
|
71
|
+
end
|
72
|
+
# Serializes data to a persistent store (file, in this
|
73
|
+
# terminology). Serialization is synchronous, so this
|
74
|
+
# will pause your run loop until complete.
|
75
|
+
#
|
76
|
+
# +file_name+ is the name of the persistent store you
|
77
|
+
# want to use. If you omit this, it will use the last
|
78
|
+
# remembered file name.
|
79
|
+
#
|
80
|
+
# Raises a +MotionModel::PersistFileError+ on failure.
|
81
|
+
def serialize_to_file(file_name = nil)
|
82
|
+
@file_name = file_name if file_name
|
83
|
+
error_ptr = Pointer.new(:object)
|
84
|
+
|
85
|
+
data = NSKeyedArchiver.archivedDataWithRootObject collection
|
86
|
+
unless data.writeToFile(documents_file(@file_name), options: NSDataWritingAtomic, error: error_ptr)
|
87
|
+
# De-reference the pointer.
|
88
|
+
error = error_ptr[0]
|
89
|
+
|
90
|
+
# Now we can use the `error' object.
|
91
|
+
raise MotionModel::PersistFileError.new "Error when writing data: #{error}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
def documents_file(file_name)
|
97
|
+
file_path = File.join NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true), file_name
|
98
|
+
file_path
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def initWithCoder(coder)
|
103
|
+
self.init
|
104
|
+
|
105
|
+
new_tag_id = 1
|
106
|
+
columns.each do |attr|
|
107
|
+
next if has_relation?(attr)
|
108
|
+
# If a model revision has taken place, don't try to decode
|
109
|
+
# something that's not there.
|
110
|
+
if coder.containsValueForKey(attr.to_s)
|
111
|
+
value = coder.decodeObjectForKey(attr.to_s)
|
112
|
+
self.send("#{attr}=", value)
|
113
|
+
else
|
114
|
+
self.send("#{attr}=", nil)
|
115
|
+
end
|
116
|
+
|
117
|
+
# re-issue tags to make sure they are unique
|
118
|
+
@tag = new_tag_id
|
119
|
+
new_tag_id += 1
|
120
|
+
end
|
121
|
+
add_to_store
|
122
|
+
|
123
|
+
self
|
124
|
+
end
|
125
|
+
|
126
|
+
# Follow Apple's recommendation not to encode missing
|
127
|
+
# values.
|
128
|
+
def encodeWithCoder(coder)
|
129
|
+
columns.each do |attr|
|
130
|
+
# Serialize attributes except the proxy has_many and belongs_to ones.
|
131
|
+
unless [:belongs_to, :has_many].include? column_named(attr).type
|
132
|
+
value = self.send(attr)
|
133
|
+
unless value.nil?
|
134
|
+
coder.encodeObject(value, forKey: attr.to_s)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
end
|
data/lib/motion_model/ext.rb
CHANGED
@@ -351,3 +351,20 @@ def UIInterfaceOrientationIsPortrait(orientation)
|
|
351
351
|
orientation == UIInterfaceOrientationPortrait ||
|
352
352
|
orientation == UIInterfaceOrientationPortraitUpsideDown
|
353
353
|
end
|
354
|
+
|
355
|
+
class Module
|
356
|
+
# Retrieve a constant within its scope
|
357
|
+
def deep_const_get(const)
|
358
|
+
if Symbol === const
|
359
|
+
const = const.to_s
|
360
|
+
else
|
361
|
+
const = const.to_str.dup
|
362
|
+
end
|
363
|
+
if const.sub!(/^::/, '')
|
364
|
+
base = Object
|
365
|
+
else
|
366
|
+
base = self
|
367
|
+
end
|
368
|
+
const.split(/::/).inject(base) { |mod, name| mod.const_get(name) }
|
369
|
+
end
|
370
|
+
end
|
@@ -161,9 +161,14 @@ module MotionModel
|
|
161
161
|
|
162
162
|
# returns all elements that match as an array.
|
163
163
|
def all
|
164
|
-
|
164
|
+
to_a
|
165
165
|
end
|
166
166
|
|
167
|
+
# returns all elements that match as an array.
|
168
|
+
def to_a
|
169
|
+
@collection
|
170
|
+
end
|
171
|
+
|
167
172
|
# each is a shortcut method to turn a query into an iterator. It allows
|
168
173
|
# you to write code like:
|
169
174
|
#
|
@@ -4,14 +4,14 @@ module MotionModel
|
|
4
4
|
attr_accessor :name
|
5
5
|
attr_accessor :type
|
6
6
|
attr_accessor :default
|
7
|
-
attr_accessor :
|
7
|
+
attr_accessor :dependent
|
8
8
|
|
9
9
|
def initialize(name = nil, type = nil, options = {})
|
10
10
|
@name = name
|
11
11
|
@type = type
|
12
12
|
raise RuntimeError.new "columns need a type declared." if type.nil?
|
13
13
|
@default = options.delete :default
|
14
|
-
@
|
14
|
+
@dependent = options.delete :dependent
|
15
15
|
@options = options
|
16
16
|
end
|
17
17
|
|
@@ -19,16 +19,32 @@ module MotionModel
|
|
19
19
|
@options
|
20
20
|
end
|
21
21
|
|
22
|
+
def class_name
|
23
|
+
@options[:joined_class_name] || @name
|
24
|
+
end
|
25
|
+
|
22
26
|
def classify
|
23
|
-
|
24
|
-
|
25
|
-
@klass ||= Object.const_get(@name.to_s.camelize)
|
26
|
-
when :has_many
|
27
|
-
@klass ||= Object.const_get(@name.to_s.singularize.camelize)
|
27
|
+
if @options[:class]
|
28
|
+
@options[:class]
|
28
29
|
else
|
29
|
-
|
30
|
+
case @type
|
31
|
+
when :belongs_to
|
32
|
+
@klass ||= Object.const_get(class_name.to_s.camelize)
|
33
|
+
when :has_many, :has_one
|
34
|
+
@klass ||= Object.const_get(class_name.to_s.singularize.camelize)
|
35
|
+
else
|
36
|
+
raise "#{@name} is not a relation. This isn't supposed to happen."
|
37
|
+
end
|
30
38
|
end
|
31
39
|
end
|
40
|
+
|
41
|
+
def class_const_get
|
42
|
+
Kernel::const_get(classify)
|
43
|
+
end
|
44
|
+
|
45
|
+
def through_class
|
46
|
+
Kernel::const_get(@options[:through].to_s.classify)
|
47
|
+
end
|
32
48
|
end
|
33
49
|
end
|
34
50
|
end
|
@@ -16,7 +16,7 @@ module MotionModel
|
|
16
16
|
def should_return(column) #nodoc
|
17
17
|
skippable = [:id]
|
18
18
|
skippable += [:created_at, :updated_at] unless @expose_auto_date_fields
|
19
|
-
!skippable.include?(column) && !
|
19
|
+
!skippable.include?(column) && !relation_column?(column)
|
20
20
|
end
|
21
21
|
|
22
22
|
def returnable_columns #nodoc
|
@@ -26,14 +26,14 @@ module MotionModel
|
|
26
26
|
def default_hash_for(column, value)
|
27
27
|
{:key => column.to_sym,
|
28
28
|
:title => column.to_s.humanize,
|
29
|
-
:type => FORMOTION_MAP[
|
29
|
+
:type => FORMOTION_MAP[column_type(column)],
|
30
30
|
:placeholder => column.to_s.humanize,
|
31
31
|
:value => value
|
32
32
|
}
|
33
33
|
end
|
34
34
|
|
35
35
|
def is_date_time?(column)
|
36
|
-
column_type =
|
36
|
+
column_type = column_type(column)
|
37
37
|
[:date, :time].include?(column_type)
|
38
38
|
end
|
39
39
|
|
@@ -78,7 +78,7 @@ module MotionModel
|
|
78
78
|
# you say so, offering you the opportunity to validate your form data.
|
79
79
|
def from_formotion!(data)
|
80
80
|
self.returnable_columns.each{|column|
|
81
|
-
if data[column] &&
|
81
|
+
if data[column] && column_type(column) == :date || column_type(column) == :time
|
82
82
|
data[column] = Time.at(data[column]) unless data[column].nil?
|
83
83
|
end
|
84
84
|
value = self.send("#{column}=", data[column])
|