her 0.3.5 → 0.3.6
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|