her 0.2.4 → 0.2.5

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
@@ -179,22 +179,19 @@ gem "faraday_middleware"
179
179
  In your Ruby code:
180
180
 
181
181
  ```ruby
182
- class MyCache
183
- def initialize
184
- @cache = {}
185
- end
186
-
187
- def write(key, value)
188
- @cache[key] = value
182
+ class MyCache < Hash
183
+ def read(key)
184
+ if cached = self[key]
185
+ Marshal.load(cached)
186
+ end
189
187
  end
190
188
 
191
- def read(key)
192
- @cache[key]
189
+ def write(key, data)
190
+ self[key] = Marshal.dump(data)
193
191
  end
194
192
 
195
- def fetch(key, &block)
196
- return value = read(key) if value.nil?
197
- write key, yield
193
+ def fetch(key)
194
+ read(key) || yield.tap { |data| write(key, data) }
198
195
  end
199
196
  end
200
197
 
@@ -445,9 +442,22 @@ Category.all
445
442
  * Better error handling
446
443
  * Better API documentation (using YARD)
447
444
 
448
- ## Contributors
445
+ ## Contribute
449
446
 
450
- Feel free to contribute and submit issues/pull requests [on GitHub](https://github.com/remiprev/her/issues) like these fine folks did:
447
+ Yes please! Feel free to contribute and submit issues/pull requests [on GitHub](https://github.com/remiprev/her/issues).
448
+
449
+ ### How to contribute
450
+
451
+ * Fork the repository
452
+ * Implement your feature or fix
453
+ * Add examples that describe it (in the `spec` directory)
454
+ * Make sure `bundle exec rake spec` passes after your modifications
455
+ * Commit (bonus points for doing it in a `feature-*` branch)
456
+ * Send a pull request!
457
+
458
+ ### Contributors
459
+
460
+ These fine folks helped with Her:
451
461
 
452
462
  * [@jfcixmedia](https://github.com/jfcixmedia)
453
463
  * [@EtienneLem](https://github.com/EtienneLem)
@@ -455,8 +465,6 @@ Feel free to contribute and submit issues/pull requests [on GitHub](https://gith
455
465
  * [@tysontate](https://github.com/tysontate)
456
466
  * [@nfo](https://github.com/nfo)
457
467
 
458
- Take a look at the `spec` folder before you do, and make sure `bundle exec rake spec` passes after your modifications :)
459
-
460
468
  ## License
461
469
 
462
470
  Her is © 2012 [Rémi Prévost](http://exomel.com) and may be freely distributed under the [MIT license](https://github.com/remiprev/her/blob/master/LICENSE). See the `LICENSE` file.
data/UPGRADE.md CHANGED
@@ -4,27 +4,27 @@ Here is a list of backward-incompatible changes that were introduced while Her i
4
4
 
5
5
  * Her no longer includes default middleware when making HTTP requests. The user has now to define all the needed middleware. Before:
6
6
 
7
- Her::API.setup :base_uri => "https://api.example.com" do |builder|
8
- builder.insert(0, FaradayMiddle::OAuth)
9
- end
7
+ Her::API.setup :base_uri => "https://api.example.com" do |builder|
8
+ builder.insert(0, FaradayMiddle::OAuth)
9
+ end
10
10
 
11
11
  Now:
12
12
 
13
- Her::API.setup :base_uri => "https://api.example.com" do |builder|
14
- builder.use FaradayMiddle::OAuth
15
- builder.use Her::Middleware::FirstLevelParseJSON
16
- builder.use Faraday::Request::UrlEncoded
17
- builder.use Faraday::Adapter::NetHttp
18
- end
13
+ Her::API.setup :base_uri => "https://api.example.com" do |builder|
14
+ builder.use FaradayMiddle::OAuth
15
+ builder.use Her::Middleware::FirstLevelParseJSON
16
+ builder.use Faraday::Request::UrlEncoded
17
+ builder.use Faraday::Adapter::NetHttp
18
+ end
19
19
 
20
20
  ## 0.2
21
21
 
22
22
  * The default parser middleware has been replaced to treat first-level JSON data as the resource or collection data. Before it expected this:
23
23
 
24
- { "data": { "id": 1, "name": "Foo" }, "errors": [] }
24
+ { "data": { "id": 1, "name": "Foo" }, "errors": [] }
25
25
 
26
26
  Now it expects this (the `errors` key is not treated as resource data):
27
27
 
28
- { "id": 1, "name": "Foo", "errors": [] }
28
+ { "id": 1, "name": "Foo", "errors": [] }
29
29
 
30
30
  If you still want to get the old behavior, you can use `Her::Middleware::SecondLevelParseJSON` instead of `Her::Middleware::FirstLevelParseJSON` in your middleware stack.
@@ -12,22 +12,19 @@ class TwitterSearchParser < Faraday::Response::Middleware
12
12
  end
13
13
  end
14
14
 
15
- class MyCache
16
- def initialize
17
- @cache = {}
18
- end
19
-
20
- def write(key, value)
21
- @cache[key] = value
15
+ class MyCache < Hash
16
+ def read(key)
17
+ if cached = self[key]
18
+ Marshal.load(cached)
19
+ end
22
20
  end
23
21
 
24
- def read(key)
25
- @cache[key]
22
+ def write(key, data)
23
+ self[key] = Marshal.dump(data)
26
24
  end
27
25
 
28
- def fetch(key, &block)
29
- return value = read(key) if value.nil?
30
- write key, yield
26
+ def fetch(key)
27
+ read(key) || yield.tap { |data| write(key, data) }
31
28
  end
32
29
  end
33
30
 
@@ -35,8 +32,10 @@ $cache = MyCache.new
35
32
 
36
33
  # Initialize API
37
34
  Her::API.setup :base_uri => "http://search.twitter.com" do |builder|
38
- builder.swap Her::Middleware::FirstLevelParseJSON, TwitterSearchParser
35
+ builder.use Faraday::Request::UrlEncoded
39
36
  builder.use FaradayMiddleware::Caching, $cache
37
+ builder.use TwitterSearchParser
38
+ builder.use Faraday::Adapter::NetHttp
40
39
  end
41
40
 
42
41
  # Define classes
@@ -49,6 +48,6 @@ class Tweet
49
48
  end
50
49
 
51
50
  get "/" do
52
- @tweets = Tweet.search("github", :rpp => 30)
51
+ @tweets = Tweet.search("justin bieber", :rpp => 30)
53
52
  haml :index
54
53
  end
@@ -26,7 +26,7 @@ module Her
26
26
  path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
27
27
  get_raw(path, attrs) do |parsed_data|
28
28
  if parsed_data[:data].is_a?(Array)
29
- new_collection(parsed_data)
29
+ new_collection(parsed_data[:data])
30
30
  else
31
31
  new(parsed_data[:data])
32
32
  end
@@ -43,7 +43,7 @@ module Her
43
43
  def get_collection(path, attrs={}) # {{{
44
44
  path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
45
45
  get_raw(path, attrs) do |parsed_data|
46
- new_collection(parsed_data)
46
+ new_collection(parsed_data[:data])
47
47
  end
48
48
  end # }}}
49
49
 
@@ -60,7 +60,7 @@ module Her
60
60
  path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
61
61
  post_raw(path, attrs) do |parsed_data|
62
62
  if parsed_data[:data].is_a?(Array)
63
- new_collection(parsed_data)
63
+ new_collection(parsed_data[:data])
64
64
  else
65
65
  new(parsed_data[:data])
66
66
  end
@@ -77,7 +77,7 @@ module Her
77
77
  def post_collection(path, attrs={}) # {{{
78
78
  path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
79
79
  post_raw(path, attrs) do |parsed_data|
80
- new_collection(parsed_data)
80
+ new_collection(parsed_data[:data])
81
81
  end
82
82
  end # }}}
83
83
 
@@ -94,7 +94,7 @@ module Her
94
94
  path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
95
95
  put_raw(path, attrs) do |parsed_data|
96
96
  if parsed_data[:data].is_a?(Array)
97
- new_collection(parsed_data)
97
+ new_collection(parsed_data[:data])
98
98
  else
99
99
  new(parsed_data[:data])
100
100
  end
@@ -111,7 +111,7 @@ module Her
111
111
  def put_collection(path, attrs={}) # {{{
112
112
  path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
113
113
  put_raw(path, attrs) do |parsed_data|
114
- new_collection(parsed_data)
114
+ new_collection(parsed_data[:data])
115
115
  end
116
116
  end # }}}
117
117
 
@@ -128,7 +128,7 @@ module Her
128
128
  path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
129
129
  patch_raw(path, attrs) do |parsed_data|
130
130
  if parsed_data[:data].is_a?(Array)
131
- new_collection(parsed_data)
131
+ new_collection(parsed_data[:data])
132
132
  else
133
133
  new(parsed_data[:data])
134
134
  end
@@ -145,7 +145,7 @@ module Her
145
145
  def patch_collection(path, attrs={}) # {{{
146
146
  path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
147
147
  patch_raw(path, attrs) do |parsed_data|
148
- new_collection(parsed_data)
148
+ new_collection(parsed_data[:data])
149
149
  end
150
150
  end # }}}
151
151
 
@@ -162,7 +162,7 @@ module Her
162
162
  path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
163
163
  delete_raw(path, attrs) do |parsed_data|
164
164
  if parsed_data[:data].is_a?(Array)
165
- new_collection(parsed_data)
165
+ new_collection(parsed_data[:data])
166
166
  else
167
167
  new(parsed_data[:data])
168
168
  end
@@ -179,7 +179,7 @@ module Her
179
179
  def delete_collection(path, attrs={}) # {{{
180
180
  path = "#{build_request_path(attrs)}/#{path}" if path.is_a?(Symbol)
181
181
  delete_raw(path, attrs) do |parsed_data|
182
- new_collection(parsed_data)
182
+ new_collection(parsed_data[:data])
183
183
  end
184
184
  end # }}}
185
185
 
@@ -39,9 +39,8 @@ module Her
39
39
 
40
40
  # Initialize a collection of resources with raw data from an HTTP request
41
41
  #
42
- # @param [Hash] parsed_data The raw `parsed_data` parsed from the HTTP response
43
- def new_collection(parsed_data) # {{{
44
- collection_data = parsed_data[:data]
42
+ # @param [Array] collection_data An array of model hashes
43
+ def new_collection(collection_data) # {{{
45
44
  Her::Model::ORM.initialize_collection(self.to_s.underscore, collection_data)
46
45
  end # }}}
47
46
 
@@ -68,7 +67,7 @@ module Her
68
67
  # # Fetched via GET "/users"
69
68
  def all(params={}) # {{{
70
69
  request(params.merge(:_method => :get, :_path => "#{build_request_path(params)}")) do |parsed_data|
71
- new_collection(parsed_data)
70
+ new_collection(parsed_data[:data])
72
71
  end
73
72
  end # }}}
74
73
 
@@ -16,11 +16,11 @@ module Her
16
16
  relationships.each do |relationship|
17
17
  if data.include?(relationship[:name])
18
18
  if type == :has_many
19
- data[relationship[:name]] = Her::Model::ORM.initialize_collection(relationship[:name], data[relationship[:name]])
19
+ data[relationship[:name]] = Her::Model::ORM.initialize_collection(relationship[:class_name], data[relationship[:name]])
20
20
  elsif type == :has_one
21
- data[relationship[:name]] = Object.const_get(relationship[:name].to_s.classify).new(data[relationship[:name]])
21
+ data[relationship[:name]] = Object.const_get(relationship[:class_name]).new(data[relationship[:name]])
22
22
  elsif type == :belongs_to
23
- data[relationship[:name]] = Object.const_get(relationship[:name].to_s.classify).new(data[relationship[:name]])
23
+ data[relationship[:name]] = Object.const_get(relationship[:class_name]).new(data[relationship[:name]])
24
24
  end
25
25
  end
26
26
  end
@@ -48,11 +48,12 @@ module Her
48
48
  # # Fetched via GET "/users/1/articles"
49
49
  def has_many(name, attrs={}) # {{{
50
50
  @her_relationships ||= {}
51
- (@her_relationships[:has_many] ||= []) << attrs.merge(:name => name)
51
+ attrs = { :class_name => name.to_s.classify, :name => name }.merge(attrs)
52
+ (@her_relationships[:has_many] ||= []) << attrs
52
53
 
53
54
  define_method(name) do
54
55
  return @data[name] if @data.include?(name) # Do not fetch from API again if we have it in @data
55
- self.class.get_collection("#{self.class.build_request_path(:id => id)}/#{name.to_s.pluralize}")
56
+ Object.const_get(attrs[:class_name]).get_collection("#{self.class.build_request_path(:id => id)}/#{name.to_s.pluralize}")
56
57
  end
57
58
  end # }}}
58
59
 
@@ -76,11 +77,12 @@ module Her
76
77
  # # Fetched via GET "/users/1/organization"
77
78
  def has_one(name, attrs={}) # {{{
78
79
  @her_relationships ||= {}
79
- (@her_relationships[:has_one] ||= []) << attrs.merge(:name => name)
80
+ attrs = { :class_name => name.to_s.classify, :name => name, :foreign_key => "#{name}_id" }.merge(attrs)
81
+ (@her_relationships[:has_one] ||= []) << attrs
80
82
 
81
83
  define_method(name) do
82
84
  return @data[name] if @data.include?(name) # Do not fetch from API again if we have it in @data
83
- self.class.get_resource("#{self.class.build_request_path(:id => id)}/#{name.to_s.singularize}")
85
+ Object.const_get(attrs[:class_name]).get_resource("#{self.class.build_request_path(:id => id)}/#{name.to_s.singularize}")
84
86
  end
85
87
  end # }}}
86
88
 
@@ -104,11 +106,12 @@ module Her
104
106
  # # Fetched via GET "/teams/2"
105
107
  def belongs_to(name, attrs={}) # {{{
106
108
  @her_relationships ||= {}
107
- (@her_relationships[:belongs_to] ||= []) << attrs.merge(:name => name)
109
+ attrs = { :class_name => name.to_s.classify, :name => name, :foreign_key => "#{name}_id" }.merge(attrs)
110
+ (@her_relationships[:belongs_to] ||= []) << attrs
108
111
 
109
112
  define_method(name) do
110
113
  return @data[name] if @data.include?(name) # Do not fetch from API again if we have it in @data
111
- self.class.get_resource("#{Object.const_get(name.to_s.classify).build_request_path(:id => @data["#{name}_id".to_sym])}")
114
+ Object.const_get(attrs[:class_name]).get_resource("#{Object.const_get(name.to_s.classify).build_request_path(:id => @data["#{name}_id".to_sym])}")
112
115
  end
113
116
  end # }}}
114
117
  end
@@ -1,3 +1,3 @@
1
1
  module Her
2
- VERSION = "0.2.4"
2
+ VERSION = "0.2.5"
3
3
  end
@@ -2,46 +2,67 @@
2
2
  require File.join(File.dirname(__FILE__), "../spec_helper.rb")
3
3
 
4
4
  describe Her::Model::Relationships do
5
- context "setting relationships" do
5
+ context "setting relationships without details" do
6
6
  before do # {{{
7
7
  spawn_model :User
8
8
  end # }}}
9
9
 
10
10
  it "handles a single 'has_many' relationship" do # {{{
11
11
  User.has_many :comments
12
- User.relationships[:has_many].should == [{ :name => :comments }]
12
+ User.relationships[:has_many].should == [{ :name => :comments, :class_name => "Comment" }]
13
13
  end # }}}
14
14
 
15
15
  it "handles multiples 'has_many' relationship" do # {{{
16
16
  User.has_many :comments
17
17
  User.has_many :posts
18
- User.relationships[:has_many].should == [{ :name => :comments }, { :name => :posts }]
18
+ User.relationships[:has_many].should == [{ :name => :comments, :class_name => "Comment" }, { :name => :posts, :class_name => "Post" }]
19
19
  end # }}}
20
20
 
21
21
  it "handles a single 'has_one' relationship" do # {{{
22
22
  User.has_one :category
23
- User.relationships[:has_one].should == [{ :name => :category }]
23
+ User.relationships[:has_one].should == [{ :name => :category, :class_name => "Category", :foreign_key => "category_id" }]
24
24
  end # }}}
25
25
 
26
26
  it "handles multiples 'has_one' relationship" do # {{{
27
27
  User.has_one :category
28
28
  User.has_one :role
29
- User.relationships[:has_one].should == [{ :name => :category }, { :name => :role }]
29
+ User.relationships[:has_one].should == [{ :name => :category, :class_name => "Category", :foreign_key => "category_id" }, { :name => :role, :class_name => "Role", :foreign_key => "role_id" }]
30
30
  end # }}}
31
31
 
32
32
  it "handles a single belongs_to relationship" do # {{{
33
33
  User.belongs_to :organization
34
- User.relationships[:belongs_to].should == [{ :name => :organization }]
34
+ User.relationships[:belongs_to].should == [{ :name => :organization, :class_name => "Organization", :foreign_key => "organization_id" }]
35
35
  end # }}}
36
36
 
37
37
  it "handles multiples 'belongs_to' relationship" do # {{{
38
38
  User.belongs_to :organization
39
39
  User.belongs_to :family
40
- User.relationships[:belongs_to].should == [{ :name => :organization }, { :name => :family }]
40
+ User.relationships[:belongs_to].should == [{ :name => :organization, :class_name => "Organization", :foreign_key => "organization_id" }, { :name => :family, :class_name => "Family", :foreign_key => "family_id" }]
41
41
  end # }}}
42
42
  end
43
43
 
44
- context "handling relationships" do
44
+ context "setting relationships with details" do
45
+ before do # {{{
46
+ spawn_model :User
47
+ end # }}}
48
+
49
+ it "handles a single 'has_many' relationship" do # {{{
50
+ User.has_many :comments, :class_name => "Post"
51
+ User.relationships[:has_many].should == [{ :name => :comments, :class_name => "Post" }]
52
+ end # }}}
53
+
54
+ it "handles a single 'has_one' relationship" do # {{{
55
+ User.has_one :category, :class_name => "Topic", :foreign_key => "topic_id"
56
+ User.relationships[:has_one].should == [{ :name => :category, :class_name => "Topic", :foreign_key => "topic_id" }]
57
+ end # }}}
58
+
59
+ it "handles a single belongs_to relationship" do # {{{
60
+ User.belongs_to :organization, :class_name => "Business", :foreign_key => "business_id"
61
+ User.relationships[:belongs_to].should == [{ :name => :organization, :class_name => "Business", :foreign_key => "business_id" }]
62
+ end # }}}
63
+ end
64
+
65
+ context "handling relationships without details" do
45
66
  before do # {{{
46
67
  Her::API.setup :base_uri => "https://api.example.com" do |builder|
47
68
  builder.use Her::Middleware::FirstLevelParseJSON
@@ -65,39 +86,44 @@ describe Her::Model::Relationships do
65
86
  spawn_model :Comment
66
87
  spawn_model :Role
67
88
 
68
-
69
89
  @user_with_included_data = User.find(1)
70
90
  @user_without_included_data = User.find(2)
71
91
  end # }}}
72
92
 
73
93
  it "maps an array of included data through has_many" do # {{{
94
+ @user_with_included_data.comments.first.class.should == Comment
74
95
  @user_with_included_data.comments.length.should == 2
75
96
  @user_with_included_data.comments.first.id.should == 2
76
97
  @user_with_included_data.comments.first.body.should == "Tobias, you blow hard!"
77
98
  end # }}}
78
99
 
79
100
  it "fetches data that was not included through has_many" do # {{{
101
+ @user_without_included_data.comments.first.class.should == Comment
80
102
  @user_without_included_data.comments.length.should == 2
81
103
  @user_without_included_data.comments.first.id.should == 4
82
104
  @user_without_included_data.comments.first.body.should == "They're having a FIRESALE?"
83
105
  end # }}}
84
106
 
85
107
  it "maps an array of included data through has_one" do # {{{
108
+ @user_with_included_data.role.class.should == Role
86
109
  @user_with_included_data.role.id.should == 1
87
110
  @user_with_included_data.role.body.should == "Admin"
88
111
  end # }}}
89
112
 
90
113
  it "fetches data that was not included through has_one" do # {{{
114
+ @user_without_included_data.role.class.should == Role
91
115
  @user_without_included_data.role.id.should == 2
92
116
  @user_without_included_data.role.body.should == "User"
93
117
  end # }}}
94
118
 
95
119
  it "maps an array of included data through belongs_to" do # {{{
120
+ @user_with_included_data.organization.class.should == Organization
96
121
  @user_with_included_data.organization.id.should == 1
97
122
  @user_with_included_data.organization.name.should == "Bluth Company"
98
123
  end # }}}
99
124
 
100
125
  it "fetches data that was not included through belongs_to" do # {{{
126
+ @user_without_included_data.organization.class.should == Organization
101
127
  @user_without_included_data.organization.id.should == 1
102
128
  @user_without_included_data.organization.name.should == "Bluth Company"
103
129
  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.2.4
4
+ version: 0.2.5
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-05-03 00:00:00.000000000Z
12
+ date: 2012-05-05 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -282,7 +282,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
282
282
  version: '0'
283
283
  segments:
284
284
  - 0
285
- hash: -3092731316656290620
285
+ hash: 112971381959523864
286
286
  required_rubygems_version: !ruby/object:Gem::Requirement
287
287
  none: false
288
288
  requirements:
@@ -291,7 +291,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
291
291
  version: '0'
292
292
  segments:
293
293
  - 0
294
- hash: -3092731316656290620
294
+ hash: 112971381959523864
295
295
  requirements: []
296
296
  rubyforge_project:
297
297
  rubygems_version: 1.8.18