her 0.3.5 → 0.3.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/README.md +24 -7
- data/lib/her/model.rb +17 -1
- data/lib/her/model/orm.rb +4 -3
- data/lib/her/model/paths.rb +1 -1
- data/lib/her/model/relationships.rb +163 -136
- data/lib/her/version.rb +1 -1
- data/spec/model/orm_spec.rb +12 -6
- data/spec/model/paths_spec.rb +1 -0
- data/spec/model/relationships_spec.rb +31 -6
- data/spec/model_spec.rb +43 -0
- metadata +6 -4
data/README.md
CHANGED
@@ -70,25 +70,40 @@ Since Her relies on [Faraday](https://github.com/technoweenie/faraday) to send H
|
|
70
70
|
|
71
71
|
Her doesn’t support authentication by default. However, it’s easy to implement one with request middleware. Using the `connection` block, we can add it to the middleware stack.
|
72
72
|
|
73
|
-
For example, to add a API token header to your requests, you would do something like this:
|
73
|
+
For example, to add a API token header to your requests in a Rails application, you would do something like this:
|
74
74
|
|
75
75
|
```ruby
|
76
|
-
|
76
|
+
# app/controllers/application_controller.rb
|
77
|
+
class ApplicationController < ActionController::Base
|
78
|
+
around_filter :do_with_authenticated_user
|
79
|
+
|
80
|
+
def as_authenticated_user
|
81
|
+
Thread.current[:my_api_token] = session[:my_api_token]
|
82
|
+
begin
|
83
|
+
yield
|
84
|
+
ensure
|
85
|
+
Thread.current[:my_access_token] = nil
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# lib/my_token_authentication.rb
|
91
|
+
class MyTokenAuthentication < Faraday::Middleware
|
77
92
|
def initialize(app, options={})
|
78
93
|
@app = app
|
79
|
-
@options = options
|
80
94
|
end
|
81
95
|
|
82
96
|
def call(env)
|
83
|
-
env[:request_headers]["X-API-Token"] =
|
97
|
+
env[:request_headers]["X-API-Token"] = Thread.current[:my_api_token] if Thread.current[:my_api_token].present?
|
84
98
|
@app.call(env)
|
85
99
|
end
|
86
100
|
end
|
87
101
|
|
88
|
-
|
89
|
-
|
90
|
-
connection.use TokenAuthentication, :token => "bb2b2dd75413d32c1ac421d39e95b978d1819ff611f68fc2fdd5c8b9c7331192"
|
102
|
+
# config/initializers/her.rb
|
103
|
+
require "lib/my_token_authentication"
|
91
104
|
|
105
|
+
Her::API.setup :url => "https://api.example.com" do |connection|
|
106
|
+
connection.use MyTokenAuthentication
|
92
107
|
connection.use Faraday::Request::UrlEncoded
|
93
108
|
connection.use Her::Middleware::DefaultParseJSON
|
94
109
|
connection.use Faraday::Adapter::NetHttp
|
@@ -177,6 +192,8 @@ end
|
|
177
192
|
@tweets = Tweet.get("/statuses/home_timeline.json")
|
178
193
|
```
|
179
194
|
|
195
|
+
See the *Authentication* middleware section for an example of how to pass different credentials based on the current user.
|
196
|
+
|
180
197
|
### Caching
|
181
198
|
|
182
199
|
Again, using the `faraday_middleware` makes it very easy to cache requests and responses:
|
data/lib/her/model.rb
CHANGED
@@ -24,12 +24,12 @@ module Her
|
|
24
24
|
include Her::Model::ORM
|
25
25
|
include Her::Model::Introspection
|
26
26
|
include Her::Model::Paths
|
27
|
+
include Her::Model::Relationships
|
27
28
|
|
28
29
|
# Class methods
|
29
30
|
included do
|
30
31
|
extend Her::Model::Base
|
31
32
|
extend Her::Model::HTTP
|
32
|
-
extend Her::Model::Relationships
|
33
33
|
extend Her::Model::Hooks
|
34
34
|
|
35
35
|
# Define default settings
|
@@ -38,5 +38,21 @@ module Her
|
|
38
38
|
resource_path "#{base_path}/:id"
|
39
39
|
uses_api Her::API.default_api
|
40
40
|
end
|
41
|
+
|
42
|
+
# Returns true if attribute_name is
|
43
|
+
# * in orm data
|
44
|
+
# * a relationship
|
45
|
+
def has_key?(attribute_name)
|
46
|
+
has_data?(attribute_name) ||
|
47
|
+
has_relationship?(attribute_name)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns
|
51
|
+
# * the value of the attribute_nane attribute if it's in orm data
|
52
|
+
# * the resource/collection corrsponding to attribute_name if it's a relationship
|
53
|
+
def [](attribute_name)
|
54
|
+
get_data(attribute_name) ||
|
55
|
+
get_relationship(attribute_name)
|
56
|
+
end
|
41
57
|
end
|
42
58
|
end
|
data/lib/her/model/orm.rb
CHANGED
@@ -61,12 +61,12 @@ module Her
|
|
61
61
|
end
|
62
62
|
|
63
63
|
# Handles returning true for the accessible attributes
|
64
|
-
def
|
64
|
+
def has_data?(attribute_name)
|
65
65
|
@data.include?(attribute_name)
|
66
66
|
end
|
67
67
|
|
68
68
|
# Handles returning attribute value from data
|
69
|
-
def
|
69
|
+
def get_data(attribute_name)
|
70
70
|
@data[attribute_name]
|
71
71
|
end
|
72
72
|
|
@@ -134,7 +134,7 @@ module Her
|
|
134
134
|
|
135
135
|
self.class.wrap_in_hooks(resource, *hooks) do |resource, klass|
|
136
136
|
klass.request(params.merge(:_method => method, :_path => "#{request_path}")) do |parsed_data|
|
137
|
-
self.data = parsed_data[:data]
|
137
|
+
self.data = parsed_data[:data] if parsed_data[:data].any?
|
138
138
|
self.metadata = parsed_data[:metadata]
|
139
139
|
self.errors = parsed_data[:errors]
|
140
140
|
|
@@ -242,6 +242,7 @@ module Her
|
|
242
242
|
def save_existing(id, params)
|
243
243
|
resource = new(params.merge(:id => id))
|
244
244
|
resource.save
|
245
|
+
resource
|
245
246
|
end
|
246
247
|
|
247
248
|
# Destroy an existing resource
|
data/lib/her/model/paths.rb
CHANGED
@@ -61,7 +61,7 @@ module Her
|
|
61
61
|
def build_request_path(path=nil, parameters={})
|
62
62
|
unless path.is_a?(String)
|
63
63
|
parameters = path || {}
|
64
|
-
path = parameters.include?(:id) ? resource_path : collection_path
|
64
|
+
path = parameters.include?(:id) && !parameters[:id].nil? ? resource_path : collection_path
|
65
65
|
end
|
66
66
|
|
67
67
|
path.gsub(/:([\w_]+)/) do
|
@@ -2,161 +2,188 @@ module Her
|
|
2
2
|
module Model
|
3
3
|
# This module adds relationships to models
|
4
4
|
module Relationships
|
5
|
-
|
6
|
-
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
# Returns true if the model has a relationship_name relationship, false otherwise.
|
8
|
+
def has_relationship?(relationship_name)
|
9
|
+
relationships = self.class.relationships.values.flatten.map { |r| r[:name] }
|
10
|
+
relationships.include?(relationship_name)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns the resource/collection corresponding to the relationship_name relationship.
|
14
|
+
def get_relationship(relationship_name)
|
15
|
+
send(relationship_name) if has_relationship?(relationship_name)
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
# Return @her_relationships, lazily initialized with copy of the
|
20
|
+
# superclass' her_relationships, or an empty hash.
|
21
|
+
#
|
22
|
+
# @private
|
23
|
+
def relationships
|
24
|
+
@her_relationships ||= begin
|
25
|
+
if superclass.respond_to?(:relationships)
|
26
|
+
superclass.relationships.dup
|
27
|
+
else
|
28
|
+
{}
|
29
|
+
end
|
15
30
|
end
|
16
31
|
end
|
17
|
-
end
|
18
32
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
# Parse relationships data after initializing a new object
|
34
|
+
#
|
35
|
+
# @private
|
36
|
+
def parse_relationships(data)
|
37
|
+
relationships.each_pair do |type, definitions|
|
38
|
+
definitions.each do |relationship|
|
39
|
+
name = relationship[:name]
|
40
|
+
next unless data[name]
|
41
|
+
klass = self.nearby_class(relationship[:class_name])
|
42
|
+
data[name] = case type
|
43
|
+
when :has_many
|
44
|
+
Her::Model::ORM.initialize_collection(klass, :data => data[name])
|
45
|
+
when :has_one, :belongs_to
|
46
|
+
klass.new(data[name])
|
47
|
+
else
|
48
|
+
nil
|
49
|
+
end
|
35
50
|
end
|
36
51
|
end
|
52
|
+
data
|
37
53
|
end
|
38
|
-
data
|
39
|
-
end
|
40
54
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
55
|
+
# Define an *has_many* relationship.
|
56
|
+
#
|
57
|
+
# @param [Symbol] name The name of the model
|
58
|
+
# @param [Hash] attrs Options (currently not used)
|
59
|
+
#
|
60
|
+
# @example
|
61
|
+
# class User
|
62
|
+
# include Her::API
|
63
|
+
# has_many :articles
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# class Article
|
67
|
+
# include Her::API
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# @user = User.find(1)
|
71
|
+
# @user.articles # => [#<Article(articles/2) id=2 title="Hello world.">]
|
72
|
+
# # Fetched via GET "/users/1/articles"
|
73
|
+
def has_many(name, attrs={})
|
74
|
+
attrs = {
|
75
|
+
:class_name => name.to_s.classify,
|
76
|
+
:name => name,
|
77
|
+
:path => "/#{name}",
|
78
|
+
:inverse_of => nil
|
79
|
+
}.merge(attrs)
|
80
|
+
(relationships[:has_many] ||= []) << attrs
|
81
|
+
|
82
|
+
define_method(name) do |*method_attrs|
|
83
|
+
method_attrs = method_attrs[0] || {}
|
84
|
+
klass = self.class.nearby_class(attrs[:class_name])
|
85
|
+
if method_attrs.any?
|
86
|
+
@data[name] = klass.get_collection("#{self.class.build_request_path(method_attrs.merge(:id => id))}#{attrs[:path]}")
|
87
|
+
else
|
88
|
+
@data[name] ||= klass.get_collection("#{self.class.build_request_path(:id => id)}#{attrs[:path]}")
|
89
|
+
end
|
90
|
+
|
91
|
+
inverse_of = if attrs[:inverse_of]
|
92
|
+
attrs[:inverse_of]
|
93
|
+
else
|
94
|
+
self.class.name.split('::').last.tableize.singularize
|
95
|
+
end
|
96
|
+
@data[name].each do |entry|
|
97
|
+
entry.send("#{inverse_of}=", self)
|
98
|
+
end
|
66
99
|
|
67
|
-
|
68
|
-
method_attrs = method_attrs[0] || {}
|
69
|
-
klass = self.class.nearby_class(attrs[:class_name])
|
70
|
-
if method_attrs.any?
|
71
|
-
klass.get_collection("#{self.class.build_request_path(method_attrs.merge(:id => id))}#{attrs[:path]}")
|
72
|
-
else
|
73
|
-
@data[name] ||= klass.get_collection("#{self.class.build_request_path(:id => id)}#{attrs[:path]}")
|
100
|
+
@data[name]
|
74
101
|
end
|
75
102
|
end
|
76
|
-
end
|
77
103
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
104
|
+
# Define an *has_one* relationship.
|
105
|
+
#
|
106
|
+
# @param [Symbol] name The name of the model
|
107
|
+
# @param [Hash] attrs Options (currently not used)
|
108
|
+
#
|
109
|
+
# @example
|
110
|
+
# class User
|
111
|
+
# include Her::API
|
112
|
+
# has_one :organization
|
113
|
+
# end
|
114
|
+
#
|
115
|
+
# class Organization
|
116
|
+
# include Her::API
|
117
|
+
# end
|
118
|
+
#
|
119
|
+
# @user = User.find(1)
|
120
|
+
# @user.organization # => #<Organization(organizations/2) id=2 name="Foobar Inc.">
|
121
|
+
# # Fetched via GET "/users/1/organization"
|
122
|
+
def has_one(name, attrs={})
|
123
|
+
attrs = {
|
124
|
+
:class_name => name.to_s.classify,
|
125
|
+
:name => name,
|
126
|
+
:path => "/#{name}"
|
127
|
+
}.merge(attrs)
|
128
|
+
(relationships[:has_one] ||= []) << attrs
|
103
129
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
130
|
+
define_method(name) do |*method_attrs|
|
131
|
+
method_attrs = method_attrs[0] || {}
|
132
|
+
klass = self.class.nearby_class(attrs[:class_name])
|
133
|
+
if method_attrs.any?
|
134
|
+
klass.get_resource("#{self.class.build_request_path(method_attrs.merge(:id => id))}#{attrs[:path]}")
|
135
|
+
else
|
136
|
+
@data[name] ||= klass.get_resource("#{self.class.build_request_path(:id => id)}#{attrs[:path]}")
|
137
|
+
end
|
111
138
|
end
|
112
139
|
end
|
113
|
-
end
|
114
140
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
+
# Define a *belongs_to* relationship.
|
142
|
+
#
|
143
|
+
# @param [Symbol] name The name of the model
|
144
|
+
# @param [Hash] attrs Options (currently not used)
|
145
|
+
#
|
146
|
+
# @example
|
147
|
+
# class User
|
148
|
+
# include Her::API
|
149
|
+
# belongs_to :team, :class_name => "Group"
|
150
|
+
# end
|
151
|
+
#
|
152
|
+
# class Group
|
153
|
+
# include Her::API
|
154
|
+
# end
|
155
|
+
#
|
156
|
+
# @user = User.find(1)
|
157
|
+
# @user.team # => #<Team(teams/2) id=2 name="Developers">
|
158
|
+
# # Fetched via GET "/teams/2"
|
159
|
+
def belongs_to(name, attrs={})
|
160
|
+
attrs = {
|
161
|
+
:class_name => name.to_s.classify,
|
162
|
+
:name => name,
|
163
|
+
:foreign_key => "#{name}_id",
|
164
|
+
:path => "/#{name.to_s.pluralize}/:id"
|
165
|
+
}.merge(attrs)
|
166
|
+
(relationships[:belongs_to] ||= []) << attrs
|
141
167
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
168
|
+
define_method(name) do |*method_attrs|
|
169
|
+
method_attrs = method_attrs[0] || {}
|
170
|
+
klass = self.class.nearby_class(attrs[:class_name])
|
171
|
+
if method_attrs.any?
|
172
|
+
klass.get_resource("#{klass.build_request_path(method_attrs.merge(:id => @data[attrs[:foreign_key].to_sym]))}")
|
173
|
+
else
|
174
|
+
@data[name] ||= klass.get_resource("#{klass.build_request_path(:id => @data[attrs[:foreign_key].to_sym])}")
|
175
|
+
end
|
149
176
|
end
|
150
177
|
end
|
151
|
-
end
|
152
178
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
179
|
+
# @private
|
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])}")
|
186
|
+
end
|
160
187
|
end
|
161
188
|
end
|
162
189
|
end
|
data/lib/her/version.rb
CHANGED
data/spec/model/orm_spec.rb
CHANGED
@@ -81,16 +81,16 @@ describe Her::Model::ORM do
|
|
81
81
|
@new_user.should respond_to(:fullname?)
|
82
82
|
end
|
83
83
|
|
84
|
-
it "handles
|
84
|
+
it "handles has_data? for getter" do
|
85
85
|
@new_user = Foo::User.new(:fullname => 'Mayonegg')
|
86
|
-
@new_user.should_not
|
87
|
-
@new_user.should
|
86
|
+
@new_user.should_not have_data(:unknown_method_for_a_user)
|
87
|
+
@new_user.should have_data(:fullname)
|
88
88
|
end
|
89
89
|
|
90
|
-
it "handles
|
90
|
+
it "handles get_data for getter" do
|
91
91
|
@new_user = Foo::User.new(:fullname => 'Mayonegg')
|
92
|
-
@new_user
|
93
|
-
@new_user
|
92
|
+
@new_user.get_data(:unknown_method_for_a_user).should be_nil
|
93
|
+
@new_user.get_data(:fullname).should == 'Mayonegg'
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
@@ -272,6 +272,12 @@ describe Her::Model::ORM do
|
|
272
272
|
@company = Foo::Company.new
|
273
273
|
@company.save.should be_false
|
274
274
|
end
|
275
|
+
|
276
|
+
it "don't overwrite data if response is empty" do
|
277
|
+
@company = Foo::Company.new(:name => 'Company Inc.')
|
278
|
+
@company.save.should be_false
|
279
|
+
@company.name.should == "Company Inc."
|
280
|
+
end
|
275
281
|
end
|
276
282
|
|
277
283
|
context "updating resources" do
|
data/spec/model/paths_spec.rb
CHANGED
@@ -11,6 +11,7 @@ describe Her::Model::Paths do
|
|
11
11
|
describe "#build_request_path" do
|
12
12
|
it "builds paths with defaults" do
|
13
13
|
Foo::User.build_request_path(:id => "foo").should == "users/foo"
|
14
|
+
Foo::User.build_request_path(:id => nil).should == "users"
|
14
15
|
Foo::User.build_request_path.should == "users"
|
15
16
|
end
|
16
17
|
|
@@ -9,13 +9,13 @@ describe Her::Model::Relationships do
|
|
9
9
|
|
10
10
|
it "handles a single 'has_many' relationship" do
|
11
11
|
Foo::User.has_many :comments
|
12
|
-
Foo::User.relationships[:has_many].should == [{ :name => :comments, :class_name => "Comment", :path => "/comments" }]
|
12
|
+
Foo::User.relationships[:has_many].should == [{ :name => :comments, :class_name => "Comment", :path => "/comments", :inverse_of => nil }]
|
13
13
|
end
|
14
14
|
|
15
15
|
it "handles multiples 'has_many' relationship" do
|
16
16
|
Foo::User.has_many :comments
|
17
17
|
Foo::User.has_many :posts
|
18
|
-
Foo::User.relationships[:has_many].should == [{ :name => :comments, :class_name => "Comment", :path => "/comments" }, { :name => :posts, :class_name => "Post", :path => "/posts" }]
|
18
|
+
Foo::User.relationships[:has_many].should == [{ :name => :comments, :class_name => "Comment", :path => "/comments", :inverse_of => nil }, { :name => :posts, :class_name => "Post", :path => "/posts", :inverse_of => nil }]
|
19
19
|
end
|
20
20
|
|
21
21
|
it "handles a single 'has_one' relationship" do
|
@@ -47,8 +47,8 @@ describe Her::Model::Relationships do
|
|
47
47
|
end
|
48
48
|
|
49
49
|
it "handles a single 'has_many' relationship" do
|
50
|
-
Foo::User.has_many :comments, :class_name => "Post"
|
51
|
-
Foo::User.relationships[:has_many].should == [{ :name => :comments, :class_name => "Post", :path => "/comments" }]
|
50
|
+
Foo::User.has_many :comments, :class_name => "Post", :inverse_of => :admin
|
51
|
+
Foo::User.relationships[:has_many].should == [{ :name => :comments, :class_name => "Post", :path => "/comments", :inverse_of => :admin }]
|
52
52
|
end
|
53
53
|
|
54
54
|
it "handles a single 'has_one' relationship" do
|
@@ -78,12 +78,13 @@ describe Her::Model::Relationships do
|
|
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!" }, { :id => 3, :body => "I wouldn't mind kissing that man between the cheeks, so to speak" }], :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 => [{ :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] }
|
82
82
|
stub.get("/users/2") { |env| [200, {}, { :id => 2, :name => "Lindsay Fünke", :organization_id => 2 }.to_json] }
|
83
83
|
stub.get("/users/1/comments") { |env| [200, {}, [{ :id => 4, :body => "They're having a FIRESALE?" }].to_json] }
|
84
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] }
|
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
|
+
stub.get("/users/1/posts") { |env| [200, {}, {:id => 1, :body => 'blogging stuff', :admin_id => 1 }.to_json] }
|
87
88
|
stub.get("/organizations/1") { |env| [200, {}, { :id => 1, :name => "Bluth Company Foo" }.to_json] }
|
88
89
|
stub.get("/organizations/2") { |env| [200, {}, { :id => 2, :name => "Bluth Company" }.to_json] }
|
89
90
|
end
|
@@ -93,10 +94,16 @@ describe Her::Model::Relationships do
|
|
93
94
|
has_many :comments
|
94
95
|
has_one :role
|
95
96
|
belongs_to :organization
|
97
|
+
has_many :posts, :inverse_of => :admin
|
98
|
+
end
|
99
|
+
spawn_model "Foo::Comment" do
|
100
|
+
belongs_to :user
|
101
|
+
end
|
102
|
+
spawn_model "Foo::Post" do
|
103
|
+
belongs_to :admin, :class_name => 'Foo::User'
|
96
104
|
end
|
97
105
|
|
98
106
|
spawn_model "Foo::Organization"
|
99
|
-
spawn_model "Foo::Comment"
|
100
107
|
spawn_model "Foo::Role"
|
101
108
|
|
102
109
|
@user_with_included_data = Foo::User.find(1)
|
@@ -110,6 +117,14 @@ describe Her::Model::Relationships do
|
|
110
117
|
@user_with_included_data.comments.first.body.should == "Tobias, you blow hard!"
|
111
118
|
end
|
112
119
|
|
120
|
+
it "does not refetch the parents models data if they have been fetched before" do
|
121
|
+
@user_with_included_data.comments.first.user.object_id.should == @user_with_included_data.object_id
|
122
|
+
end
|
123
|
+
|
124
|
+
it "uses the given inverse_of key to set the parent model" do
|
125
|
+
@user_with_included_data.posts.first.admin.object_id.should == @user_with_included_data.object_id
|
126
|
+
end
|
127
|
+
|
113
128
|
it "fetches data that was not included through has_many" do
|
114
129
|
@user_without_included_data.comments.first.should be_a(Foo::Comment)
|
115
130
|
@user_without_included_data.comments.length.should == 2
|
@@ -152,6 +167,16 @@ describe Her::Model::Relationships do
|
|
152
167
|
it "fetches belongs_to data even if it was included, only if called with parameters" do
|
153
168
|
@user_with_included_data.organization(:foo_id => 1).name.should == "Bluth Company Foo"
|
154
169
|
end
|
170
|
+
|
171
|
+
it "can tell if it has a relationship" do
|
172
|
+
@user_without_included_data.has_relationship?(:unknown_relationship).should be_false
|
173
|
+
@user_without_included_data.has_relationship?(:organization).should be_true
|
174
|
+
end
|
175
|
+
|
176
|
+
it "fetches the resource corresponding to a named relationship" do
|
177
|
+
@user_without_included_data.get_relationship(:unknown_relationship).should be_nil
|
178
|
+
@user_without_included_data.get_relationship(:organization).name.should == "Bluth Company"
|
179
|
+
end
|
155
180
|
end
|
156
181
|
|
157
182
|
context "handling relationships with details" do
|
data/spec/model_spec.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Her::Model do
|
5
|
+
before do
|
6
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
7
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
8
|
+
builder.use Faraday::Request::UrlEncoded
|
9
|
+
builder.adapter :test do |stub|
|
10
|
+
stub.get("/users/1") { |env| [200, {}, { :id => 1, :name => "Tobias Fünke" }.to_json] }
|
11
|
+
stub.get("/users/1/comments") { |env| [200, {}, [{ :id => 4, :body => "They're having a FIRESALE?" }].to_json] }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
spawn_model "Foo::User" do
|
16
|
+
has_many :comments
|
17
|
+
end
|
18
|
+
|
19
|
+
spawn_model "Foo::Comment"
|
20
|
+
|
21
|
+
@user_without_included_data = Foo::User.find(1)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "handles has_key? for data" do
|
25
|
+
@user_without_included_data.should_not have_key(:unknown_method_for_a_user)
|
26
|
+
@user_without_included_data.should have_key(:name)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "handles has_key? for relationships" do
|
30
|
+
@user_without_included_data.should_not have_key(:unknown_method_for_a_user)
|
31
|
+
@user_without_included_data.should have_key(:comments)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "handles [] for data" do
|
35
|
+
@user_without_included_data[:unknown_method_for_a_user].should be_nil
|
36
|
+
@user_without_included_data[:name].should == "Tobias Fünke"
|
37
|
+
end
|
38
|
+
|
39
|
+
it "handles [] for relationships" do
|
40
|
+
@user_without_included_data[:unknown_relationship].should be_nil
|
41
|
+
@user_without_included_data[:comments].first.body.should == "They're having a FIRESALE?"
|
42
|
+
end
|
43
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: her
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-12-
|
12
|
+
date: 2012-12-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -255,6 +255,7 @@ files:
|
|
255
255
|
- spec/model/orm_spec.rb
|
256
256
|
- spec/model/paths_spec.rb
|
257
257
|
- spec/model/relationships_spec.rb
|
258
|
+
- spec/model_spec.rb
|
258
259
|
- spec/spec_helper.rb
|
259
260
|
homepage: http://remiprev.github.com/her
|
260
261
|
licenses:
|
@@ -271,7 +272,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
271
272
|
version: '0'
|
272
273
|
segments:
|
273
274
|
- 0
|
274
|
-
hash: -
|
275
|
+
hash: -1080420939636029022
|
275
276
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
276
277
|
none: false
|
277
278
|
requirements:
|
@@ -280,7 +281,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
280
281
|
version: '0'
|
281
282
|
segments:
|
282
283
|
- 0
|
283
|
-
hash: -
|
284
|
+
hash: -1080420939636029022
|
284
285
|
requirements: []
|
285
286
|
rubyforge_project:
|
286
287
|
rubygems_version: 1.8.23
|
@@ -299,5 +300,6 @@ test_files:
|
|
299
300
|
- spec/model/orm_spec.rb
|
300
301
|
- spec/model/paths_spec.rb
|
301
302
|
- spec/model/relationships_spec.rb
|
303
|
+
- spec/model_spec.rb
|
302
304
|
- spec/spec_helper.rb
|
303
305
|
has_rdoc:
|