motion-loco 0.1.3 → 0.2.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.
- checksums.yaml +8 -8
- data/README.md +55 -27
- data/lib/motion-loco.rb +5 -1
- data/lib/motion-loco/adapter.rb +168 -4
- data/lib/motion-loco/fixture_adapter.rb +9 -8
- data/lib/motion-loco/model.rb +172 -3
- data/lib/motion-loco/observable.rb +57 -17
- data/lib/motion-loco/record_array.rb +0 -6
- data/lib/motion-loco/resizable.rb +8 -8
- data/lib/motion-loco/rest_adapter.rb +27 -9
- data/lib/motion-loco/sqlite_adapter.rb +235 -0
- data/lib/motion-loco/table_view.rb +83 -79
- data/lib/motion-loco/version.rb +1 -1
- data/lib/motion-loco/views.rb +51 -47
- metadata +3 -3
- data/lib/motion-loco/savable.rb +0 -127
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NzdjMjhmYjA5ZTgzNjEzMTg2ZTQ1ZjcxZjYyOGEwMWRjM2I3NWI2MA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NTVkY2JlMmE0MzE1NTI0OTk2YmI5YjJiZmJiNmRlZGZlYmEyZDI3Mg==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MzQ5YTcyNjAxODRlYTU5NGVlMmE1YWU2OTc5MTcxYzVjZmVhNzkxNTk2NmQ3
|
10
|
+
ZDQxMzQ3ZTJlNzczYjYxYWI2OTQ2NTlmY2JlZGUzNzI4ODNiNTI2YjMxNTll
|
11
|
+
ZGZhNDI5MGFhZGM2MzM3ZTYwNWU4Y2NlMDYyODlmYTk2NGQxYTg=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MzlhNDI5ODUzODczZDZlNzgzMDIxOGNkYjkyMGU5YTZiY2M3YWM2NDg3YTNm
|
14
|
+
YWZiZjE2NzE3NWNiZTExNTZkYTAwOGYzY2NmZTlmYTY3MmUxOWVkZjU4NTVk
|
15
|
+
NTVlYzlkMWRkOTRmNWFhNWQxMTU4ZDZjMzQwY2JkODY0NmZhNTQ=
|
data/README.md
CHANGED
@@ -2,20 +2,19 @@
|
|
2
2
|
|
3
3
|
Motion-Loco is a library for [RubyMotion](http://rubymotion.com)
|
4
4
|
that includes [Ember.js](http://emberjs.com) inspired bindings,
|
5
|
-
computed properties, and observers.
|
5
|
+
computed properties, and observers. **And Ember-Data inspired [data](#locofixtureadapter) [adapters](#locorestadapter)!**
|
6
6
|
|
7
|
-
|
7
|
+
## What's New!
|
8
8
|
|
9
|
-
|
10
|
-
but I feel like it needs some more features to really be useful.**
|
9
|
+
### June 23th, 2013
|
11
10
|
|
12
|
-
|
11
|
+
#### SQLiteAdapter and Relationships!
|
13
12
|
|
14
|
-
|
13
|
+
I need to write up some better documentation, but be sure to check out the [Loco::SQLiteAdapter](locosqliteadapter) and the new `has_many` and `belongs_to` relationships in `Loco::Model`.
|
15
14
|
|
16
|
-
|
15
|
+
The relationship stuff needs some major code clean up, so don't copy my code if you're doing anything similar for loading/saving relationship data. :)
|
17
16
|
|
18
|
-
|
17
|
+
The [tests](https://github.com/brianpattison/motion-loco/tree/master/spec) all pass though... so that's something, right?
|
19
18
|
|
20
19
|
## Installation
|
21
20
|
|
@@ -40,8 +39,8 @@ They beat out using a method because they can be observed like any other propert
|
|
40
39
|
|
41
40
|
```ruby
|
42
41
|
class Person < Loco::Model
|
43
|
-
property :first_name
|
44
|
-
property :last_name
|
42
|
+
property :first_name, :string
|
43
|
+
property :last_name, :string
|
45
44
|
|
46
45
|
# Computed property for full name that watches for changes
|
47
46
|
# in the object's first_name and last_name properties.
|
@@ -64,8 +63,8 @@ Bindings are used to link the property of an object to a property of another obj
|
|
64
63
|
|
65
64
|
```ruby
|
66
65
|
class Person < Loco::Model
|
67
|
-
property :first_name
|
68
|
-
property :last_name
|
66
|
+
property :first_name, :string
|
67
|
+
property :last_name, :string
|
69
68
|
property :full_name, lambda{|object|
|
70
69
|
"#{object.first_name} #{object.last_name}"
|
71
70
|
}.property(:first_name, :last_name)
|
@@ -76,7 +75,7 @@ end
|
|
76
75
|
last_name: 'Pattison'
|
77
76
|
)
|
78
77
|
|
79
|
-
@label = Loco::Label.alloc.initWithFrame(
|
78
|
+
@label = Loco::UI::Label.alloc.initWithFrame(
|
80
79
|
textBinding: [@person, 'full_name'],
|
81
80
|
height: 30,
|
82
81
|
top: 20,
|
@@ -96,7 +95,7 @@ class PersonController < Loco::Controller
|
|
96
95
|
property :content
|
97
96
|
end
|
98
97
|
|
99
|
-
@label = Loco::Label.alloc.initWithFrame(
|
98
|
+
@label = Loco::UI::Label.alloc.initWithFrame(
|
100
99
|
textBinding: 'PersonController.content.full_name',
|
101
100
|
height: 30,
|
102
101
|
top: 20,
|
@@ -113,18 +112,18 @@ PersonController.content = @person
|
|
113
112
|
@label.text # "Brian Pattison"
|
114
113
|
```
|
115
114
|
|
116
|
-
### Loco::TableView
|
115
|
+
### Loco::UI::TableView
|
117
116
|
|
118
|
-
A `Loco::TableView` is used for to easily bind a collection of objects
|
117
|
+
A `Loco::UI::TableView` is used for to easily bind a collection of objects
|
119
118
|
to a `UITableView` and each item in the collection to a reusable `UITableViewCell`.
|
120
119
|
|
121
120
|
```ruby
|
122
|
-
class MyTableViewCell < Loco::TableViewCell
|
121
|
+
class MyTableViewCell < Loco::UI::TableViewCell
|
123
122
|
# The `view_setup` method is called the first time the cell is created.
|
124
123
|
# Bindings can be made to the item assigned to the cell
|
125
124
|
# by binding to `parentView.content`.
|
126
125
|
def view_setup
|
127
|
-
@label = Loco::Label.alloc.initWithFrame(
|
126
|
+
@label = Loco::UI::Label.alloc.initWithFrame(
|
128
127
|
textBinding: 'parentView.content.first_name',
|
129
128
|
height: 30,
|
130
129
|
left: 60,
|
@@ -135,7 +134,7 @@ class MyTableViewCell < Loco::TableViewCell
|
|
135
134
|
end
|
136
135
|
end
|
137
136
|
|
138
|
-
class MyTableView < Loco::TableView
|
137
|
+
class MyTableView < Loco::UI::TableView
|
139
138
|
item_view_class MyTableViewCell
|
140
139
|
end
|
141
140
|
|
@@ -153,7 +152,7 @@ end
|
|
153
152
|
```ruby
|
154
153
|
class Show < Loco::Model
|
155
154
|
adapter 'Loco::FixtureAdapter'
|
156
|
-
property :title
|
155
|
+
property :title, :string
|
157
156
|
end
|
158
157
|
|
159
158
|
@show = Show.find(2) # Loads from `resources/fixtures/plural_class_name.json`
|
@@ -165,8 +164,8 @@ end
|
|
165
164
|
```ruby
|
166
165
|
class Post < Loco::Model
|
167
166
|
adapter 'Loco::RESTAdapter', 'http://localhost:3000'
|
168
|
-
property :title
|
169
|
-
property :body
|
167
|
+
property :title, :string
|
168
|
+
property :body, :string
|
170
169
|
end
|
171
170
|
|
172
171
|
# GET http://localhost:3000/posts/1.json
|
@@ -211,13 +210,42 @@ end
|
|
211
210
|
end
|
212
211
|
```
|
213
212
|
|
214
|
-
|
213
|
+
### Loco::SQLiteAdapter
|
214
|
+
```ruby
|
215
|
+
class Player < Loco::Model
|
216
|
+
adapter 'Loco::SQLiteAdapter'
|
217
|
+
property :name, :string
|
218
|
+
has_many :scores
|
219
|
+
end
|
220
|
+
|
221
|
+
class Score < Loco::Model
|
222
|
+
adapter 'Loco::SQLiteAdapter'
|
223
|
+
property :rank, :integer
|
224
|
+
property :value, :integer
|
225
|
+
belongs_to :player
|
226
|
+
end
|
227
|
+
|
228
|
+
@player = Player.new(name: 'Kirsten Pattison')
|
229
|
+
@player.save
|
230
|
+
|
231
|
+
@score = Score.new(rank: 1, value: 50000, player: @player)
|
232
|
+
@score.save
|
233
|
+
|
234
|
+
@player.scores.length # 1
|
235
|
+
@score.player.name # Kirsten Pattison
|
236
|
+
```
|
237
|
+
|
238
|
+
## TODO
|
215
239
|
|
240
|
+
- RESTAdapter
|
241
|
+
- Sideload belongs_to/has_many
|
242
|
+
- SQLiteAdapter
|
243
|
+
- Data migrations?
|
216
244
|
- State Manager
|
217
|
-
-
|
218
|
-
-
|
219
|
-
-
|
220
|
-
-
|
245
|
+
- Pretty big challenge, so it might be a while
|
246
|
+
- Limit transitions between states
|
247
|
+
- Rollback dirty/unsaved records
|
248
|
+
- Improve everything! :)
|
221
249
|
|
222
250
|
## Contributing
|
223
251
|
|
data/lib/motion-loco.rb
CHANGED
@@ -8,4 +8,8 @@ require 'bubble-wrap/http'
|
|
8
8
|
require 'motion-require'
|
9
9
|
require 'motion-support/inflector'
|
10
10
|
|
11
|
-
Motion::Require.all(Dir.glob(File.expand_path('../motion-loco/**/*.rb', __FILE__)))
|
11
|
+
Motion::Require.all(Dir.glob(File.expand_path('../motion-loco/**/*.rb', __FILE__)))
|
12
|
+
|
13
|
+
Motion::Project::App.setup do |app|
|
14
|
+
app.frameworks += ['CoreData']
|
15
|
+
end
|
data/lib/motion-loco/adapter.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
motion_require 'convenience_methods'
|
2
|
+
|
1
3
|
module Loco
|
2
4
|
|
3
5
|
class Adapter
|
@@ -6,6 +8,10 @@ module Loco
|
|
6
8
|
raise NoMethodError, "Loco::Adapter subclasses must implement #create_record(record, &block)."
|
7
9
|
end
|
8
10
|
|
11
|
+
def delete_record(record, &block)
|
12
|
+
raise NoMethodError, "Loco::Adapter subclasses must implement #delete_record(record, &block)."
|
13
|
+
end
|
14
|
+
|
9
15
|
def find(record, id, &block)
|
10
16
|
raise NoMethodError, "Loco::Adapter subclasses must implement #find(record, id, &block)."
|
11
17
|
end
|
@@ -22,14 +28,172 @@ module Loco
|
|
22
28
|
raise NoMethodError, "Loco::Adapter subclasses must implement #find_query(type, records, params, &block)."
|
23
29
|
end
|
24
30
|
|
25
|
-
def
|
26
|
-
raise NoMethodError, "Loco::Adapter subclasses must implement #
|
31
|
+
def update_record(record, &block)
|
32
|
+
raise NoMethodError, "Loco::Adapter subclasses must implement #update_record(record, &block)."
|
27
33
|
end
|
28
34
|
|
29
|
-
def
|
30
|
-
|
35
|
+
def serialize(record, options={}, json={})
|
36
|
+
properties = record.class.get_class_properties.select{|prop|
|
37
|
+
if prop[:type]
|
38
|
+
if prop[:name] == :id
|
39
|
+
options[:include_id] || options[:includeId]
|
40
|
+
else
|
41
|
+
true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
}
|
45
|
+
|
46
|
+
transforms = self.class.get_transforms
|
47
|
+
|
48
|
+
properties.each do |property|
|
49
|
+
key = property[:name].to_sym
|
50
|
+
transform = transforms[property[:type]]
|
51
|
+
if transform
|
52
|
+
json[key] = transform[:serialize].call(record.valueForKey(key))
|
53
|
+
else
|
54
|
+
json[key] = record.valueForKey(key)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
if options[:root] != false
|
59
|
+
if options[:root].nil? || options[:root] == true
|
60
|
+
root = record.class.to_s.underscore.to_sym
|
61
|
+
else
|
62
|
+
root = options[:root].to_sym
|
63
|
+
end
|
64
|
+
temp = {}
|
65
|
+
temp[root] = json
|
66
|
+
json = temp
|
67
|
+
end
|
68
|
+
json
|
69
|
+
end
|
70
|
+
|
71
|
+
class << self
|
72
|
+
|
73
|
+
def get_transforms
|
74
|
+
if @transforms.nil?
|
75
|
+
@transforms = {}
|
76
|
+
if self.superclass.respond_to? :get_transforms
|
77
|
+
@transforms.merge!(self.superclass.get_transforms)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
@transforms
|
81
|
+
end
|
82
|
+
|
83
|
+
def register_transform(name, transforms={})
|
84
|
+
@transforms = get_transforms
|
85
|
+
@transforms[name.to_sym] = transforms
|
86
|
+
end
|
87
|
+
alias_method :registerTransform, :register_transform
|
88
|
+
|
31
89
|
end
|
32
90
|
|
91
|
+
private
|
92
|
+
|
93
|
+
def load(type, records, data)
|
94
|
+
if records.is_a? Array
|
95
|
+
if data.is_a? Hash
|
96
|
+
data = data[type.to_s.underscore.pluralize]
|
97
|
+
end
|
98
|
+
records.load(type, transform_data(type, data))
|
99
|
+
else
|
100
|
+
if data.is_a?(Hash) && data.has_key?(type.to_s.underscore)
|
101
|
+
data = data[type.to_s.underscore]
|
102
|
+
end
|
103
|
+
records.load(data.valueForKey(:id), transform_data(type, data))
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def transform_data_item(type, data)
|
108
|
+
json = {}
|
109
|
+
transforms = self.class.get_transforms
|
110
|
+
|
111
|
+
type.get_class_properties.each do |property|
|
112
|
+
key = property[:name].to_sym
|
113
|
+
transform = transforms[property[:type]]
|
114
|
+
if transform
|
115
|
+
json[key] = transform[:deserialize].call(data.valueForKey(key))
|
116
|
+
else
|
117
|
+
json[key] = data.valueForKey(key)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
type.get_class_relationships.each do |relationship|
|
122
|
+
if relationship[:belongs_to]
|
123
|
+
key = "#{relationship[:belongs_to]}_id".to_sym
|
124
|
+
elsif relationship[:has_many]
|
125
|
+
key = "#{relationship[:has_many].to_s.singularize}_ids".to_sym
|
126
|
+
end
|
127
|
+
json[key] = data.valueForKey(key)
|
128
|
+
end
|
129
|
+
|
130
|
+
json
|
131
|
+
end
|
132
|
+
|
133
|
+
def transform_data(type, data)
|
134
|
+
if data.is_a? Array
|
135
|
+
json = []
|
136
|
+
data.each do |data_item|
|
137
|
+
json << transform_data_item(type, data_item)
|
138
|
+
end
|
139
|
+
json
|
140
|
+
else
|
141
|
+
transform_data_item(type, data)
|
142
|
+
end
|
143
|
+
end
|
33
144
|
end
|
34
145
|
|
146
|
+
Adapter.register_transform(:date, {
|
147
|
+
serialize: lambda{|value|
|
148
|
+
dateFormatter = NSDateFormatter.alloc.init
|
149
|
+
dateFormatter.setDateFormat('yyyy-MM-dd')
|
150
|
+
value = dateFormatter.stringFromDate(value)
|
151
|
+
},
|
152
|
+
deserialize: lambda{|value|
|
153
|
+
if value.is_a? NSDate
|
154
|
+
value
|
155
|
+
else
|
156
|
+
dateFormatter = NSDateFormatter.alloc.init
|
157
|
+
dateFormatter.setDateFormat('yyyy-MM-dd')
|
158
|
+
dateFormatter.dateFromString(value.to_s)
|
159
|
+
end
|
160
|
+
}
|
161
|
+
})
|
162
|
+
|
163
|
+
Adapter.register_transform(:array, {
|
164
|
+
serialize: lambda{|value|
|
165
|
+
value.to_a
|
166
|
+
},
|
167
|
+
deserialize: lambda{|value|
|
168
|
+
value.to_a
|
169
|
+
}
|
170
|
+
})
|
171
|
+
|
172
|
+
Adapter.register_transform(:integer, {
|
173
|
+
serialize: lambda{|value|
|
174
|
+
value.to_i
|
175
|
+
},
|
176
|
+
deserialize: lambda{|value|
|
177
|
+
value.to_i
|
178
|
+
}
|
179
|
+
})
|
180
|
+
|
181
|
+
Adapter.register_transform(:float, {
|
182
|
+
serialize: lambda{|value|
|
183
|
+
value.to_f
|
184
|
+
},
|
185
|
+
deserialize: lambda{|value|
|
186
|
+
value.to_f
|
187
|
+
}
|
188
|
+
})
|
189
|
+
|
190
|
+
Adapter.register_transform(:string, {
|
191
|
+
serialize: lambda{|value|
|
192
|
+
value.to_s
|
193
|
+
},
|
194
|
+
deserialize: lambda{|value|
|
195
|
+
value.to_s
|
196
|
+
}
|
197
|
+
})
|
198
|
+
|
35
199
|
end
|
@@ -11,15 +11,16 @@ module Loco
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def find(record, id, &block)
|
14
|
+
type = record.class
|
14
15
|
error = Pointer.new(:id)
|
15
|
-
file = File.read(File.join(NSBundle.mainBundle.resourcePath, "fixtures", "#{
|
16
|
+
file = File.read(File.join(NSBundle.mainBundle.resourcePath, "fixtures", "#{type.to_s.underscore.pluralize}.json"))
|
16
17
|
data = NSJSONSerialization.JSONObjectWithData(file.to_data, options:JSON_OPTIONS, error:error).find{|obj| obj[:id] == id }
|
17
18
|
if data
|
18
|
-
|
19
|
+
load(type, record, data)
|
19
20
|
block.call(record) if block.is_a? Proc
|
20
21
|
record
|
21
22
|
else
|
22
|
-
raise Loco::FixtureAdapter::RecordNotFound, "#{
|
23
|
+
raise Loco::FixtureAdapter::RecordNotFound, "#{type} with the id `#{id}' could not be loaded."
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
@@ -27,7 +28,7 @@ module Loco
|
|
27
28
|
error = Pointer.new(:id)
|
28
29
|
file = File.read(File.join(NSBundle.mainBundle.resourcePath, "fixtures", "#{type.to_s.underscore.pluralize}.json"))
|
29
30
|
data = NSJSONSerialization.JSONObjectWithData(file.to_data, options:JSON_OPTIONS, error:error)
|
30
|
-
|
31
|
+
load(type, records, data)
|
31
32
|
block.call(records) if block.is_a? Proc
|
32
33
|
records
|
33
34
|
end
|
@@ -38,7 +39,7 @@ module Loco
|
|
38
39
|
data = NSJSONSerialization.JSONObjectWithData(file.to_data, options:JSON_OPTIONS, error:error).select{|obj|
|
39
40
|
ids.map(&:to_s).include?(obj[:id].to_s)
|
40
41
|
}
|
41
|
-
|
42
|
+
load(type, records, data)
|
42
43
|
block.call(records) if block.is_a? Proc
|
43
44
|
records
|
44
45
|
end
|
@@ -53,13 +54,13 @@ module Loco
|
|
53
54
|
end
|
54
55
|
match
|
55
56
|
}
|
56
|
-
|
57
|
+
load(type, records, data)
|
57
58
|
block.call(records) if block.is_a? Proc
|
58
59
|
records
|
59
60
|
end
|
60
61
|
|
61
|
-
def
|
62
|
-
raise NoMethodError, "Loco::FixtureAdapter cannot
|
62
|
+
def update_record(record, &block)
|
63
|
+
raise NoMethodError, "Loco::FixtureAdapter cannot update records."
|
63
64
|
end
|
64
65
|
|
65
66
|
def delete_record(record, &block)
|
data/lib/motion-loco/model.rb
CHANGED
@@ -1,12 +1,181 @@
|
|
1
1
|
motion_require 'observable'
|
2
|
-
motion_require '
|
2
|
+
motion_require 'record_array'
|
3
3
|
|
4
4
|
module Loco
|
5
5
|
|
6
6
|
class Model
|
7
7
|
include Observable
|
8
|
-
|
9
|
-
|
8
|
+
property :id, :integer
|
9
|
+
|
10
|
+
def destroy(&block)
|
11
|
+
adapter = self.class.get_class_adapter
|
12
|
+
unless self.new?
|
13
|
+
adapter.delete_record(self) do |record|
|
14
|
+
block.call(record) if block.is_a? Proc
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def load(id, data)
|
20
|
+
data.merge!({ id: id })
|
21
|
+
self.set_properties(data)
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def new?
|
26
|
+
self.id.nil?
|
27
|
+
end
|
28
|
+
|
29
|
+
def save(&block)
|
30
|
+
adapter = self.class.get_class_adapter
|
31
|
+
if self.new?
|
32
|
+
adapter.create_record(self) do |record|
|
33
|
+
block.call(record) if block.is_a? Proc
|
34
|
+
end
|
35
|
+
else
|
36
|
+
adapter.update_record(self) do |record|
|
37
|
+
block.call(record) if block.is_a? Proc
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def serialize(options={})
|
43
|
+
self.class.get_class_adapter.serialize(self, options)
|
44
|
+
end
|
45
|
+
|
46
|
+
class << self
|
47
|
+
|
48
|
+
def adapter(adapter_class, *args)
|
49
|
+
if adapter_class.is_a? String
|
50
|
+
@adapter = adapter_class.constantize.new(*args)
|
51
|
+
else
|
52
|
+
@adapter = adapter_class.new(*args)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def belongs_to(model)
|
57
|
+
attr_accessor model
|
58
|
+
attr_accessor "#{model}_id"
|
59
|
+
|
60
|
+
belongs_to_class_name = model.to_s.classify
|
61
|
+
|
62
|
+
define_method "#{model}" do |&block|
|
63
|
+
record = instance_variable_get("@#{model}")
|
64
|
+
if record
|
65
|
+
block.call(record) if block.is_a? Proc
|
66
|
+
record
|
67
|
+
else
|
68
|
+
belongs_to_id = self.send("#{model}_id")
|
69
|
+
if belongs_to_id
|
70
|
+
belongs_to_class_name.constantize.find(belongs_to_id) do |record|
|
71
|
+
instance_variable_set("@#{model}", record)
|
72
|
+
block.call(record) if block.is_a? Proc
|
73
|
+
end
|
74
|
+
else
|
75
|
+
block.call(record) if block.is_a? Proc
|
76
|
+
record
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
define_method "#{model}=" do |record|
|
82
|
+
raise TypeError, "Expecting a #{belongs_to_class_name} as defined by #belongs_to :#{model}" unless record.is_a? belongs_to_class_name.constantize
|
83
|
+
self.send("#{model}_id=", (record.nil? ? nil : record.id))
|
84
|
+
instance_variable_set("@#{model}", record)
|
85
|
+
record
|
86
|
+
end
|
87
|
+
|
88
|
+
relationships = get_class_relationships
|
89
|
+
relationships << { belongs_to: model }
|
90
|
+
end
|
91
|
+
|
92
|
+
def has_many(model)
|
93
|
+
attr_accessor model
|
94
|
+
attr_accessor "#{model.to_s.singularize}_ids"
|
95
|
+
|
96
|
+
has_many_class_name = model.to_s.singularize.classify
|
97
|
+
|
98
|
+
define_method "#{model}" do |&block|
|
99
|
+
has_many_class = has_many_class_name.constantize
|
100
|
+
records = instance_variable_get("@#{model}")
|
101
|
+
if records
|
102
|
+
block.call(records) if block.is_a? Proc
|
103
|
+
records
|
104
|
+
else
|
105
|
+
has_many_ids = self.send("#{model.to_s.singularize}_ids")
|
106
|
+
if has_many_ids
|
107
|
+
has_many_class.find(has_many_ids) do |records|
|
108
|
+
block.call(records) if block.is_a? Proc
|
109
|
+
end
|
110
|
+
else
|
111
|
+
query = {}
|
112
|
+
query["#{self.class.to_s.underscore.singularize}_id"] = self.id
|
113
|
+
has_many_class.find(query) do |records|
|
114
|
+
block.call(records) if block.is_a? Proc
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
define_method "#{model}=" do |records|
|
121
|
+
has_many_class = has_many_class_name.constantize
|
122
|
+
if (records.is_a?(RecordArray) || records.is_a?(Array)) && (records.length == 0 || (records.length > 0 && records.first.class == has_many_class))
|
123
|
+
unless records.is_a?(RecordArray)
|
124
|
+
record_array = RecordArray.new
|
125
|
+
record_array.addObjectsFromArray(records)
|
126
|
+
records = record_array
|
127
|
+
end
|
128
|
+
else
|
129
|
+
raise TypeError, "Expecting a Loco::RecordArray of #{has_many_class_name} objects as defined by #has_many :#{model}"
|
130
|
+
end
|
131
|
+
|
132
|
+
self.send("#{model.to_s.singularize}_ids=", records.map(&:id))
|
133
|
+
instance_variable_set("@#{model}", records)
|
134
|
+
end
|
135
|
+
|
136
|
+
relationships = get_class_relationships
|
137
|
+
relationships << { has_many: model }
|
138
|
+
end
|
139
|
+
|
140
|
+
def find(id=nil, &block)
|
141
|
+
adapter = self.get_class_adapter
|
142
|
+
if id.nil?
|
143
|
+
# Return all records
|
144
|
+
records = RecordArray.new
|
145
|
+
adapter.find_all(self, records) do |records|
|
146
|
+
block.call(records) if block.is_a? Proc
|
147
|
+
end
|
148
|
+
elsif id.is_a? Array
|
149
|
+
# Return records with given ids
|
150
|
+
records = RecordArray.new
|
151
|
+
id.each do |i|
|
152
|
+
records << self.new(id: i)
|
153
|
+
end
|
154
|
+
adapter.find_many(self, records, id) do |records|
|
155
|
+
block.call(records) if block.is_a? Proc
|
156
|
+
end
|
157
|
+
elsif id.is_a? Hash
|
158
|
+
# Return records matching query
|
159
|
+
records = RecordArray.new
|
160
|
+
adapter.find_query(self, records, id) do |records|
|
161
|
+
block.call(records) if block.is_a? Proc
|
162
|
+
end
|
163
|
+
else
|
164
|
+
record = self.new(id: id)
|
165
|
+
adapter.find(record, id) do |record|
|
166
|
+
block.call(record) if block.is_a? Proc
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
alias_method :all, :find
|
171
|
+
alias_method :where, :find
|
172
|
+
|
173
|
+
def get_class_adapter
|
174
|
+
@adapter ||= Adapter.new
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
10
179
|
end
|
11
180
|
|
12
181
|
end
|