her 0.3.6 → 0.3.7
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/CONTRIBUTING.md +26 -0
- data/FEATURES.md +296 -0
- data/MIDDLEWARE.md +183 -0
- data/README.md +20 -482
- data/TESTING.md +88 -0
- data/her.gemspec +2 -2
- data/lib/her/api.rb +2 -2
- data/lib/her/collection.rb +3 -3
- data/lib/her/middleware/first_level_parse_json.rb +1 -1
- data/lib/her/model/http.rb +5 -1
- data/lib/her/model/orm.rb +12 -0
- data/lib/her/version.rb +1 -1
- data/spec/collection_spec.rb +27 -0
- data/spec/middleware/first_level_parse_json_spec.rb +12 -5
- data/spec/model/orm_spec.rb +25 -0
- metadata +15 -9
data/TESTING.md
ADDED
@@ -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
|
+
```
|
data/her.gemspec
CHANGED
@@ -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
|
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", "~>
|
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
|
|
data/lib/her/api.rb
CHANGED
@@ -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
|
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
|
data/lib/her/collection.rb
CHANGED
@@ -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,
|
data/lib/her/model/http.rb
CHANGED
@@ -5,7 +5,11 @@ module Her
|
|
5
5
|
# Automatically inherit a superclass' api
|
6
6
|
def her_api
|
7
7
|
@her_api ||= begin
|
8
|
-
|
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
|
|
data/lib/her/model/orm.rb
CHANGED
@@ -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)
|
data/lib/her/version.rb
CHANGED
@@ -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(:
|
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(
|
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 =>
|
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
|
data/spec/model/orm_spec.rb
CHANGED
@@ -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.
|
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-
|
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
|
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
|
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: '
|
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: '
|
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: -
|
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: -
|
289
|
+
hash: -1556423297690229107
|
285
290
|
requirements: []
|
286
291
|
rubyforge_project:
|
287
|
-
rubygems_version: 1.8.
|
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
|