motion-loco 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|