her 0.3.6 → 0.3.7

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.
@@ -0,0 +1,88 @@
1
+ # Testing Her models
2
+
3
+ Suppose we have these two models bound to your API:
4
+
5
+ ```ruby
6
+ # app/models/user.rb
7
+ class User
8
+ include Her::Model
9
+ custom_get :popular
10
+ end
11
+
12
+ # app/models/post.rb
13
+ class Post
14
+ include Her::Model
15
+ custom_get :recent, :archived
16
+ end
17
+ ```
18
+
19
+ In order to test them, we’ll have to stub the remote API requests. With [RSpec](https://github.com/rspec/rspec-core), we can do this like so:
20
+
21
+ ```ruby
22
+ # spec/spec_helper.rb
23
+ RSpec.configure do |config|
24
+ config.include(Module.new do
25
+ def stub_api_for(klass)
26
+ klass.uses_api (api = Her::API.new)
27
+
28
+ # Here, you would customize this for your own API (URL, middleware, etc)
29
+ # like you have done in your application’s initializer
30
+ api.setup :url => "http://api.example.com" do |connection|
31
+ connection.use Her::Middleware::FirstLevelParseJSON
32
+ connection.use Faraday::Request::UrlEncoded
33
+ connection.adapter(:test) { |s| yield(s) }
34
+ end
35
+ end
36
+ end)
37
+ end
38
+ ```
39
+
40
+ Then, in your tests, we can specify what (fake) HTTP requests will return:
41
+
42
+ ```ruby
43
+ # spec/models/user.rb
44
+ describe User do
45
+ before do
46
+ stub_api_for(User) do |stub|
47
+ stub.get("/users/popular") { |env| [200, {}, [{ :id => 1, :name => "Tobias Fünke" }, { :id => 2, :name => "Lindsay Fünke" }].to_json] }
48
+ end
49
+ end
50
+
51
+ describe :popular do
52
+ subject { User.popular }
53
+ its(:length) { should == 2 }
54
+ its(:errors) { should be_empty }
55
+ end
56
+ end
57
+ ```
58
+
59
+ We can redefine the API for a model as many times as we want, like for more complex tests:
60
+
61
+ ```ruby
62
+ # spec/models/user.rb
63
+ describe Post do
64
+ describe :recent do
65
+ before do
66
+ stub_api_for(Post) do |stub|
67
+ stub.get("/posts/recent") { |env| [200, {}, [{ :id => 1 }, { :id => 2 }].to_json] }
68
+ end
69
+ end
70
+
71
+ subject { Post.recent }
72
+ its(:length) { should == 2 }
73
+ its(:errors) { should be_empty }
74
+ end
75
+
76
+ describe :archived do
77
+ before do
78
+ stub_api_for(Post) do |stub|
79
+ stub.get("/posts/archived") { |env| [200, {}, [{ :id => 1 }, { :id => 2 }].to_json] }
80
+ end
81
+ end
82
+
83
+ subject { Post.archived }
84
+ its(:length) { should == 2 }
85
+ its(:errors) { should be_empty }
86
+ end
87
+ end
88
+ ```
@@ -17,13 +17,13 @@ Gem::Specification.new do |s|
17
17
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
18
  s.require_paths = ["lib"]
19
19
 
20
- s.add_development_dependency "rake", "~> 0.9.2"
20
+ s.add_development_dependency "rake", "~> 10.0"
21
21
  s.add_development_dependency "rspec", "~> 2.11"
22
22
  s.add_development_dependency "yard", "~> 0.8"
23
23
  s.add_development_dependency "redcarpet", "~> 2.1"
24
24
  s.add_development_dependency "mocha", "~> 0.12"
25
25
  s.add_development_dependency "guard", "~> 1.2"
26
- s.add_development_dependency "guard-rspec", "~> 1.2"
26
+ s.add_development_dependency "guard-rspec", "~> 2.3"
27
27
  s.add_development_dependency "rb-fsevent", "~> 0.9"
28
28
  s.add_development_dependency "growl", "~> 1.0"
29
29
 
@@ -39,7 +39,7 @@ module Her
39
39
  # class MyCustomParser < Faraday::Response::Middleware
40
40
  # def on_complete(env)
41
41
  # json = JSON.parse(env[:body], :symbolize_names => true)
42
- # errors = json.delete(:errors) || []
42
+ # errors = json.delete(:errors) || {}
43
43
  # metadata = json.delete(:metadata) || []
44
44
  # env[:body] = { :data => json, :errors => errors, :metadata => metadata }
45
45
  # end
@@ -59,7 +59,7 @@ module Her
59
59
  end
60
60
 
61
61
  # Define a custom parsing procedure. The procedure is passed the response object and is
62
- # expected to return a hash with three keys: a main data Hash, an errors Array
62
+ # expected to return a hash with three keys: a main data Hash, an errors Hash
63
63
  # and a metadata Hash.
64
64
  #
65
65
  # @private
@@ -3,10 +3,10 @@ module Her
3
3
  attr_reader :metadata, :errors
4
4
 
5
5
  # @private
6
- def initialize(items=[], metadata={}, errors=[])
6
+ def initialize(items=[], metadata={}, errors={})
7
7
  super(items)
8
- @metadata = metadata || {}
9
- @errors = errors || []
8
+ @metadata = metadata
9
+ @errors = errors
10
10
  end
11
11
  end
12
12
  end
@@ -8,7 +8,7 @@ module Her
8
8
  # @return [Mixed] the parsed response
9
9
  def parse(body)
10
10
  json = MultiJson.load(body, :symbolize_keys => true)
11
- errors = json.delete(:errors) || []
11
+ errors = json.delete(:errors) || {}
12
12
  metadata = json.delete(:metadata) || []
13
13
  {
14
14
  :data => json,
@@ -5,7 +5,11 @@ module Her
5
5
  # Automatically inherit a superclass' api
6
6
  def her_api
7
7
  @her_api ||= begin
8
- superclass.her_api if superclass.respond_to?(:her_api)
8
+ if superclass.respond_to?(:her_api)
9
+ superclass.her_api
10
+ else
11
+ Her::API.default_api
12
+ end
9
13
  end
10
14
  end
11
15
 
@@ -4,6 +4,8 @@ module Her
4
4
  module ORM
5
5
  extend ActiveSupport::Concern
6
6
  attr_accessor :data, :metadata, :errors
7
+ alias :attributes :data
8
+ alias :attributes= :data=
7
9
 
8
10
  # Initialize a new object with data received from an HTTP request
9
11
  def initialize(params={})
@@ -35,6 +37,9 @@ module Her
35
37
  if setter_method_names.include?(setter_method)
36
38
  model.send(setter_method, value)
37
39
  else
40
+ if key.is_a?(String)
41
+ key = key.to_sym
42
+ end
38
43
  memo[key] = value
39
44
  end
40
45
  memo
@@ -60,6 +65,13 @@ module Her
60
65
  method.to_s.end_with?('=') || method.to_s.end_with?('?') || @data.include?(method) || super
61
66
  end
62
67
 
68
+ # Assign new data to an instance
69
+ def assign_data(new_data)
70
+ new_data = Her::Model::ORM.use_setter_methods(self, new_data)
71
+ @data.update new_data
72
+ end
73
+ alias :assign_attributes :assign_data
74
+
63
75
  # Handles returning true for the accessible attributes
64
76
  def has_data?(attribute_name)
65
77
  @data.include?(attribute_name)
@@ -1,3 +1,3 @@
1
1
  module Her
2
- VERSION = "0.3.6"
2
+ VERSION = "0.3.7"
3
3
  end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Her::Collection do
4
+
5
+ let(:items) { [1,2,3,4] }
6
+ let(:metadata) { {:name => 'Testname'} }
7
+ let(:errors) { {:name => ['not_present']} }
8
+
9
+ describe "#new" do
10
+ context "without parameters" do
11
+
12
+ subject { Her::Collection.new }
13
+
14
+ it { should eq([])}
15
+ its(:metadata) { should eq({})}
16
+ its(:errors) { should eq({})}
17
+ end
18
+
19
+ context "with parameters" do
20
+ subject { Her::Collection.new(items, metadata, errors)}
21
+
22
+ it { should eq([1,2,3,4])}
23
+ its(:metadata) { should eq({:name => 'Testname'})}
24
+ its(:errors) { should eq({:name => ['not_present']})}
25
+ end
26
+ end
27
+ end
@@ -3,23 +3,30 @@ require "spec_helper"
3
3
 
4
4
  describe Her::Middleware::FirstLevelParseJSON do
5
5
  subject { described_class.new }
6
- let(:body) { "{\"id\": 1, \"name\": \"Tobias Fünke\", \"errors\": 2, \"metadata\": 3}" }
6
+ let(:body_without_errors) { "{\"id\": 1, \"name\": \"Tobias Fünke\", \"metadata\": 3}" }
7
+ let(:body_with_errors) { "{\"id\": 1, \"name\": \"Tobias Fünke\", \"errors\": { \"name\": [ \"not_valid\", \"should_be_present\" ] }, \"metadata\": 3}" }
7
8
 
8
9
  it "parses body as json" do
9
- subject.parse(body).tap do |json|
10
+ subject.parse(body_without_errors).tap do |json|
10
11
  json[:data].should == { :id => 1, :name => "Tobias Fünke" }
11
- json[:errors].should == 2
12
12
  json[:metadata].should == 3
13
13
  end
14
14
  end
15
15
 
16
16
  it "parses :body key as json in the env hash" do
17
- env = { :body => body }
17
+ env = { :body => body_without_errors }
18
18
  subject.on_complete(env)
19
19
  env[:body].tap do |json|
20
20
  json[:data].should == { :id => 1, :name => "Tobias Fünke" }
21
- json[:errors].should == 2
22
21
  json[:metadata].should == 3
23
22
  end
24
23
  end
24
+
25
+ it 'ensures the errors are a hash if there are no errors' do
26
+ subject.parse(body_without_errors)[:errors].should eq({})
27
+ end
28
+
29
+ it 'ensures the errors are a hash if there are no errors' do
30
+ subject.parse(body_with_errors)[:errors].should eq({:name => [ 'not_valid', 'should_be_present']})
31
+ end
25
32
  end
@@ -49,6 +49,11 @@ describe Her::Model::ORM do
49
49
  @existing_user.new?.should be_false
50
50
  end
51
51
 
52
+ it "accepts new resource with strings as hash keys" do
53
+ @new_user = Foo::User.new('fullname' => "Tobias Fünke")
54
+ @new_user.fullname.should == "Tobias Fünke"
55
+ end
56
+
52
57
  it "handles method missing for getter" do
53
58
  @new_user = Foo::User.new(:fullname => 'Mayonegg')
54
59
  lambda { @new_user.unknown_method_for_a_user }.should raise_error(NoMethodError)
@@ -314,6 +319,26 @@ describe Her::Model::ORM do
314
319
  end
315
320
  end
316
321
 
322
+ context "assigning new resource data" do
323
+ before do
324
+ Her::API.setup :url => "https://api.example.com" do |builder|
325
+ builder.use Her::Middleware::FirstLevelParseJSON
326
+ builder.use Faraday::Request::UrlEncoded
327
+ builder.adapter :test do |stub|
328
+ stub.get("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke", :active => true }.to_json] }
329
+ end
330
+ end
331
+
332
+ spawn_model "Foo::User"
333
+ @user = Foo::User.find(1)
334
+ end
335
+
336
+ it "handles data update through #assign_attributes" do
337
+ @user.assign_attributes :active => true
338
+ @user.should be_active
339
+ end
340
+ end
341
+
317
342
  context "deleting resources" do
318
343
  before do
319
344
  Her::API.setup :url => "https://api.example.com" do |builder|
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.6
4
+ version: 0.3.7
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-05 00:00:00.000000000 Z
12
+ date: 2012-12-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: 0.9.2
21
+ version: '10.0'
22
22
  type: :development
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- version: 0.9.2
29
+ version: '10.0'
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: rspec
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -114,7 +114,7 @@ dependencies:
114
114
  requirements:
115
115
  - - ~>
116
116
  - !ruby/object:Gem::Version
117
- version: '1.2'
117
+ version: '2.3'
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
@@ -122,7 +122,7 @@ dependencies:
122
122
  requirements:
123
123
  - - ~>
124
124
  - !ruby/object:Gem::Version
125
- version: '1.2'
125
+ version: '2.3'
126
126
  - !ruby/object:Gem::Dependency
127
127
  name: rb-fsevent
128
128
  requirement: !ruby/object:Gem::Requirement
@@ -213,11 +213,15 @@ files:
213
213
  - .gitignore
214
214
  - .rspec
215
215
  - .travis.yml
216
+ - CONTRIBUTING.md
217
+ - FEATURES.md
216
218
  - Gemfile
217
219
  - Guardfile
218
220
  - LICENSE
221
+ - MIDDLEWARE.md
219
222
  - README.md
220
223
  - Rakefile
224
+ - TESTING.md
221
225
  - UPGRADE.md
222
226
  - examples/twitter-oauth/Gemfile
223
227
  - examples/twitter-oauth/app.rb
@@ -246,6 +250,7 @@ files:
246
250
  - lib/her/model/relationships.rb
247
251
  - lib/her/version.rb
248
252
  - spec/api_spec.rb
253
+ - spec/collection_spec.rb
249
254
  - spec/middleware/accept_json_spec.rb
250
255
  - spec/middleware/first_level_parse_json_spec.rb
251
256
  - spec/middleware/second_level_parse_json_spec.rb
@@ -272,7 +277,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
272
277
  version: '0'
273
278
  segments:
274
279
  - 0
275
- hash: -1080420939636029022
280
+ hash: -1556423297690229107
276
281
  required_rubygems_version: !ruby/object:Gem::Requirement
277
282
  none: false
278
283
  requirements:
@@ -281,16 +286,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
281
286
  version: '0'
282
287
  segments:
283
288
  - 0
284
- hash: -1080420939636029022
289
+ hash: -1556423297690229107
285
290
  requirements: []
286
291
  rubyforge_project:
287
- rubygems_version: 1.8.23
292
+ rubygems_version: 1.8.24
288
293
  signing_key:
289
294
  specification_version: 3
290
295
  summary: A simple Representational State Transfer-based Hypertext Transfer Protocol-powered
291
296
  Object Relational Mapper. Her?
292
297
  test_files:
293
298
  - spec/api_spec.rb
299
+ - spec/collection_spec.rb
294
300
  - spec/middleware/accept_json_spec.rb
295
301
  - spec/middleware/first_level_parse_json_spec.rb
296
302
  - spec/middleware/second_level_parse_json_spec.rb