her 0.5.1 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/Gemfile +1 -1
- data/LICENSE +1 -1
- data/README.md +9 -10
- data/lib/her/api.rb +3 -3
- data/lib/her/errors.rb +1 -1
- data/lib/her/model.rb +6 -6
- data/lib/her/model/{relationships.rb → associations.rb} +79 -58
- data/lib/her/model/introspection.rb +1 -1
- data/lib/her/model/nested_attributes.rb +24 -24
- data/lib/her/model/orm.rb +55 -50
- data/lib/her/model/paths.rb +2 -2
- data/lib/her/version.rb +1 -1
- data/spec/model/{relationships_spec.rb → associations_spec.rb} +56 -52
- data/spec/model/nested_attributes_spec.rb +3 -3
- data/spec/model/orm_spec.rb +5 -15
- data/spec/model/paths_spec.rb +3 -3
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NDM1MWIwOWZmYThlZjA5YTJlMzdlZDM3MDQzMjg5ZGNlOTVhOGYxMQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ODgwMDMwMDM3M2MyZTdjN2YwZDBhMDlhMDJlZTY5MzcwZTVhMjVjNQ==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
OGIyY2JkM2Y5OTI4NzdmZDk5OTE2ZGM4OWU4ZTM3MTYzOWNhMGRlOWZlNTc4
|
10
|
+
ZTcxODBmYzMxYjdmNDJkNGFhZDc0NGQzZGE3ODU0NTE4ODliYjQ4MTIyMGJi
|
11
|
+
NjBjMTE3ZGE1ZDM5Y2QwYWVjNTgwZDQxOGRkNDc0OTRhYzA2YjQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
M2ZlYmU3ZGE5ZjBkNDkxYjgyZWFlZmM4OWEyYjMxYzYzMmI1ODAyOTFhMmM1
|
14
|
+
ZDk0ZDA1YWUxODY2NGIzZDEwMTdiODQ5MWJkN2NlYjFlMTc5YjAxZmVkYzY1
|
15
|
+
NjY3YWY4NGIyNGFkNzMxNmZmY2E2YmJkOTIyNWZjMjU3MjZhNTE=
|
data/Gemfile
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
source
|
1
|
+
source "https://rubygems.org"
|
2
2
|
gemspec
|
data/LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2012 Rémi Prévost
|
1
|
+
Copyright (c) 2012-2013 Rémi Prévost
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
4
|
|
data/README.md
CHANGED
@@ -261,12 +261,12 @@ end
|
|
261
261
|
|
262
262
|
Here’s a list of several useful features available in Her.
|
263
263
|
|
264
|
-
###
|
264
|
+
### Associations
|
265
265
|
|
266
|
-
You can define `has_many`, `has_one` and `belongs_to`
|
266
|
+
You can define `has_many`, `has_one` and `belongs_to` associations in your models. The association data is handled in two different ways.
|
267
267
|
|
268
|
-
1. If Her finds
|
269
|
-
2. If no
|
268
|
+
1. If Her finds association data when parsing a resource, that data will be used to create the associated model objects on the resource.
|
269
|
+
2. If no association data was included when parsing a resource, calling a method with the same name as the association will fetch the data (providing there’s an HTTP request available for it in the API).
|
270
270
|
|
271
271
|
For example:
|
272
272
|
|
@@ -291,7 +291,7 @@ class Organization
|
|
291
291
|
end
|
292
292
|
```
|
293
293
|
|
294
|
-
If there’s
|
294
|
+
If there’s association data in the resource, no extra HTTP request is made when calling the `#comments` method and an array of resources is returned:
|
295
295
|
|
296
296
|
```ruby
|
297
297
|
@user = User.find(1)
|
@@ -315,23 +315,23 @@ If there’s relationship data in the resource, no extra HTTP request is made wh
|
|
315
315
|
# #<Organization id=2 name="Bluth Company">
|
316
316
|
```
|
317
317
|
|
318
|
-
If there’s no
|
318
|
+
If there’s no association data in the resource, Her makes a HTTP request to retrieve the data.
|
319
319
|
|
320
320
|
```ruby
|
321
321
|
@user = User.find(1)
|
322
322
|
# { :data => { :id => 1, :name => "George Michael Bluth", :organization_id => 2 }}
|
323
323
|
|
324
|
-
# has_many
|
324
|
+
# has_many association:
|
325
325
|
@user.comments
|
326
326
|
# GET /users/1/comments
|
327
327
|
# [#<Comment id=1>, #<Comment id=2>]
|
328
328
|
|
329
|
-
# has_one
|
329
|
+
# has_one association:
|
330
330
|
@user.role
|
331
331
|
# GET /users/1/role
|
332
332
|
# #<Role id=1>
|
333
333
|
|
334
|
-
# belongs_to
|
334
|
+
# belongs_to association:
|
335
335
|
@user.organization
|
336
336
|
# (the organization id comes from :organization_id, by default)
|
337
337
|
# GET /organizations/2
|
@@ -365,7 +365,6 @@ end
|
|
365
365
|
### Dirty attributes
|
366
366
|
|
367
367
|
Her includes `ActiveModel::Dirty` so you can keep track of the attributes that have changed in an object.
|
368
|
-
an object, or `#create` on a model class.
|
369
368
|
|
370
369
|
```ruby
|
371
370
|
class User
|
data/lib/her/api.rb
CHANGED
@@ -7,8 +7,8 @@ module Her
|
|
7
7
|
|
8
8
|
# Setup a default API connection. Accepted arguments and options are the same as {API#setup}.
|
9
9
|
def self.setup(attrs={}, &block)
|
10
|
-
|
11
|
-
|
10
|
+
@default_api = new
|
11
|
+
@default_api.setup(attrs, &block)
|
12
12
|
end
|
13
13
|
|
14
14
|
# Create a new API object. This is useful to create multiple APIs and use them with the `uses_api` method.
|
@@ -103,7 +103,7 @@ module Her
|
|
103
103
|
private
|
104
104
|
# @private
|
105
105
|
def self.default_api(attrs={})
|
106
|
-
defined?(
|
106
|
+
defined?(@default_api) ? @default_api : nil
|
107
107
|
end
|
108
108
|
end
|
109
109
|
end
|
data/lib/her/errors.rb
CHANGED
data/lib/her/model.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require "her/model/base"
|
2
2
|
require "her/model/http"
|
3
3
|
require "her/model/orm"
|
4
|
-
require "her/model/
|
4
|
+
require "her/model/associations"
|
5
5
|
require "her/model/introspection"
|
6
6
|
require "her/model/paths"
|
7
7
|
require "her/model/nested_attributes"
|
@@ -25,7 +25,7 @@ module Her
|
|
25
25
|
include Her::Model::ORM
|
26
26
|
include Her::Model::Introspection
|
27
27
|
include Her::Model::Paths
|
28
|
-
include Her::Model::
|
28
|
+
include Her::Model::Associations
|
29
29
|
include Her::Model::NestedAttributes
|
30
30
|
include ActiveModel::Validations
|
31
31
|
include ActiveModel::Conversion
|
@@ -51,18 +51,18 @@ module Her
|
|
51
51
|
|
52
52
|
# Returns true if attribute_name is
|
53
53
|
# * in orm data
|
54
|
-
# *
|
54
|
+
# * an association
|
55
55
|
def has_key?(attribute_name)
|
56
56
|
has_data?(attribute_name) ||
|
57
|
-
|
57
|
+
has_association?(attribute_name)
|
58
58
|
end
|
59
59
|
|
60
60
|
# Returns
|
61
61
|
# * the value of the attribute_nane attribute if it's in orm data
|
62
|
-
# * the resource/collection corrsponding to attribute_name if it's
|
62
|
+
# * the resource/collection corrsponding to attribute_name if it's an association
|
63
63
|
def [](attribute_name)
|
64
64
|
get_data(attribute_name) ||
|
65
|
-
|
65
|
+
get_association(attribute_name)
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
@@ -1,49 +1,55 @@
|
|
1
1
|
module Her
|
2
2
|
module Model
|
3
|
-
# This module adds
|
4
|
-
module
|
3
|
+
# This module adds associations to models
|
4
|
+
module Associations
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
|
-
# Returns true if the model has a
|
8
|
-
def
|
9
|
-
|
10
|
-
|
7
|
+
# Returns true if the model has a association_name association, false otherwise.
|
8
|
+
def has_association?(association_name)
|
9
|
+
associations = self.class.associations.values.flatten.map { |r| r[:name] }
|
10
|
+
associations.include?(association_name)
|
11
11
|
end
|
12
|
+
alias :has_relationship? :has_association?
|
12
13
|
|
13
|
-
# Returns the resource/collection corresponding to the
|
14
|
-
def
|
15
|
-
send(
|
14
|
+
# Returns the resource/collection corresponding to the association_name association.
|
15
|
+
def get_association(association_name)
|
16
|
+
send(association_name) if has_association?(association_name)
|
16
17
|
end
|
18
|
+
alias :get_relationship :get_association
|
17
19
|
|
18
20
|
module ClassMethods
|
19
|
-
# Return @
|
20
|
-
# superclass'
|
21
|
+
# Return @her_associations, lazily initialized with copy of the
|
22
|
+
# superclass' her_associations, or an empty hash.
|
21
23
|
#
|
22
24
|
# @private
|
23
|
-
def
|
24
|
-
@
|
25
|
-
if superclass.respond_to?(:
|
26
|
-
superclass.
|
25
|
+
def associations
|
26
|
+
@her_associations ||= begin
|
27
|
+
if superclass.respond_to?(:associations)
|
28
|
+
superclass.associations.dup
|
27
29
|
else
|
28
30
|
{}
|
29
31
|
end
|
30
32
|
end
|
31
33
|
end
|
34
|
+
alias :relationships :associations
|
32
35
|
|
33
|
-
# Parse
|
36
|
+
# Parse associations data after initializing a new object
|
34
37
|
#
|
35
38
|
# @private
|
36
|
-
def
|
37
|
-
|
38
|
-
definitions.each do |
|
39
|
-
|
40
|
-
next unless data[
|
41
|
-
|
39
|
+
def parse_associations(data)
|
40
|
+
associations.each_pair do |type, definitions|
|
41
|
+
definitions.each do |association|
|
42
|
+
data_key = association[:data_key]
|
43
|
+
next unless data[data_key]
|
44
|
+
|
45
|
+
klass = self.nearby_class(association[:class_name])
|
46
|
+
name = association[:name]
|
47
|
+
|
42
48
|
data[name] = case type
|
43
49
|
when :has_many
|
44
|
-
Her::Model::ORM.initialize_collection(klass, :data => data[
|
50
|
+
Her::Model::ORM.initialize_collection(klass, :data => data[data_key])
|
45
51
|
when :has_one, :belongs_to
|
46
|
-
klass.new(
|
52
|
+
klass.new(data[data_key])
|
47
53
|
else
|
48
54
|
nil
|
49
55
|
end
|
@@ -52,7 +58,7 @@ module Her
|
|
52
58
|
data
|
53
59
|
end
|
54
60
|
|
55
|
-
# Define an *has_many*
|
61
|
+
# Define an *has_many* association.
|
56
62
|
#
|
57
63
|
# @param [Symbol] name The name of the model
|
58
64
|
# @param [Hash] attrs Options (currently not used)
|
@@ -74,34 +80,39 @@ module Her
|
|
74
80
|
attrs = {
|
75
81
|
:class_name => name.to_s.classify,
|
76
82
|
:name => name,
|
83
|
+
:data_key => name,
|
77
84
|
:path => "/#{name}",
|
78
85
|
:inverse_of => nil
|
79
86
|
}.merge(attrs)
|
80
|
-
(
|
87
|
+
(associations[:has_many] ||= []) << attrs
|
81
88
|
|
82
89
|
define_method(name) do |*method_attrs|
|
83
90
|
method_attrs = method_attrs[0] || {}
|
84
91
|
klass = self.class.nearby_class(attrs[:class_name])
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
92
|
+
|
93
|
+
return Her::Collection.new if @attributes.include?(name) && @attributes[name].empty? && method_attrs.empty?
|
94
|
+
|
95
|
+
if @attributes[name].blank? || method_attrs.any?
|
96
|
+
path = begin
|
97
|
+
self.class.build_request_path(@attributes.merge(method_attrs))
|
98
|
+
rescue Her::Errors::PathError
|
99
|
+
return nil
|
100
|
+
end
|
101
|
+
|
102
|
+
@attributes[name] = klass.get_collection("#{path}#{attrs[:path]}", method_attrs)
|
89
103
|
end
|
90
104
|
|
91
|
-
inverse_of =
|
92
|
-
|
93
|
-
|
94
|
-
self.class.name.split('::').last.tableize.singularize
|
95
|
-
end
|
96
|
-
@data[name].each do |entry|
|
105
|
+
inverse_of = attrs[:inverse_of] || self.class.name.split('::').last.tableize.singularize
|
106
|
+
|
107
|
+
@attributes[name].each do |entry|
|
97
108
|
entry.send("#{inverse_of}=", self)
|
98
109
|
end
|
99
110
|
|
100
|
-
@
|
111
|
+
@attributes[name]
|
101
112
|
end
|
102
113
|
end
|
103
114
|
|
104
|
-
# Define an *has_one*
|
115
|
+
# Define an *has_one* association.
|
105
116
|
#
|
106
117
|
# @param [Symbol] name The name of the model
|
107
118
|
# @param [Hash] attrs Options (currently not used)
|
@@ -123,22 +134,32 @@ module Her
|
|
123
134
|
attrs = {
|
124
135
|
:class_name => name.to_s.classify,
|
125
136
|
:name => name,
|
137
|
+
:data_key => name,
|
126
138
|
:path => "/#{name}"
|
127
139
|
}.merge(attrs)
|
128
|
-
(
|
140
|
+
(associations[:has_one] ||= []) << attrs
|
129
141
|
|
130
142
|
define_method(name) do |*method_attrs|
|
131
143
|
method_attrs = method_attrs[0] || {}
|
132
144
|
klass = self.class.nearby_class(attrs[:class_name])
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
145
|
+
|
146
|
+
return nil if @attributes.include?(name) && @attributes[name].nil? && method_attrs.empty?
|
147
|
+
|
148
|
+
if @attributes[name].blank? || method_attrs.any?
|
149
|
+
path = begin
|
150
|
+
self.class.build_request_path(@attributes.merge(method_attrs))
|
151
|
+
rescue Her::Errors::PathError
|
152
|
+
return nil
|
153
|
+
end
|
154
|
+
|
155
|
+
@attributes[name] = klass.get_resource("#{path}#{attrs[:path]}", method_attrs)
|
137
156
|
end
|
157
|
+
|
158
|
+
@attributes[name]
|
138
159
|
end
|
139
160
|
end
|
140
161
|
|
141
|
-
# Define a *belongs_to*
|
162
|
+
# Define a *belongs_to* association.
|
142
163
|
#
|
143
164
|
# @param [Symbol] name The name of the model
|
144
165
|
# @param [Hash] attrs Options (currently not used)
|
@@ -160,29 +181,29 @@ module Her
|
|
160
181
|
attrs = {
|
161
182
|
:class_name => name.to_s.classify,
|
162
183
|
:name => name,
|
184
|
+
:data_key => name,
|
163
185
|
:foreign_key => "#{name}_id",
|
164
186
|
:path => "/#{name.to_s.pluralize}/:id"
|
165
187
|
}.merge(attrs)
|
166
|
-
(
|
188
|
+
(associations[:belongs_to] ||= []) << attrs
|
167
189
|
|
168
190
|
define_method(name) do |*method_attrs|
|
169
191
|
method_attrs = method_attrs[0] || {}
|
170
192
|
klass = self.class.nearby_class(attrs[:class_name])
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
193
|
+
|
194
|
+
return nil if @attributes.include?(name) && @attributes[name].nil? && method_attrs.empty?
|
195
|
+
|
196
|
+
if @attributes[name].blank? || method_attrs.any?
|
197
|
+
path = begin
|
198
|
+
klass.build_request_path(@attributes.merge(method_attrs.merge(:id => @attributes[attrs[:foreign_key].to_sym])))
|
199
|
+
rescue Her::Errors::PathError
|
200
|
+
return nil
|
201
|
+
end
|
202
|
+
|
203
|
+
@attributes[name] = klass.get_resource("#{path}", method_attrs)
|
175
204
|
end
|
176
|
-
end
|
177
|
-
end
|
178
205
|
|
179
|
-
|
180
|
-
def relationship_accessor(type, attrs)
|
181
|
-
name = attrs[:name]
|
182
|
-
class_name = attrs[:class_name]
|
183
|
-
define_method(name) do
|
184
|
-
klass = self.class.nearby_class(attrs[:class_name])
|
185
|
-
@data[name] ||= klass.get_resource("#{klass.build_request_path(attrs[:path], :id => @data[attrs[:foreign_key].to_sym])}")
|
206
|
+
@attributes[name]
|
186
207
|
end
|
187
208
|
end
|
188
209
|
end
|
@@ -12,7 +12,7 @@ module Her
|
|
12
12
|
# @user = User.find(1)
|
13
13
|
# p @user # => #<User(/users/1) id=1 name="Tobias Fünke">
|
14
14
|
def inspect
|
15
|
-
"#<#{self.class}(#{request_path}) #{
|
15
|
+
"#<#{self.class}(#{request_path}) #{attributes.keys.map { |k| "#{k}=#{attribute_for_inspect(send(k))}" }.join(" ")}>"
|
16
16
|
end
|
17
17
|
|
18
18
|
private
|
@@ -4,52 +4,52 @@ module Her
|
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
6
|
module ClassMethods
|
7
|
-
def accepts_nested_attributes_for(*
|
8
|
-
|
7
|
+
def accepts_nested_attributes_for(*association_names)
|
8
|
+
association_names.each do |association_name|
|
9
9
|
type = nil
|
10
|
-
[:belongs_to, :has_one, :has_many].each do |
|
11
|
-
if !
|
12
|
-
type =
|
10
|
+
[:belongs_to, :has_one, :has_many].each do |association_type|
|
11
|
+
if !associations[association_type].nil? && associations[association_type].any? { |association| association[:name] == association_name }
|
12
|
+
type = association_type
|
13
13
|
end
|
14
14
|
end
|
15
15
|
if type.nil?
|
16
|
-
raise(
|
16
|
+
raise(AssociationUnknownError.new("Unknown association name :#{association_name}"))
|
17
17
|
end
|
18
18
|
class_eval <<-eoruby, __FILE__, __LINE__ + 1
|
19
|
-
if method_defined?(:#{
|
20
|
-
remove_method(:#{
|
19
|
+
if method_defined?(:#{association_name}_attributes=)
|
20
|
+
remove_method(:#{association_name}_attributes=)
|
21
21
|
end
|
22
|
-
def #{
|
23
|
-
assign_nested_attributes_for_#{type}
|
22
|
+
def #{association_name}_attributes=(attributes)
|
23
|
+
assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
|
24
24
|
end
|
25
25
|
eoruby
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
def
|
31
|
-
|
30
|
+
def assign_nested_attributes_for_belongs_to_association(association_name, attributes)
|
31
|
+
assign_nested_attributes_for_simple_association(:belongs_to, association_name, attributes)
|
32
32
|
end
|
33
33
|
|
34
|
-
def
|
35
|
-
|
34
|
+
def assign_nested_attributes_for_has_one_association(association_name, attributes)
|
35
|
+
assign_nested_attributes_for_simple_association(:has_one, association_name, attributes)
|
36
36
|
end
|
37
37
|
|
38
|
-
def
|
39
|
-
|
40
|
-
klass = self.class.nearby_class(
|
41
|
-
self.send("#{
|
38
|
+
def assign_nested_attributes_for_has_many_association(association_name, attributes)
|
39
|
+
association = self.class.associations[:has_many].find { |association| association[:name] == association_name }
|
40
|
+
klass = self.class.nearby_class(association[:class_name])
|
41
|
+
self.send("#{association[:name]}=", Her::Model::ORM.initialize_collection(klass, :data => attributes))
|
42
42
|
end
|
43
43
|
|
44
44
|
private
|
45
|
-
def
|
46
|
-
|
47
|
-
if has_data?(
|
48
|
-
self.send("#{
|
45
|
+
def assign_nested_attributes_for_simple_association(association_type, association_name, attributes)
|
46
|
+
association = self.class.associations[association_type].find { |association| association[:name] == association_name }
|
47
|
+
if has_data?(association[:name])
|
48
|
+
self.send("#{association[:name]}").assign_data(attributes)
|
49
49
|
else
|
50
|
-
klass = self.class.nearby_class(
|
50
|
+
klass = self.class.nearby_class(association[:class_name])
|
51
51
|
instance = klass.new(klass.parse(attributes))
|
52
|
-
self.send("#{
|
52
|
+
self.send("#{association[:name]}=", instance)
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
data/lib/her/model/orm.rb
CHANGED
@@ -3,17 +3,18 @@ module Her
|
|
3
3
|
# This module adds ORM-like capabilities to the model
|
4
4
|
module ORM
|
5
5
|
extend ActiveSupport::Concern
|
6
|
-
attr_accessor :
|
7
|
-
alias :
|
8
|
-
alias :
|
6
|
+
attr_accessor :attributes, :metadata, :response_errors
|
7
|
+
alias :data :attributes
|
8
|
+
alias :data= :attributes=
|
9
9
|
|
10
10
|
# Initialize a new object with data received from an HTTP request
|
11
|
-
def initialize(
|
12
|
-
|
13
|
-
@
|
14
|
-
@
|
11
|
+
def initialize(attributes={})
|
12
|
+
attributes ||= {}
|
13
|
+
@metadata = attributes.delete(:_metadata) || {}
|
14
|
+
@response_errors = attributes.delete(:_errors) || {}
|
15
|
+
@destroyed = attributes.delete(:_destroyed) || false
|
15
16
|
|
16
|
-
|
17
|
+
update_attributes(attributes)
|
17
18
|
end
|
18
19
|
|
19
20
|
# Initialize a collection of resources
|
@@ -32,6 +33,7 @@ module Her
|
|
32
33
|
# @private
|
33
34
|
def self.use_setter_methods(model, params)
|
34
35
|
setter_method_names = model.class.setter_method_names
|
36
|
+
params ||= {}
|
35
37
|
params.inject({}) do |memo, (key, value)|
|
36
38
|
setter_method = key.to_s + '='
|
37
39
|
if setter_method_names.include?(setter_method)
|
@@ -46,16 +48,18 @@ module Her
|
|
46
48
|
end
|
47
49
|
end
|
48
50
|
|
49
|
-
# Handles missing methods
|
51
|
+
# Handles missing methods
|
50
52
|
# @private
|
51
53
|
def method_missing(method, *args, &blk)
|
52
|
-
if method.to_s.
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
54
|
+
if method.to_s =~ /[?=]$/ || attributes.include?(method)
|
55
|
+
# Extract the attribute
|
56
|
+
attribute = method.to_s.sub(/[?=]$/, '')
|
57
|
+
|
58
|
+
# Create a new `attribute` methods set
|
59
|
+
self.class.attributes(*attribute)
|
60
|
+
|
61
|
+
# Resend the method!
|
62
|
+
send(method, *args, &blk)
|
59
63
|
else
|
60
64
|
super
|
61
65
|
end
|
@@ -63,44 +67,45 @@ module Her
|
|
63
67
|
|
64
68
|
# Handles returning true for the cases handled by method_missing
|
65
69
|
def respond_to?(method, include_private = false)
|
66
|
-
method.to_s.end_with?('=') || method.to_s.end_with?('?') || @
|
70
|
+
method.to_s.end_with?('=') || method.to_s.end_with?('?') || @attributes.include?(method) || super
|
67
71
|
end
|
68
72
|
|
69
73
|
def respond_to_missing?(method, include_private = false)
|
70
|
-
method.to_s.end_with?('=') || method.to_s.end_with?('?') || @
|
74
|
+
method.to_s.end_with?('=') || method.to_s.end_with?('?') || @attributes.include?(method) || @attributes.include?(method) || super
|
71
75
|
end
|
72
76
|
|
73
77
|
# Assign new data to an instance
|
74
|
-
def
|
75
|
-
|
76
|
-
|
78
|
+
def assign_attributes(new_attributes)
|
79
|
+
new_attributes = Her::Model::ORM.use_setter_methods(self, new_attributes)
|
80
|
+
attributes.update new_attributes
|
77
81
|
end
|
78
|
-
alias :
|
82
|
+
alias :assign_data :assign_attributes
|
79
83
|
|
80
84
|
# Handles returning true for the accessible attributes
|
81
|
-
def
|
82
|
-
|
85
|
+
def has_attribute?(attribute_name)
|
86
|
+
attributes.include?(attribute_name)
|
83
87
|
end
|
88
|
+
alias :has_data? :has_attribute?
|
84
89
|
|
85
|
-
|
86
|
-
|
87
|
-
@data[attribute_name]
|
90
|
+
def get_attribute(attribute_name)
|
91
|
+
attributes[attribute_name]
|
88
92
|
end
|
93
|
+
alias :get_data :get_attribute
|
89
94
|
|
90
95
|
# Override the method to prevent from returning the object ID (in ruby-1.8.7)
|
91
96
|
# @private
|
92
97
|
def id
|
93
|
-
|
98
|
+
attributes[:id] || super
|
94
99
|
end
|
95
100
|
|
96
101
|
# Return `true` if a resource was not saved yet
|
97
102
|
def new?
|
98
|
-
|
103
|
+
!attributes.include?(:id)
|
99
104
|
end
|
100
105
|
|
101
106
|
# Return `true` if the other object is also a Her::Model and has matching data
|
102
107
|
def ==(other)
|
103
|
-
other.is_a?(Her::Model) &&
|
108
|
+
other.is_a?(Her::Model) && attributes == other.attributes
|
104
109
|
end
|
105
110
|
|
106
111
|
# Delegate to the == method
|
@@ -108,10 +113,10 @@ module Her
|
|
108
113
|
self == other
|
109
114
|
end
|
110
115
|
|
111
|
-
# Delegate to @
|
116
|
+
# Delegate to @attributes, allowing models to act correctly in code like:
|
112
117
|
# [ Model.find(1), Model.find(1) ].uniq # => [ Model.find(1) ]
|
113
118
|
def hash
|
114
|
-
|
119
|
+
attributes.hash
|
115
120
|
end
|
116
121
|
|
117
122
|
# Return whether the object has been destroyed
|
@@ -136,7 +141,7 @@ module Her
|
|
136
141
|
params = to_params
|
137
142
|
resource = self
|
138
143
|
|
139
|
-
if
|
144
|
+
if attributes[:id]
|
140
145
|
callback = :update
|
141
146
|
method = :put
|
142
147
|
else
|
@@ -147,12 +152,12 @@ module Her
|
|
147
152
|
run_callbacks callback do
|
148
153
|
run_callbacks :save do
|
149
154
|
self.class.request(params.merge(:_method => method, :_path => "#{request_path}")) do |parsed_data, response|
|
150
|
-
|
155
|
+
update_attributes(self.class.parse(parsed_data[:data])) if parsed_data[:data].any?
|
151
156
|
self.metadata = parsed_data[:metadata]
|
152
157
|
self.response_errors = parsed_data[:errors]
|
153
158
|
self.changed_attributes.clear if self.changed_attributes.present?
|
154
159
|
|
155
|
-
return false if self.response_errors.any?
|
160
|
+
return false if !response.success? || self.response_errors.any?
|
156
161
|
end
|
157
162
|
end
|
158
163
|
end
|
@@ -170,7 +175,7 @@ module Her
|
|
170
175
|
resource = self
|
171
176
|
run_callbacks :destroy do
|
172
177
|
self.class.request(:_method => :delete, :_path => "#{request_path}") do |parsed_data, response|
|
173
|
-
|
178
|
+
update_attributes(self.class.parse(parsed_data[:data])) if parsed_data[:data].any?
|
174
179
|
self.metadata = parsed_data[:metadata]
|
175
180
|
self.response_errors = parsed_data[:errors]
|
176
181
|
@destroyed = true
|
@@ -180,13 +185,13 @@ module Her
|
|
180
185
|
end
|
181
186
|
|
182
187
|
# @private
|
183
|
-
def
|
184
|
-
@
|
185
|
-
# Use setter methods first, then translate attributes of
|
186
|
-
# into
|
187
|
-
|
188
|
-
|
189
|
-
|
188
|
+
def update_attributes(raw_data)
|
189
|
+
@attributes ||= {}
|
190
|
+
# Use setter methods first, then translate attributes of associations
|
191
|
+
# into association instances, then merge the parsed_data into @attributes.
|
192
|
+
unset_attributes = Her::Model::ORM.use_setter_methods(self, raw_data)
|
193
|
+
parsed_attributes = self.class.parse_associations(unset_attributes)
|
194
|
+
attributes.update(parsed_attributes)
|
190
195
|
end
|
191
196
|
|
192
197
|
# Convert into a hash of request parameters
|
@@ -196,9 +201,9 @@ module Her
|
|
196
201
|
# # => { :id => 1, :name => 'John Smith' }
|
197
202
|
def to_params
|
198
203
|
if self.class.include_root_in_json
|
199
|
-
{ (self.class.include_root_in_json == true ? self.class.root_element : self.class.include_root_in_json) =>
|
204
|
+
{ (self.class.include_root_in_json == true ? self.class.root_element : self.class.include_root_in_json) => attributes.dup }
|
200
205
|
else
|
201
|
-
|
206
|
+
attributes.dup
|
202
207
|
end
|
203
208
|
end
|
204
209
|
|
@@ -220,16 +225,16 @@ module Her
|
|
220
225
|
attribute = attribute.to_sym
|
221
226
|
|
222
227
|
define_method "#{attribute}".to_sym do
|
223
|
-
@
|
228
|
+
@attributes.include?(attribute) ? @attributes[attribute] : nil
|
224
229
|
end
|
225
230
|
|
226
231
|
define_method "#{attribute}=".to_sym do |value|
|
227
|
-
self.send("#{attribute}_will_change!".to_sym) if @
|
228
|
-
@
|
232
|
+
self.send("#{attribute}_will_change!".to_sym) if @attributes[attribute] != value
|
233
|
+
@attributes[attribute] = value
|
229
234
|
end
|
230
235
|
|
231
236
|
define_method "#{attribute}?".to_sym do
|
232
|
-
@
|
237
|
+
@attributes.include?(attribute) && @attributes[attribute].present?
|
233
238
|
end
|
234
239
|
end
|
235
240
|
end
|
@@ -299,7 +304,7 @@ module Her
|
|
299
304
|
request(params.merge(:_method => :post, :_path => "#{build_request_path(params)}")) do |parsed_data, response|
|
300
305
|
data = parse(parsed_data[:data])
|
301
306
|
resource.instance_eval do
|
302
|
-
|
307
|
+
update_attributes(data)
|
303
308
|
@metadata = parsed_data[:metadata]
|
304
309
|
@response_errors = parsed_data[:errors]
|
305
310
|
@changed_attributes.clear if @changed_attributes.present?
|
data/lib/her/model/paths.rb
CHANGED
@@ -12,7 +12,7 @@ module Her
|
|
12
12
|
#
|
13
13
|
# User.find(1) # Fetched via GET /utilisateurs/1
|
14
14
|
def request_path
|
15
|
-
self.class.build_request_path(
|
15
|
+
self.class.build_request_path(attributes.dup)
|
16
16
|
end
|
17
17
|
|
18
18
|
module ClassMethods
|
@@ -67,7 +67,7 @@ module Her
|
|
67
67
|
|
68
68
|
path.gsub(/:([\w_]+)/) do
|
69
69
|
# Look for :key or :_key, otherwise raise an exception
|
70
|
-
parameters.delete($1.to_sym) || parameters.delete("_#{$1}".to_sym) || raise(Her::Errors::PathError
|
70
|
+
parameters.delete($1.to_sym) || parameters.delete("_#{$1}".to_sym) || raise(Her::Errors::PathError, "Missing :_#{$1} parameter to build the request path. Path is `#{path}`. Parameters are `#{parameters.inspect}`.")
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
data/lib/her/version.rb
CHANGED
@@ -1,100 +1,100 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require File.join(File.dirname(__FILE__), "../spec_helper.rb")
|
3
3
|
|
4
|
-
describe Her::Model::
|
5
|
-
context "setting
|
4
|
+
describe Her::Model::Associations do
|
5
|
+
context "setting associations without details" do
|
6
6
|
before do
|
7
7
|
spawn_model "Foo::User"
|
8
8
|
end
|
9
9
|
|
10
|
-
it "handles a single 'has_many'
|
10
|
+
it "handles a single 'has_many' association" do
|
11
11
|
Foo::User.has_many :comments
|
12
|
-
Foo::User.
|
12
|
+
Foo::User.associations[:has_many].should == [{ :name => :comments, :data_key => :comments, :class_name => "Comment", :path => "/comments", :inverse_of => nil }]
|
13
13
|
end
|
14
14
|
|
15
|
-
it "handles multiples 'has_many'
|
15
|
+
it "handles multiples 'has_many' association" do
|
16
16
|
Foo::User.has_many :comments
|
17
17
|
Foo::User.has_many :posts
|
18
|
-
Foo::User.
|
18
|
+
Foo::User.associations[:has_many].should == [{ :name => :comments, :data_key => :comments, :class_name => "Comment", :path => "/comments", :inverse_of => nil }, { :name => :posts, :data_key => :posts, :class_name => "Post", :path => "/posts", :inverse_of => nil }]
|
19
19
|
end
|
20
20
|
|
21
|
-
it "handles a single 'has_one'
|
21
|
+
it "handles a single 'has_one' association" do
|
22
22
|
Foo::User.has_one :category
|
23
|
-
Foo::User.
|
23
|
+
Foo::User.associations[:has_one].should == [{ :name => :category, :data_key => :category, :class_name => "Category", :path => "/category" }]
|
24
24
|
end
|
25
25
|
|
26
|
-
it "handles multiples 'has_one'
|
26
|
+
it "handles multiples 'has_one' association" do
|
27
27
|
Foo::User.has_one :category
|
28
28
|
Foo::User.has_one :role
|
29
|
-
Foo::User.
|
29
|
+
Foo::User.associations[:has_one].should == [{ :name => :category, :data_key => :category, :class_name => "Category", :path => "/category" }, { :name => :role, :data_key => :role, :class_name => "Role", :path => "/role" }]
|
30
30
|
end
|
31
31
|
|
32
|
-
it "handles a single belongs_to
|
32
|
+
it "handles a single belongs_to association" do
|
33
33
|
Foo::User.belongs_to :organization
|
34
|
-
Foo::User.
|
34
|
+
Foo::User.associations[:belongs_to].should == [{ :name => :organization, :data_key => :organization, :class_name => "Organization", :foreign_key => "organization_id", :path => "/organizations/:id" }]
|
35
35
|
end
|
36
36
|
|
37
|
-
it "handles multiples 'belongs_to'
|
37
|
+
it "handles multiples 'belongs_to' association" do
|
38
38
|
Foo::User.belongs_to :organization
|
39
39
|
Foo::User.belongs_to :family
|
40
|
-
Foo::User.
|
40
|
+
Foo::User.associations[:belongs_to].should == [{ :name => :organization, :data_key => :organization, :class_name => "Organization", :foreign_key => "organization_id", :path => "/organizations/:id" }, { :name => :family, :data_key => :family, :class_name => "Family", :foreign_key => "family_id", :path => "/families/:id" }]
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
context "setting
|
44
|
+
context "setting associations with details" do
|
45
45
|
before do
|
46
46
|
spawn_model "Foo::User"
|
47
47
|
end
|
48
48
|
|
49
|
-
it "handles a single 'has_many'
|
50
|
-
Foo::User.has_many :comments, :class_name => "Post", :inverse_of => :admin
|
51
|
-
Foo::User.
|
49
|
+
it "handles a single 'has_many' association" do
|
50
|
+
Foo::User.has_many :comments, :class_name => "Post", :inverse_of => :admin, :data_key => :user_comments
|
51
|
+
Foo::User.associations[:has_many].should == [{ :name => :comments, :data_key => :user_comments, :class_name => "Post", :path => "/comments", :inverse_of => :admin }]
|
52
52
|
end
|
53
53
|
|
54
|
-
it "handles a single 'has_one'
|
55
|
-
Foo::User.has_one :category, :class_name => "Topic", :foreign_key => "topic_id"
|
56
|
-
Foo::User.
|
54
|
+
it "handles a single 'has_one' association" do
|
55
|
+
Foo::User.has_one :category, :class_name => "Topic", :foreign_key => "topic_id", :data_key => :topic
|
56
|
+
Foo::User.associations[:has_one].should == [{ :name => :category, :data_key => :topic, :class_name => "Topic", :foreign_key => "topic_id", :path => "/category" }]
|
57
57
|
end
|
58
58
|
|
59
|
-
it "handles a single belongs_to
|
60
|
-
Foo::User.belongs_to :organization, :class_name => "Business", :foreign_key => "org_id"
|
61
|
-
Foo::User.
|
59
|
+
it "handles a single belongs_to association" do
|
60
|
+
Foo::User.belongs_to :organization, :class_name => "Business", :foreign_key => "org_id", :data_key => :org
|
61
|
+
Foo::User.associations[:belongs_to].should == [{ :name => :organization, :data_key => :org, :class_name => "Business", :foreign_key => "org_id", :path => "/organizations/:id" }]
|
62
62
|
end
|
63
63
|
|
64
|
-
context "inheriting
|
65
|
-
it "copies
|
64
|
+
context "inheriting associations from a superclass" do
|
65
|
+
it "copies associations to the subclass" do
|
66
66
|
Foo::User.has_many :comments, :class_name => "Post"
|
67
67
|
subclass = Class.new(Foo::User)
|
68
|
-
subclass.
|
69
|
-
subclass.
|
70
|
-
subclass.
|
68
|
+
subclass.associations.object_id.should_not == Foo::User.associations.object_id
|
69
|
+
subclass.associations[:has_many].length.should == 1
|
70
|
+
subclass.associations[:has_many].first[:class_name].should == "Post"
|
71
71
|
end
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
-
context "handling
|
75
|
+
context "handling associations without details" do
|
76
76
|
before do
|
77
77
|
Her::API.setup :url => "https://api.example.com" do |builder|
|
78
78
|
builder.use Her::Middleware::FirstLevelParseJSON
|
79
79
|
builder.use Faraday::Request::UrlEncoded
|
80
80
|
builder.adapter :test do |stub|
|
81
|
-
stub.get("/users/1") { |env| [200, {}, { :id => 1, :name => "Tobias Fünke", :comments => [{ :id => 2, :body => "Tobias, you blow hard!", :user_id => 1 }, { :id => 3, :body => "I wouldn't mind kissing that man between the cheeks, so to speak", :user_id => 1 }], :role => { :id => 1, :body => "Admin" }, :organization => { :id => 1, :name => "Bluth Company" }, :organization_id => 1 }.to_json] }
|
81
|
+
stub.get("/users/1") { |env| [200, {}, { :id => 1, :name => "Tobias Fünke", :comments => [{ :comment => { :id => 2, :body => "Tobias, you blow hard!", :user_id => 1 } }, { :comment => { :id => 3, :body => "I wouldn't mind kissing that man between the cheeks, so to speak", :user_id => 1 } }], :role => { :id => 1, :body => "Admin" }, :organization => { :id => 1, :name => "Bluth Company" }, :organization_id => 1 }.to_json] }
|
82
82
|
stub.get("/users/2") { |env| [200, {}, { :id => 2, :name => "Lindsay Fünke", :organization_id => 2 }.to_json] }
|
83
|
-
stub.get("/users/1/comments") { |env| [200, {}, [{ :id => 4, :body => "They're having a FIRESALE?" }].to_json] }
|
84
|
-
stub.get("/users/2/comments") { |env| [200, {}, [{ :id => 4, :body => "They're having a FIRESALE?" }, { :id => 5, :body => "Is this the tiny town from Footloose?" }].to_json] }
|
83
|
+
stub.get("/users/1/comments") { |env| [200, {}, [{ :comment => { :id => 4, :body => "They're having a FIRESALE?" } }].to_json] }
|
84
|
+
stub.get("/users/2/comments") { |env| [200, {}, [{ :comment => { :id => 4, :body => "They're having a FIRESALE?" } }, { :comment => { :id => 5, :body => "Is this the tiny town from Footloose?" } }].to_json] }
|
85
85
|
stub.get("/users/2/role") { |env| [200, {}, { :id => 2, :body => "User" }.to_json] }
|
86
86
|
stub.get("/users/1/role") { |env| [200, {}, { :id => 3, :body => "User" }.to_json] }
|
87
87
|
stub.get("/users/1/posts") { |env| [200, {}, {:id => 1, :body => 'blogging stuff', :admin_id => 1 }.to_json] }
|
88
|
-
stub.get("/organizations/1") { |env| [200, {}, { :id => 1, :name => "Bluth Company Foo" }.to_json] }
|
89
|
-
stub.post("/users") { |env| [200, {}, { :id => 5, :name => "Mr. Krabs", :comments => [{ :id => 99, :body => "Rodríguez, nasibisibusi?", :user_id => 5 }], :role => { :id => 1, :body => "Admin" }, :organization => { :id => 3, :name => "Krusty Krab" }, :organization_id => 3 }.to_json] }
|
90
|
-
stub.put("/users/5") { |env| [200, {}, { :id => 5, :name => "Clancy Brown", :comments => [{ :id => 99, :body => "Rodríguez, nasibisibusi?", :user_id => 5 }], :role => { :id => 1, :body => "Admin" }, :organization => { :id => 3, :name => "Krusty Krab" }, :organization_id => 3 }.to_json] }
|
91
|
-
stub.delete("/users/5") { |env| [200, {}, { :id => 5, :name => "Clancy Brown", :comments => [{ :id => 99, :body => "Rodríguez, nasibisibusi?", :user_id => 5 }], :role => { :id => 1, :body => "Admin" }, :organization => { :id => 3, :name => "Krusty Krab" }, :organization_id => 3 }.to_json] }
|
88
|
+
stub.get("/organizations/1") { |env| [200, {}, { :organization => { :id => 1, :name => "Bluth Company Foo" } }.to_json] }
|
89
|
+
stub.post("/users") { |env| [200, {}, { :id => 5, :name => "Mr. Krabs", :comments => [{ :comment => { :id => 99, :body => "Rodríguez, nasibisibusi?", :user_id => 5 } }], :role => { :id => 1, :body => "Admin" }, :organization => { :id => 3, :name => "Krusty Krab" }, :organization_id => 3 }.to_json] }
|
90
|
+
stub.put("/users/5") { |env| [200, {}, { :id => 5, :name => "Clancy Brown", :comments => [{ :comment => { :id => 99, :body => "Rodríguez, nasibisibusi?", :user_id => 5 } }], :role => { :id => 1, :body => "Admin" }, :organization => { :id => 3, :name => "Krusty Krab" }, :organization_id => 3 }.to_json] }
|
91
|
+
stub.delete("/users/5") { |env| [200, {}, { :id => 5, :name => "Clancy Brown", :comments => [{ :comment => { :id => 99, :body => "Rodríguez, nasibisibusi?", :user_id => 5 } }], :role => { :id => 1, :body => "Admin" }, :organization => { :id => 3, :name => "Krusty Krab" }, :organization_id => 3 }.to_json] }
|
92
92
|
|
93
93
|
stub.get("/organizations/2") do |env|
|
94
94
|
if env[:params]["admin"] == "true"
|
95
|
-
[200, {}, { :id => 2, :name => "Bluth Company (admin)" }.to_json]
|
95
|
+
[200, {}, { :organization => { :id => 2, :name => "Bluth Company (admin)" } }.to_json]
|
96
96
|
else
|
97
|
-
[200, {}, { :id => 2, :name => "Bluth Company" }.to_json]
|
97
|
+
[200, {}, { :organization => { :id => 2, :name => "Bluth Company" } }.to_json]
|
98
98
|
end
|
99
99
|
end
|
100
100
|
end
|
@@ -108,12 +108,16 @@ describe Her::Model::Relationships do
|
|
108
108
|
end
|
109
109
|
spawn_model "Foo::Comment" do
|
110
110
|
belongs_to :user
|
111
|
+
parse_root_in_json true
|
111
112
|
end
|
112
113
|
spawn_model "Foo::Post" do
|
113
114
|
belongs_to :admin, :class_name => 'Foo::User'
|
114
115
|
end
|
115
116
|
|
116
|
-
spawn_model "Foo::Organization"
|
117
|
+
spawn_model "Foo::Organization" do
|
118
|
+
parse_root_in_json true
|
119
|
+
end
|
120
|
+
|
117
121
|
spawn_model "Foo::Role"
|
118
122
|
|
119
123
|
@user_with_included_data = Foo::User.find(1)
|
@@ -182,14 +186,14 @@ describe Her::Model::Relationships do
|
|
182
186
|
@user_with_included_data.organization(:foo_id => 1).name.should == "Bluth Company Foo"
|
183
187
|
end
|
184
188
|
|
185
|
-
it "can tell if it has a
|
186
|
-
@user_without_included_data.
|
187
|
-
@user_without_included_data.
|
189
|
+
it "can tell if it has a association" do
|
190
|
+
@user_without_included_data.has_association?(:unknown_association).should be_false
|
191
|
+
@user_without_included_data.has_association?(:organization).should be_true
|
188
192
|
end
|
189
193
|
|
190
|
-
it "fetches the resource corresponding to a named
|
191
|
-
@user_without_included_data.
|
192
|
-
@user_without_included_data.
|
194
|
+
it "fetches the resource corresponding to a named association" do
|
195
|
+
@user_without_included_data.get_association(:unknown_association).should be_nil
|
196
|
+
@user_without_included_data.get_association(:organization).name.should == "Bluth Company"
|
193
197
|
end
|
194
198
|
|
195
199
|
it "pass query string parameters when additional arguments are passed" do
|
@@ -216,21 +220,21 @@ describe Her::Model::Relationships do
|
|
216
220
|
end
|
217
221
|
end
|
218
222
|
|
219
|
-
context "handling
|
223
|
+
context "handling associations with details" do
|
220
224
|
before do
|
221
225
|
Her::API.setup :url => "https://api.example.com" do |builder|
|
222
226
|
builder.use Her::Middleware::FirstLevelParseJSON
|
223
227
|
builder.use Faraday::Request::UrlEncoded
|
224
228
|
builder.adapter :test do |stub|
|
225
|
-
stub.get("/users/1") { |env| [200, {}, { :id => 1, :name => "Tobias Fünke", :organization => { :id => 1, :name => "Bluth Company" }, :organization_id => 1 }.to_json] }
|
229
|
+
stub.get("/users/1") { |env| [200, {}, { :id => 1, :name => "Tobias Fünke", :organization => { :id => 1, :name => "Bluth Company Inc." }, :organization_id => 1 }.to_json] }
|
226
230
|
stub.get("/users/2") { |env| [200, {}, { :id => 2, :name => "Lindsay Fünke", :organization_id => 1 }.to_json] }
|
227
|
-
stub.get("/users/3") { |env| [200, {}, { :id => 2, :name => "Lindsay Fünke", :
|
231
|
+
stub.get("/users/3") { |env| [200, {}, { :id => 2, :name => "Lindsay Fünke", :company => nil }.to_json] }
|
228
232
|
stub.get("/companies/1") { |env| [200, {}, { :id => 1, :name => "Bluth Company" }.to_json] }
|
229
233
|
end
|
230
234
|
end
|
231
235
|
|
232
236
|
spawn_model "Foo::User" do
|
233
|
-
belongs_to :company, :path => "/organizations/:id", :foreign_key => :organization_id
|
237
|
+
belongs_to :company, :path => "/organizations/:id", :foreign_key => :organization_id, :data_key => :organization
|
234
238
|
end
|
235
239
|
|
236
240
|
spawn_model "Foo::Company"
|
@@ -243,11 +247,11 @@ describe Her::Model::Relationships do
|
|
243
247
|
it "maps an array of included data through belongs_to" do
|
244
248
|
@user_with_included_data.company.should be_a(Foo::Company)
|
245
249
|
@user_with_included_data.company.id.should == 1
|
246
|
-
@user_with_included_data.company.name.should == "Bluth Company"
|
250
|
+
@user_with_included_data.company.name.should == "Bluth Company Inc."
|
247
251
|
end
|
248
252
|
|
249
253
|
it "does not map included data if it’s nil" do
|
250
|
-
@user_with_included_nil_data.
|
254
|
+
@user_with_included_nil_data.company.should be_nil
|
251
255
|
end
|
252
256
|
|
253
257
|
it "fetches data that was not included through belongs_to" do
|
@@ -2,7 +2,7 @@
|
|
2
2
|
require File.join(File.dirname(__FILE__), "../spec_helper.rb")
|
3
3
|
|
4
4
|
describe Her::Model::NestedAttributes do
|
5
|
-
context "with a belongs_to
|
5
|
+
context "with a belongs_to association" do
|
6
6
|
before do
|
7
7
|
Her::API.setup :url => "https://api.example.com" do |builder|
|
8
8
|
builder.use Her::Middleware::FirstLevelParseJSON
|
@@ -35,7 +35,7 @@ describe Her::Model::NestedAttributes do
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
context "with a has_one
|
38
|
+
context "with a has_one association" do
|
39
39
|
before do
|
40
40
|
Her::API.setup :url => "https://api.example.com" do |builder|
|
41
41
|
builder.use Her::Middleware::FirstLevelParseJSON
|
@@ -68,7 +68,7 @@ describe Her::Model::NestedAttributes do
|
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
-
context "with a has_many
|
71
|
+
context "with a has_many association" do
|
72
72
|
before do
|
73
73
|
Her::API.setup :url => "https://api.example.com" do |builder|
|
74
74
|
builder.use Her::Middleware::FirstLevelParseJSON
|
data/spec/model/orm_spec.rb
CHANGED
@@ -145,7 +145,7 @@ describe Her::Model::ORM do
|
|
145
145
|
builder.use Faraday::Request::UrlEncoded
|
146
146
|
builder.adapter :test do |stub|
|
147
147
|
stub.get("/users/1") { |env| [200, {}, { :id => 1, :friends => ["Maeby", "GOB", "Anne"] }.to_json] }
|
148
|
-
stub.get("/users/2") { |env| [200, {}, { :id => 1
|
148
|
+
stub.get("/users/2") { |env| [200, {}, { :id => 1 }.to_json] }
|
149
149
|
end
|
150
150
|
end
|
151
151
|
|
@@ -155,16 +155,11 @@ describe Her::Model::ORM do
|
|
155
155
|
|
156
156
|
def friends=(val)
|
157
157
|
val = val.gsub("\r", "").split("\n").map { |friend| friend.gsub(/^\s*\*\s*/, "") } if val and val.is_a?(String)
|
158
|
-
@
|
158
|
+
@attributes[:friends] = val
|
159
159
|
end
|
160
160
|
|
161
161
|
def friends
|
162
|
-
@
|
163
|
-
end
|
164
|
-
|
165
|
-
# Why would anybody want to do this? I don’t know.
|
166
|
-
def organization=(organization)
|
167
|
-
@data[:organization] = { :foo => :bar }
|
162
|
+
@attributes[:friends].map { |friend| "* #{friend}" }.join("\n")
|
168
163
|
end
|
169
164
|
end
|
170
165
|
end
|
@@ -173,21 +168,16 @@ describe Her::Model::ORM do
|
|
173
168
|
@user = User.find(1)
|
174
169
|
@user.friends.should == "* Maeby\n* GOB\n* Anne"
|
175
170
|
@user.instance_eval do
|
176
|
-
@
|
171
|
+
@attributes[:friends] = ["Maeby", "GOB", "Anne"]
|
177
172
|
end
|
178
173
|
end
|
179
174
|
|
180
|
-
it "handles custom setters with relationships" do
|
181
|
-
@user = User.find(2)
|
182
|
-
@user.organization.should == { :foo => :bar }
|
183
|
-
end
|
184
|
-
|
185
175
|
it "handles custom getters" do
|
186
176
|
@user = User.new
|
187
177
|
@user.friends = "* George\n* Oscar\n* Lucille"
|
188
178
|
@user.friends.should == "* George\n* Oscar\n* Lucille"
|
189
179
|
@user.instance_eval do
|
190
|
-
@
|
180
|
+
@attributes[:friends] = ["George", "Oscar", "Lucille"]
|
191
181
|
end
|
192
182
|
end
|
193
183
|
end
|
data/spec/model/paths_spec.rb
CHANGED
@@ -61,7 +61,7 @@ describe Her::Model::Paths do
|
|
61
61
|
|
62
62
|
it "raises exceptions when building a path without required custom variables" do
|
63
63
|
Foo::User.collection_path "/organizations/:organization_id/utilisateurs"
|
64
|
-
expect { Foo::User.build_request_path(:id => "foo") }.to raise_error(Her::Errors::PathError)
|
64
|
+
expect { Foo::User.build_request_path(:id => "foo") }.to raise_error(Her::Errors::PathError, "Missing :_organization_id parameter to build the request path. Path is `/organizations/:organization_id/utilisateurs/:id`. Parameters are `{:id=>\"foo\"}`.")
|
65
65
|
end
|
66
66
|
end
|
67
67
|
end
|
@@ -115,12 +115,12 @@ describe Her::Model::Paths do
|
|
115
115
|
|
116
116
|
it "raises exceptions when building a path without required custom variables" do
|
117
117
|
Foo::AdminUser.collection_path "/organizations/:organization_id/users"
|
118
|
-
expect { Foo::AdminUser.build_request_path(:id => "foo") }.to raise_error(Her::Errors::PathError)
|
118
|
+
expect { Foo::AdminUser.build_request_path(:id => "foo") }.to raise_error(Her::Errors::PathError, "Missing :_organization_id parameter to build the request path. Path is `/organizations/:organization_id/users/:id`. Parameters are `{:id=>\"foo\"}`.")
|
119
119
|
end
|
120
120
|
|
121
121
|
it "raises exceptions when building a relative path without required custom variables" do
|
122
122
|
Foo::AdminUser.collection_path "organizations/:organization_id/users"
|
123
|
-
expect { Foo::AdminUser.build_request_path(:id => "foo") }.to raise_error(Her::Errors::PathError)
|
123
|
+
expect { Foo::AdminUser.build_request_path(:id => "foo") }.to raise_error(Her::Errors::PathError, "Missing :_organization_id parameter to build the request path. Path is `organizations/:organization_id/users/:id`. Parameters are `{:id=>\"foo\"}`.")
|
124
124
|
end
|
125
125
|
end
|
126
126
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: her
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rémi Prévost
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-03-
|
11
|
+
date: 2013-03-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -142,19 +142,20 @@ files:
|
|
142
142
|
- lib/her/middleware/first_level_parse_json.rb
|
143
143
|
- lib/her/middleware/second_level_parse_json.rb
|
144
144
|
- lib/her/model.rb
|
145
|
+
- lib/her/model/associations.rb
|
145
146
|
- lib/her/model/base.rb
|
146
147
|
- lib/her/model/http.rb
|
147
148
|
- lib/her/model/introspection.rb
|
148
149
|
- lib/her/model/nested_attributes.rb
|
149
150
|
- lib/her/model/orm.rb
|
150
151
|
- lib/her/model/paths.rb
|
151
|
-
- lib/her/model/relationships.rb
|
152
152
|
- lib/her/version.rb
|
153
153
|
- spec/api_spec.rb
|
154
154
|
- spec/collection_spec.rb
|
155
155
|
- spec/middleware/accept_json_spec.rb
|
156
156
|
- spec/middleware/first_level_parse_json_spec.rb
|
157
157
|
- spec/middleware/second_level_parse_json_spec.rb
|
158
|
+
- spec/model/associations_spec.rb
|
158
159
|
- spec/model/callbacks_spec.rb
|
159
160
|
- spec/model/dirty_spec.rb
|
160
161
|
- spec/model/http_spec.rb
|
@@ -162,7 +163,6 @@ files:
|
|
162
163
|
- spec/model/nested_attributes_spec.rb
|
163
164
|
- spec/model/orm_spec.rb
|
164
165
|
- spec/model/paths_spec.rb
|
165
|
-
- spec/model/relationships_spec.rb
|
166
166
|
- spec/model/validations_spec.rb
|
167
167
|
- spec/model_spec.rb
|
168
168
|
- spec/spec_helper.rb
|
@@ -197,6 +197,7 @@ test_files:
|
|
197
197
|
- spec/middleware/accept_json_spec.rb
|
198
198
|
- spec/middleware/first_level_parse_json_spec.rb
|
199
199
|
- spec/middleware/second_level_parse_json_spec.rb
|
200
|
+
- spec/model/associations_spec.rb
|
200
201
|
- spec/model/callbacks_spec.rb
|
201
202
|
- spec/model/dirty_spec.rb
|
202
203
|
- spec/model/http_spec.rb
|
@@ -204,7 +205,6 @@ test_files:
|
|
204
205
|
- spec/model/nested_attributes_spec.rb
|
205
206
|
- spec/model/orm_spec.rb
|
206
207
|
- spec/model/paths_spec.rb
|
207
|
-
- spec/model/relationships_spec.rb
|
208
208
|
- spec/model/validations_spec.rb
|
209
209
|
- spec/model_spec.rb
|
210
210
|
- spec/spec_helper.rb
|