her 0.2.5 → 0.2.6
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/.rspec +2 -0
- data/README.md +83 -37
- data/Rakefile +0 -1
- data/UPGRADE.md +13 -13
- data/examples/twitter-oauth/app.rb +6 -5
- data/examples/twitter-search/app.rb +10 -8
- data/her.gemspec +13 -13
- data/lib/her.rb +7 -4
- data/lib/her/api.rb +20 -12
- data/lib/her/collection.rb +12 -0
- data/lib/her/middleware.rb +4 -3
- data/lib/her/middleware/accept_json.rb +15 -0
- data/lib/her/model.rb +8 -8
- data/lib/her/model/hooks.rb +24 -0
- data/lib/her/model/http.rb +19 -19
- data/lib/her/model/orm.rb +59 -46
- data/lib/her/model/relationships.rb +40 -27
- data/lib/her/version.rb +1 -1
- data/spec/api_spec.rb +41 -15
- data/spec/middleware/accept_json_spec.rb +10 -0
- data/spec/model/hooks_spec.rb +7 -7
- data/spec/model/http_spec.rb +44 -59
- data/spec/model/introspection_spec.rb +6 -5
- data/spec/model/orm_spec.rb +128 -23
- data/spec/model/paths_spec.rb +8 -10
- data/spec/model/relationships_spec.rb +54 -10
- data/spec/spec_helper.rb +0 -1
- metadata +59 -69
data/lib/her/middleware.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
+
require "her/middleware/first_level_parse_json"
|
2
|
+
require "her/middleware/second_level_parse_json"
|
3
|
+
require "her/middleware/accept_json"
|
4
|
+
|
1
5
|
module Her
|
2
6
|
module Middleware
|
3
|
-
autoload :FirstLevelParseJSON, "her/middleware/first_level_parse_json"
|
4
|
-
autoload :SecondLevelParseJSON, "her/middleware/second_level_parse_json"
|
5
|
-
|
6
7
|
DefaultParseJSON = FirstLevelParseJSON
|
7
8
|
end
|
8
9
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Her
|
2
|
+
module Middleware
|
3
|
+
# This middleware adds a "Accept: application/json" HTTP header
|
4
|
+
class AcceptJSON < Faraday::Middleware
|
5
|
+
def add_header(headers) # {{{
|
6
|
+
headers.merge! "Accept" => "application/json"
|
7
|
+
end # }}}
|
8
|
+
|
9
|
+
def call(env) # {{{
|
10
|
+
add_header(env[:request_headers])
|
11
|
+
@app.call(env)
|
12
|
+
end # }}}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/her/model.rb
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
require "her/model/base"
|
2
|
+
require "her/model/http"
|
3
|
+
require "her/model/orm"
|
4
|
+
require "her/model/relationships"
|
5
|
+
require "her/model/hooks"
|
6
|
+
require "her/model/introspection"
|
7
|
+
require "her/model/paths"
|
8
|
+
|
1
9
|
module Her
|
2
10
|
# This module is the main element of Her. After creating a Her::API object,
|
3
11
|
# include this module in your models to get a few magic methods defined in them.
|
@@ -10,14 +18,6 @@ module Her
|
|
10
18
|
# @user = User.new(:name => "Rémi")
|
11
19
|
# @user.save
|
12
20
|
module Model
|
13
|
-
autoload :Base, "her/model/base"
|
14
|
-
autoload :HTTP, "her/model/http"
|
15
|
-
autoload :ORM, "her/model/orm"
|
16
|
-
autoload :Relationships, "her/model/relationships"
|
17
|
-
autoload :Hooks, "her/model/hooks"
|
18
|
-
autoload :Introspection, "her/model/introspection"
|
19
|
-
autoload :Paths, "her/model/paths"
|
20
|
-
|
21
21
|
extend ActiveSupport::Concern
|
22
22
|
|
23
23
|
# Instance methods
|
data/lib/her/model/hooks.rb
CHANGED
@@ -51,6 +51,14 @@ module Her
|
|
51
51
|
# @param [Symbol, &block] method A method or a block to be called
|
52
52
|
def after_destroy(method=nil, &block); set_hook(:after, :destroy, method || block); end
|
53
53
|
|
54
|
+
# Wrap a block between “before” and “after” hooks
|
55
|
+
# @private
|
56
|
+
def wrap_in_hooks(resource, *hooks) # {{{
|
57
|
+
perform_before_hooks(resource, *hooks)
|
58
|
+
yield(resource, resource.class)
|
59
|
+
perform_after_hooks(resource, *hooks.reverse)
|
60
|
+
end # }}}
|
61
|
+
|
54
62
|
private
|
55
63
|
# @private
|
56
64
|
def hooks # {{{
|
@@ -75,6 +83,22 @@ module Her
|
|
75
83
|
end
|
76
84
|
end
|
77
85
|
end # }}}
|
86
|
+
|
87
|
+
# Perform “after” hooks on a resource
|
88
|
+
# @private
|
89
|
+
def perform_after_hooks(resource, *hooks) # {{{
|
90
|
+
hooks.each do |hook|
|
91
|
+
perform_hook(resource, :after, hook)
|
92
|
+
end
|
93
|
+
end # }}}
|
94
|
+
|
95
|
+
# Perform “before” hooks on a resource
|
96
|
+
# @private
|
97
|
+
def perform_before_hooks(resource, *hooks) # {{{
|
98
|
+
hooks.each do |hook|
|
99
|
+
perform_hook(resource, :before, hook)
|
100
|
+
end
|
101
|
+
end # }}}
|
78
102
|
end
|
79
103
|
end
|
80
104
|
end
|
data/lib/her/model/http.rb
CHANGED
@@ -26,9 +26,9 @@ module Her
|
|
26
26
|
path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
|
27
27
|
get_raw(path, attrs) do |parsed_data|
|
28
28
|
if parsed_data[:data].is_a?(Array)
|
29
|
-
new_collection(parsed_data
|
29
|
+
new_collection(parsed_data)
|
30
30
|
else
|
31
|
-
new(parsed_data[:data])
|
31
|
+
new(parsed_data[:data].merge :_metadata => parsed_data[:data], :_errors => parsed_data[:errors])
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end # }}}
|
@@ -43,7 +43,7 @@ module Her
|
|
43
43
|
def get_collection(path, attrs={}) # {{{
|
44
44
|
path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
|
45
45
|
get_raw(path, attrs) do |parsed_data|
|
46
|
-
new_collection(parsed_data
|
46
|
+
new_collection(parsed_data)
|
47
47
|
end
|
48
48
|
end # }}}
|
49
49
|
|
@@ -51,7 +51,7 @@ module Her
|
|
51
51
|
def get_resource(path, attrs={}) # {{{
|
52
52
|
path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
|
53
53
|
get_raw(path, attrs) do |parsed_data|
|
54
|
-
new(parsed_data[:data])
|
54
|
+
new(parsed_data[:data].merge :_metadata => parsed_data[:data], :_errors => parsed_data[:errors])
|
55
55
|
end
|
56
56
|
end # }}}
|
57
57
|
|
@@ -60,9 +60,9 @@ module Her
|
|
60
60
|
path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
|
61
61
|
post_raw(path, attrs) do |parsed_data|
|
62
62
|
if parsed_data[:data].is_a?(Array)
|
63
|
-
new_collection(parsed_data
|
63
|
+
new_collection(parsed_data)
|
64
64
|
else
|
65
|
-
new(parsed_data[:data])
|
65
|
+
new(parsed_data[:data].merge :_metadata => parsed_data[:data], :_errors => parsed_data[:errors])
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end # }}}
|
@@ -77,7 +77,7 @@ module Her
|
|
77
77
|
def post_collection(path, attrs={}) # {{{
|
78
78
|
path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
|
79
79
|
post_raw(path, attrs) do |parsed_data|
|
80
|
-
new_collection(parsed_data
|
80
|
+
new_collection(parsed_data)
|
81
81
|
end
|
82
82
|
end # }}}
|
83
83
|
|
@@ -94,9 +94,9 @@ module Her
|
|
94
94
|
path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
|
95
95
|
put_raw(path, attrs) do |parsed_data|
|
96
96
|
if parsed_data[:data].is_a?(Array)
|
97
|
-
new_collection(parsed_data
|
97
|
+
new_collection(parsed_data)
|
98
98
|
else
|
99
|
-
new(parsed_data[:data])
|
99
|
+
new(parsed_data[:data].merge :_metadata => parsed_data[:data], :_errors => parsed_data[:errors])
|
100
100
|
end
|
101
101
|
end
|
102
102
|
end # }}}
|
@@ -111,7 +111,7 @@ module Her
|
|
111
111
|
def put_collection(path, attrs={}) # {{{
|
112
112
|
path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
|
113
113
|
put_raw(path, attrs) do |parsed_data|
|
114
|
-
new_collection(parsed_data
|
114
|
+
new_collection(parsed_data)
|
115
115
|
end
|
116
116
|
end # }}}
|
117
117
|
|
@@ -119,7 +119,7 @@ module Her
|
|
119
119
|
def put_resource(path, attrs={}) # {{{
|
120
120
|
path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
|
121
121
|
put_raw(path, attrs) do |parsed_data|
|
122
|
-
new(parsed_data[:data])
|
122
|
+
new(parsed_data[:data].merge :_metadata => parsed_data[:data], :_errors => parsed_data[:errors])
|
123
123
|
end
|
124
124
|
end # }}}
|
125
125
|
|
@@ -128,9 +128,9 @@ module Her
|
|
128
128
|
path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
|
129
129
|
patch_raw(path, attrs) do |parsed_data|
|
130
130
|
if parsed_data[:data].is_a?(Array)
|
131
|
-
new_collection(parsed_data
|
131
|
+
new_collection(parsed_data)
|
132
132
|
else
|
133
|
-
new(parsed_data[:data])
|
133
|
+
new(parsed_data[:data].merge :_metadata => parsed_data[:data], :_errors => parsed_data[:errors])
|
134
134
|
end
|
135
135
|
end
|
136
136
|
end # }}}
|
@@ -145,7 +145,7 @@ module Her
|
|
145
145
|
def patch_collection(path, attrs={}) # {{{
|
146
146
|
path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
|
147
147
|
patch_raw(path, attrs) do |parsed_data|
|
148
|
-
new_collection(parsed_data
|
148
|
+
new_collection(parsed_data)
|
149
149
|
end
|
150
150
|
end # }}}
|
151
151
|
|
@@ -153,7 +153,7 @@ module Her
|
|
153
153
|
def patch_resource(path, attrs={}) # {{{
|
154
154
|
path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
|
155
155
|
patch_raw(path, attrs) do |parsed_data|
|
156
|
-
new(parsed_data[:data])
|
156
|
+
new(parsed_data[:data].merge :_metadata => parsed_data[:data], :_errors => parsed_data[:errors])
|
157
157
|
end
|
158
158
|
end # }}}
|
159
159
|
|
@@ -162,9 +162,9 @@ module Her
|
|
162
162
|
path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
|
163
163
|
delete_raw(path, attrs) do |parsed_data|
|
164
164
|
if parsed_data[:data].is_a?(Array)
|
165
|
-
new_collection(parsed_data
|
165
|
+
new_collection(parsed_data)
|
166
166
|
else
|
167
|
-
new(parsed_data[:data])
|
167
|
+
new(parsed_data[:data].merge :_metadata => parsed_data[:data], :_errors => parsed_data[:errors])
|
168
168
|
end
|
169
169
|
end
|
170
170
|
end # }}}
|
@@ -179,7 +179,7 @@ module Her
|
|
179
179
|
def delete_collection(path, attrs={}) # {{{
|
180
180
|
path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
|
181
181
|
delete_raw(path, attrs) do |parsed_data|
|
182
|
-
new_collection(parsed_data
|
182
|
+
new_collection(parsed_data)
|
183
183
|
end
|
184
184
|
end # }}}
|
185
185
|
|
@@ -187,7 +187,7 @@ module Her
|
|
187
187
|
def delete_resource(path, attrs={}) # {{{
|
188
188
|
path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
|
189
189
|
delete_raw(path, attrs) do |parsed_data|
|
190
|
-
new(parsed_data[:data])
|
190
|
+
new(parsed_data[:data].merge :_metadata => parsed_data[:data], :_errors => parsed_data[:errors])
|
191
191
|
end
|
192
192
|
end # }}}
|
193
193
|
|
data/lib/her/model/orm.rb
CHANGED
@@ -2,17 +2,29 @@ module Her
|
|
2
2
|
module Model
|
3
3
|
# This module adds ORM-like capabilities to the model
|
4
4
|
module ORM
|
5
|
+
attr_reader :metadata, :errors
|
6
|
+
|
5
7
|
# Initialize a new object with data received from an HTTP request
|
6
8
|
# @private
|
7
|
-
def initialize(
|
8
|
-
@data =
|
9
|
-
@
|
9
|
+
def initialize(data={}) # {{{
|
10
|
+
@data = {}
|
11
|
+
@metadata = data.delete(:_metadata) || {}
|
12
|
+
@errors = data.delete(:_errors) || {}
|
13
|
+
cleaned_data = data.inject({}) do |memo, item|
|
14
|
+
key, value = item
|
15
|
+
send "#{key}=".to_sym, value unless value.nil?
|
16
|
+
respond_to?("#{key}=") ? memo : memo.merge({ key => value })
|
17
|
+
end
|
18
|
+
@data.merge! self.class.parse_relationships(cleaned_data)
|
10
19
|
end # }}}
|
11
20
|
|
12
21
|
# Initialize a collection of resources
|
13
22
|
# @private
|
14
|
-
def self.initialize_collection(name,
|
15
|
-
collection_data.map
|
23
|
+
def self.initialize_collection(name, parsed_data={}) # {{{
|
24
|
+
collection_data = parsed_data[:data].map do |item_data|
|
25
|
+
Object.const_get(name.to_s.classify).new(item_data)
|
26
|
+
end
|
27
|
+
Her::Collection.new(collection_data, parsed_data[:metadata], parsed_data[:errors])
|
16
28
|
end # }}}
|
17
29
|
|
18
30
|
# Handles missing methods by routing them through @data
|
@@ -20,10 +32,11 @@ module Her
|
|
20
32
|
def method_missing(method, attrs=nil) # {{{
|
21
33
|
assignment_method = method.to_s =~ /\=$/
|
22
34
|
method = method.to_s.gsub(/(\?|\!|\=)$/, "").to_sym
|
23
|
-
if attrs and assignment_method
|
35
|
+
if !attrs.nil? and assignment_method
|
36
|
+
@data ||= {}
|
24
37
|
@data[method.to_s.gsub(/\=$/, "").to_sym] = attrs
|
25
38
|
else
|
26
|
-
if @data.include?(method)
|
39
|
+
if @data and @data.include?(method)
|
27
40
|
@data[method]
|
28
41
|
else
|
29
42
|
super
|
@@ -39,9 +52,9 @@ module Her
|
|
39
52
|
|
40
53
|
# Initialize a collection of resources with raw data from an HTTP request
|
41
54
|
#
|
42
|
-
# @param [Array]
|
43
|
-
def new_collection(
|
44
|
-
Her::Model::ORM.initialize_collection(self.to_s.underscore,
|
55
|
+
# @param [Array] parsed_data
|
56
|
+
def new_collection(parsed_data) # {{{
|
57
|
+
Her::Model::ORM.initialize_collection(self.to_s.underscore, parsed_data)
|
45
58
|
end # }}}
|
46
59
|
|
47
60
|
# Return `true` if a resource was not saved yet
|
@@ -49,6 +62,16 @@ module Her
|
|
49
62
|
!@data.include?(:id)
|
50
63
|
end # }}}
|
51
64
|
|
65
|
+
# Return `true` if a resource does not contain errors
|
66
|
+
def valid? # {{{
|
67
|
+
@errors.empty?
|
68
|
+
end # }}}
|
69
|
+
|
70
|
+
# Return `true` if a resource contains errors
|
71
|
+
def invalid? # {{{
|
72
|
+
@errors.any?
|
73
|
+
end # }}}
|
74
|
+
|
52
75
|
# Fetch a specific resource based on an ID
|
53
76
|
#
|
54
77
|
# @example
|
@@ -67,7 +90,7 @@ module Her
|
|
67
90
|
# # Fetched via GET "/users"
|
68
91
|
def all(params={}) # {{{
|
69
92
|
request(params.merge(:_method => :get, :_path => "#{build_request_path(params)}")) do |parsed_data|
|
70
|
-
new_collection(parsed_data
|
93
|
+
new_collection(parsed_data)
|
71
94
|
end
|
72
95
|
end # }}}
|
73
96
|
|
@@ -78,17 +101,16 @@ module Her
|
|
78
101
|
# # Called via POST "/users/1"
|
79
102
|
def create(params={}) # {{{
|
80
103
|
resource = new(params)
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
104
|
+
wrap_in_hooks(resource, :create, :save) do |resource, klass|
|
105
|
+
params = resource.instance_eval { @data }
|
106
|
+
request(params.merge(:_method => :post, :_path => "#{build_request_path(params)}")) do |parsed_data|
|
107
|
+
resource.instance_eval do
|
108
|
+
@data = parsed_data[:data]
|
109
|
+
@metadata = parsed_data[:metadata]
|
110
|
+
@errors = parsed_data[:errors]
|
111
|
+
end
|
87
112
|
end
|
88
113
|
end
|
89
|
-
perform_hook(resource, :after, :save)
|
90
|
-
perform_hook(resource, :after, :create)
|
91
|
-
|
92
114
|
resource
|
93
115
|
end # }}}
|
94
116
|
|
@@ -118,30 +140,20 @@ module Her
|
|
118
140
|
def save # {{{
|
119
141
|
params = @data.dup
|
120
142
|
resource = self
|
143
|
+
|
121
144
|
if @data[:id]
|
122
|
-
|
123
|
-
|
124
|
-
perform_hook(resource, :before, :save)
|
125
|
-
end
|
126
|
-
self.class.request(params.merge(:_method => :put, :_path => "#{request_path}")) do |parsed_data|
|
127
|
-
@data = parsed_data[:data]
|
128
|
-
end
|
129
|
-
self.class.class_eval do
|
130
|
-
perform_hook(resource, :after, :save)
|
131
|
-
perform_hook(resource, :after, :update)
|
132
|
-
end
|
133
|
-
self
|
145
|
+
hooks = [:update, :save]
|
146
|
+
method = :put
|
134
147
|
else
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
148
|
+
hooks = [:create, :save]
|
149
|
+
method = :post
|
150
|
+
end
|
151
|
+
|
152
|
+
self.class.wrap_in_hooks(resource, *hooks) do |resource, klass|
|
153
|
+
klass.request(params.merge(:_method => method, :_path => "#{request_path}")) do |parsed_data|
|
140
154
|
@data = parsed_data[:data]
|
141
|
-
|
142
|
-
|
143
|
-
perform_hook(resource, :after, :save)
|
144
|
-
perform_hook(resource, :after, :create)
|
155
|
+
@metadata = parsed_data[:metadata]
|
156
|
+
@errors = parsed_data[:errors]
|
145
157
|
end
|
146
158
|
end
|
147
159
|
self
|
@@ -154,13 +166,14 @@ module Her
|
|
154
166
|
# @user.destroy
|
155
167
|
# # Called via DELETE "/users/1"
|
156
168
|
def destroy # {{{
|
157
|
-
params = @data.dup
|
158
169
|
resource = self
|
159
|
-
self.class.
|
160
|
-
|
161
|
-
|
170
|
+
self.class.wrap_in_hooks(resource, :destroy) do |resource, klass|
|
171
|
+
klass.request(:_method => :delete, :_path => "#{request_path}") do |parsed_data|
|
172
|
+
@data = parsed_data[:data]
|
173
|
+
@metadata = parsed_data[:metadata]
|
174
|
+
@errors = parsed_data[:errors]
|
175
|
+
end
|
162
176
|
end
|
163
|
-
self.class.class_eval { perform_hook(resource, :after, :destroy) }
|
164
177
|
self
|
165
178
|
end # }}}
|
166
179
|
|
@@ -14,14 +14,16 @@ module Her
|
|
14
14
|
@her_relationships ||= {}
|
15
15
|
@her_relationships.each_pair do |type, relationships|
|
16
16
|
relationships.each do |relationship|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
17
|
+
name = relationship[:name]
|
18
|
+
class_name = relationship[:class_name]
|
19
|
+
next if !data.include?(name) or data[name].nil?
|
20
|
+
data[name] = case type
|
21
|
+
when :has_many
|
22
|
+
Her::Model::ORM.initialize_collection(class_name, :data => data[name])
|
23
|
+
when :has_one, :belongs_to
|
24
|
+
Object.const_get(class_name).new(data[name])
|
25
|
+
else
|
26
|
+
nil
|
25
27
|
end
|
26
28
|
end
|
27
29
|
end
|
@@ -47,14 +49,8 @@ module Her
|
|
47
49
|
# @user.articles # => [#<Article(articles/2) id=2 title="Hello world.">]
|
48
50
|
# # Fetched via GET "/users/1/articles"
|
49
51
|
def has_many(name, attrs={}) # {{{
|
50
|
-
@her_relationships ||= {}
|
51
52
|
attrs = { :class_name => name.to_s.classify, :name => name }.merge(attrs)
|
52
|
-
(
|
53
|
-
|
54
|
-
define_method(name) do
|
55
|
-
return @data[name] if @data.include?(name) # Do not fetch from API again if we have it in @data
|
56
|
-
Object.const_get(attrs[:class_name]).get_collection("#{self.class.build_request_path(:id => id)}/#{name.to_s.pluralize}")
|
57
|
-
end
|
53
|
+
define_relationship(:has_many, attrs)
|
58
54
|
end # }}}
|
59
55
|
|
60
56
|
# Define an *has_one* relationship.
|
@@ -76,14 +72,8 @@ module Her
|
|
76
72
|
# @user.organization # => #<Organization(organizations/2) id=2 name="Foobar Inc.">
|
77
73
|
# # Fetched via GET "/users/1/organization"
|
78
74
|
def has_one(name, attrs={}) # {{{
|
79
|
-
|
80
|
-
|
81
|
-
(@her_relationships[:has_one] ||= []) << attrs
|
82
|
-
|
83
|
-
define_method(name) do
|
84
|
-
return @data[name] if @data.include?(name) # Do not fetch from API again if we have it in @data
|
85
|
-
Object.const_get(attrs[:class_name]).get_resource("#{self.class.build_request_path(:id => id)}/#{name.to_s.singularize}")
|
86
|
-
end
|
75
|
+
attrs = { :class_name => name.to_s.classify, :name => name }.merge(attrs)
|
76
|
+
define_relationship(:has_one, attrs)
|
87
77
|
end # }}}
|
88
78
|
|
89
79
|
# Define a *belongs_to* relationship.
|
@@ -105,13 +95,36 @@ module Her
|
|
105
95
|
# @user.team # => #<Team(teams/2) id=2 name="Developers">
|
106
96
|
# # Fetched via GET "/teams/2"
|
107
97
|
def belongs_to(name, attrs={}) # {{{
|
108
|
-
@her_relationships ||= {}
|
109
98
|
attrs = { :class_name => name.to_s.classify, :name => name, :foreign_key => "#{name}_id" }.merge(attrs)
|
110
|
-
(
|
99
|
+
define_relationship(:belongs_to, attrs)
|
100
|
+
end # }}}
|
111
101
|
|
102
|
+
private
|
103
|
+
# @private
|
104
|
+
def define_relationship(type, attrs) # {{{
|
105
|
+
@her_relationships ||= {}
|
106
|
+
(@her_relationships[type] ||= []) << attrs
|
107
|
+
relationship_accessor(type, attrs)
|
108
|
+
end # }}}
|
109
|
+
|
110
|
+
# @private
|
111
|
+
def relationship_accessor(type, attrs) # {{{
|
112
|
+
name = attrs[:name]
|
113
|
+
class_name = attrs[:class_name]
|
112
114
|
define_method(name) do
|
113
|
-
return @data[name] if @data.include?(name)
|
114
|
-
|
115
|
+
return @data[name] if @data.include?(name)
|
116
|
+
|
117
|
+
klass = Object.const_get(class_name)
|
118
|
+
path = self.class.build_request_path(:id => id)
|
119
|
+
@data[name] = case type
|
120
|
+
when :belongs_to
|
121
|
+
foreign_key = attrs[:foreign_key].to_sym
|
122
|
+
klass.get_resource("#{klass.build_request_path(:id => @data[foreign_key])}")
|
123
|
+
when :has_many
|
124
|
+
klass.get_collection("#{path}/#{name.to_s.pluralize}")
|
125
|
+
when :has_one
|
126
|
+
klass.get_resource("#{path}/#{name.to_s.singularize}")
|
127
|
+
end
|
115
128
|
end
|
116
129
|
end # }}}
|
117
130
|
end
|