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 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: