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 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
- class TokenAuthentication < Faraday::Middleware
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"] = @options[:token] if @options.include?(: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
- Her::API.setup :url => "https://api.example.com" do |connection|
89
- # This token could be stored in the client session
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:
@@ -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
@@ -61,12 +61,12 @@ module Her
61
61
  end
62
62
 
63
63
  # Handles returning true for the accessible attributes
64
- def has_key?(attribute_name)
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 [](attribute_name)
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
@@ -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
- # Return @her_relationships, lazily initialized with copy of the
6
- # superclass' her_relationships, or an empty hash.
7
- #
8
- # @private
9
- def relationships
10
- @her_relationships ||= begin
11
- if superclass.respond_to?(:relationships)
12
- superclass.relationships.dup
13
- else
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
- # Parse relationships data after initializing a new object
20
- #
21
- # @private
22
- def parse_relationships(data)
23
- relationships.each_pair do |type, definitions|
24
- definitions.each do |relationship|
25
- name = relationship[:name]
26
- next unless data[name]
27
- klass = self.nearby_class(relationship[:class_name])
28
- data[name] = case type
29
- when :has_many
30
- Her::Model::ORM.initialize_collection(klass, :data => data[name])
31
- when :has_one, :belongs_to
32
- klass.new(data[name])
33
- else
34
- nil
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
- # Define an *has_many* relationship.
42
- #
43
- # @param [Symbol] name The name of the model
44
- # @param [Hash] attrs Options (currently not used)
45
- #
46
- # @example
47
- # class User
48
- # include Her::API
49
- # has_many :articles
50
- # end
51
- #
52
- # class Article
53
- # include Her::API
54
- # end
55
- #
56
- # @user = User.find(1)
57
- # @user.articles # => [#<Article(articles/2) id=2 title="Hello world.">]
58
- # # Fetched via GET "/users/1/articles"
59
- def has_many(name, attrs={})
60
- attrs = {
61
- :class_name => name.to_s.classify,
62
- :name => name,
63
- :path => "/#{name}"
64
- }.merge(attrs)
65
- (relationships[:has_many] ||= []) << attrs
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
- define_method(name) do |*method_attrs|
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
- # Define an *has_one* relationship.
79
- #
80
- # @param [Symbol] name The name of the model
81
- # @param [Hash] attrs Options (currently not used)
82
- #
83
- # @example
84
- # class User
85
- # include Her::API
86
- # has_one :organization
87
- # end
88
- #
89
- # class Organization
90
- # include Her::API
91
- # end
92
- #
93
- # @user = User.find(1)
94
- # @user.organization # => #<Organization(organizations/2) id=2 name="Foobar Inc.">
95
- # # Fetched via GET "/users/1/organization"
96
- def has_one(name, attrs={})
97
- attrs = {
98
- :class_name => name.to_s.classify,
99
- :name => name,
100
- :path => "/#{name}"
101
- }.merge(attrs)
102
- (relationships[:has_one] ||= []) << attrs
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
- define_method(name) do |*method_attrs|
105
- method_attrs = method_attrs[0] || {}
106
- klass = self.class.nearby_class(attrs[:class_name])
107
- if method_attrs.any?
108
- klass.get_resource("#{self.class.build_request_path(method_attrs.merge(:id => id))}#{attrs[:path]}")
109
- else
110
- @data[name] ||= klass.get_resource("#{self.class.build_request_path(:id => id)}#{attrs[:path]}")
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
- # Define a *belongs_to* relationship.
116
- #
117
- # @param [Symbol] name The name of the model
118
- # @param [Hash] attrs Options (currently not used)
119
- #
120
- # @example
121
- # class User
122
- # include Her::API
123
- # belongs_to :team, :class_name => "Group"
124
- # end
125
- #
126
- # class Group
127
- # include Her::API
128
- # end
129
- #
130
- # @user = User.find(1)
131
- # @user.team # => #<Team(teams/2) id=2 name="Developers">
132
- # # Fetched via GET "/teams/2"
133
- def belongs_to(name, attrs={})
134
- attrs = {
135
- :class_name => name.to_s.classify,
136
- :name => name,
137
- :foreign_key => "#{name}_id",
138
- :path => "/#{name.to_s.pluralize}/:id"
139
- }.merge(attrs)
140
- (relationships[:belongs_to] ||= []) << attrs
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
- define_method(name) do |*method_attrs|
143
- method_attrs = method_attrs[0] || {}
144
- klass = self.class.nearby_class(attrs[:class_name])
145
- if method_attrs.any?
146
- klass.get_resource("#{klass.build_request_path(method_attrs.merge(:id => @data[attrs[:foreign_key].to_sym]))}")
147
- else
148
- @data[name] ||= klass.get_resource("#{klass.build_request_path(:id => @data[attrs[:foreign_key].to_sym])}")
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
- # @private
154
- def relationship_accessor(type, attrs)
155
- name = attrs[:name]
156
- class_name = attrs[:class_name]
157
- define_method(name) do
158
- klass = self.class.nearby_class(attrs[:class_name])
159
- @data[name] ||= klass.get_resource("#{klass.build_request_path(attrs[:path], :id => @data[attrs[:foreign_key].to_sym])}")
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
@@ -1,3 +1,3 @@
1
1
  module Her
2
- VERSION = "0.3.5"
2
+ VERSION = "0.3.6"
3
3
  end
@@ -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 has_key for getter" do
84
+ it "handles has_data? for getter" do
85
85
  @new_user = Foo::User.new(:fullname => 'Mayonegg')
86
- @new_user.should_not have_key(:unknown_method_for_a_user)
87
- @new_user.should have_key(:fullname)
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 [] for getter" do
90
+ it "handles get_data for getter" do
91
91
  @new_user = Foo::User.new(:fullname => 'Mayonegg')
92
- @new_user[:unknown_method_for_a_user].should be_nil
93
- @new_user[:fullname].should == 'Mayonegg'
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
@@ -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
@@ -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.5
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-01 00:00:00.000000000 Z
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: -27727532175681908
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: -27727532175681908
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: