her 0.1.5 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +17 -1
- data/lib/her/model.rb +2 -0
- data/lib/her/model/hooks.rb +40 -0
- data/lib/her/model/orm.rb +84 -9
- data/lib/her/version.rb +1 -1
- data/spec/model/hooks_spec.rb +264 -0
- data/spec/model/http_spec.rb +149 -0
- data/spec/model/orm_spec.rb +113 -0
- data/spec/model/relationships_spec.rb +115 -0
- metadata +13 -6
- data/spec/model_spec.rb +0 -336
data/README.md
CHANGED
@@ -166,6 +166,23 @@ For `belongs_to` relationship, an extra HTTP request (to `GET /organizations/2`)
|
|
166
166
|
|
167
167
|
However, subsequent calls to `#comments` or `#role` will not trigger the extra HTTP request.
|
168
168
|
|
169
|
+
## Hooks
|
170
|
+
|
171
|
+
You can add *before* and *after* hooks to your models that are triggered on specific actions (`save`, `update`, `create`, `destroy`):
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
class User
|
175
|
+
include Her::Model
|
176
|
+
before_save :set_internal_id
|
177
|
+
|
178
|
+
def set_internal_id
|
179
|
+
self.internal_id = 42 # Will be passed in the HTTP request
|
180
|
+
end
|
181
|
+
end
|
182
|
+
```
|
183
|
+
|
184
|
+
In the future, adding hooks to all models will be possible, as well as defining and triggering your own hooks (eg. for your custom requests).
|
185
|
+
|
169
186
|
## Custom requests
|
170
187
|
|
171
188
|
You can easily add custom methods for your models. You can either use `get_collection` (which maps the returned data to a collection of resources), `get_resource` (which maps the returned data to a single resource) or `get_raw` (which yields the parsed data return from the HTTP request). Other HTTP methods are supported (`post_raw`, `put_resource`, etc.)
|
@@ -224,7 +241,6 @@ Category.all
|
|
224
241
|
|
225
242
|
## Things to be done
|
226
243
|
|
227
|
-
* Deleting resources
|
228
244
|
* Support for Faraday middleware to handle caching, alternative formats, etc.
|
229
245
|
* Hooks before save, update, create, destroy, etc.
|
230
246
|
* Better error handling
|
data/lib/her/model.rb
CHANGED
@@ -14,6 +14,7 @@ module Her
|
|
14
14
|
autoload :HTTP, "her/model/http"
|
15
15
|
autoload :ORM, "her/model/orm"
|
16
16
|
autoload :Relationships, "her/model/relationships"
|
17
|
+
autoload :Hooks, "her/model/hooks"
|
17
18
|
|
18
19
|
extend ActiveSupport::Concern
|
19
20
|
|
@@ -26,6 +27,7 @@ module Her
|
|
26
27
|
extend Her::Model::HTTP
|
27
28
|
extend Her::Model::ORM
|
28
29
|
extend Her::Model::Relationships
|
30
|
+
extend Her::Model::Hooks
|
29
31
|
|
30
32
|
# Define default settings
|
31
33
|
collection_path "#{self.to_s.downcase.pluralize}"
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Her
|
2
|
+
module Model
|
3
|
+
module Hooks
|
4
|
+
# Return hooks
|
5
|
+
# @private
|
6
|
+
def hooks # {{{
|
7
|
+
@her_hooks
|
8
|
+
end # }}}
|
9
|
+
|
10
|
+
# @private
|
11
|
+
def set_hook(time, name, action) # {{{
|
12
|
+
@her_hooks ||= {}
|
13
|
+
(@her_hooks["#{time}_#{name}".to_sym] ||= []) << action
|
14
|
+
end # }}}
|
15
|
+
|
16
|
+
# @private
|
17
|
+
def perform_hook(record, time, name) # {{{
|
18
|
+
@her_hooks ||= {}
|
19
|
+
hooks = @her_hooks["#{time}_#{name}".to_sym] || []
|
20
|
+
hooks.each do |hook|
|
21
|
+
if hook.is_a? Symbol
|
22
|
+
record.send(hook)
|
23
|
+
else
|
24
|
+
hook.call(record)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end # }}}
|
28
|
+
|
29
|
+
def before_save(method=nil, &block); set_hook(:before, :save, method || block); end
|
30
|
+
def before_create(method=nil, &block); set_hook(:before, :create, method || block); end
|
31
|
+
def before_update(method=nil, &block); set_hook(:before, :update, method || block); end
|
32
|
+
def before_destroy(method=nil, &block); set_hook(:before, :destroy, method || block); end
|
33
|
+
|
34
|
+
def after_save(method=nil, &block); set_hook(:after, :save, method || block); end
|
35
|
+
def after_create(method=nil, &block); set_hook(:after, :create, method || block); end
|
36
|
+
def after_update(method=nil, &block); set_hook(:after, :update, method || block); end
|
37
|
+
def after_destroy(method=nil, &block); set_hook(:after, :destroy, method || block); end
|
38
|
+
end # }}}
|
39
|
+
end
|
40
|
+
end
|
data/lib/her/model/orm.rb
CHANGED
@@ -20,14 +20,14 @@ module Her
|
|
20
20
|
def method_missing(method, attrs=nil) # {{{
|
21
21
|
assignment_method = method.to_s =~ /\=$/
|
22
22
|
method = method.to_s.gsub(/(\?|\!|\=)$/, "").to_sym
|
23
|
-
if
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
if attrs and assignment_method
|
24
|
+
@data[method.to_s.gsub(/\=$/, "").to_sym] = attrs
|
25
|
+
else
|
26
|
+
if @data.include?(method)
|
27
27
|
@data[method]
|
28
|
+
else
|
29
|
+
super
|
28
30
|
end
|
29
|
-
else
|
30
|
-
super
|
31
31
|
end
|
32
32
|
end # }}}
|
33
33
|
|
@@ -38,6 +38,9 @@ module Her
|
|
38
38
|
end # }}}
|
39
39
|
|
40
40
|
# Fetch a specific resource based on an ID
|
41
|
+
#
|
42
|
+
# @example
|
43
|
+
# @user = User.find(1) GET /users/1
|
41
44
|
def find(id, params={}) # {{{
|
42
45
|
request(params.merge(:_method => :get, :_path => "#{@her_collection_path}/#{id}")) do |parsed_data|
|
43
46
|
new(parsed_data[:data])
|
@@ -45,28 +48,100 @@ module Her
|
|
45
48
|
end # }}}
|
46
49
|
|
47
50
|
# Fetch a collection of resources
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# @users = User.all # GET /users
|
48
54
|
def all(params={}) # {{{
|
49
55
|
request(params.merge(:_method => :get, :_path => "#{@her_collection_path}")) do |parsed_data|
|
50
56
|
new_collection(parsed_data)
|
51
57
|
end
|
52
58
|
end # }}}
|
53
59
|
|
54
|
-
# Create a resource
|
60
|
+
# Create a resource and return it
|
61
|
+
#
|
62
|
+
# @example
|
63
|
+
# @user = User.create({ :fullname => "Tobias Fünke" }) # POST /users/1
|
55
64
|
def create(params={}) # {{{
|
65
|
+
resource = new(params)
|
66
|
+
perform_hook(resource, :before, :create)
|
67
|
+
perform_hook(resource, :before, :save)
|
68
|
+
params = resource.instance_eval { @data }
|
56
69
|
request(params.merge(:_method => :post, :_path => "#{@her_collection_path}")) do |parsed_data|
|
57
|
-
|
70
|
+
resource.instance_eval do
|
71
|
+
@data = parsed_data[:data]
|
72
|
+
end
|
58
73
|
end
|
74
|
+
perform_hook(resource, :after, :save)
|
75
|
+
perform_hook(resource, :after, :create)
|
76
|
+
|
77
|
+
resource
|
78
|
+
end # }}}
|
79
|
+
|
80
|
+
# Save an existing resource and return it
|
81
|
+
#
|
82
|
+
# @example
|
83
|
+
# @user = User.save_existing(1, { :fullname => "Tobias Fünke" }) # PUT /users/1
|
84
|
+
def save_existing(id, params) # {{{
|
85
|
+
resource = new(params.merge(:id => id))
|
86
|
+
resource.save
|
59
87
|
end # }}}
|
60
88
|
|
61
89
|
# Save a resource
|
90
|
+
#
|
91
|
+
# @example Save a resource after fetching it
|
92
|
+
# @user = User.find(1) # GET /users/1
|
93
|
+
# @user.fullname = "Tobias Fünke"
|
94
|
+
# @user.save # PUT /users/1
|
95
|
+
#
|
96
|
+
# @example Save a new resource by creating it
|
97
|
+
# @user = User.new({ :fullname => "Tobias Fünke" })
|
98
|
+
# @user.save # POST /users
|
62
99
|
def save # {{{
|
63
100
|
params = @data.dup
|
64
101
|
if @data[:id]
|
102
|
+
self.class.perform_hook(self, :before, :update)
|
103
|
+
self.class.perform_hook(self, :before, :save)
|
65
104
|
self.class.request(params.merge(:_method => :put, :_path => "#{self.class.collection_path}/#{id}")) do |parsed_data|
|
66
105
|
@data = parsed_data[:data]
|
67
106
|
end
|
107
|
+
self.class.perform_hook(self, :after, :save)
|
108
|
+
self.class.perform_hook(self, :after, :update)
|
109
|
+
self
|
68
110
|
else
|
69
|
-
self.class.create
|
111
|
+
self.class.perform_hook(self, :before, :create)
|
112
|
+
self.class.perform_hook(self, :before, :save)
|
113
|
+
self.class.request(params.merge(:_method => :post, :_path => "#{self.class.collection_path}")) do |parsed_data|
|
114
|
+
@data = parsed_data[:data]
|
115
|
+
end
|
116
|
+
self.class.perform_hook(self, :after, :save)
|
117
|
+
self.class.perform_hook(self, :after, :create)
|
118
|
+
end
|
119
|
+
self
|
120
|
+
end # }}}
|
121
|
+
|
122
|
+
# Destroy a resource
|
123
|
+
#
|
124
|
+
# @example
|
125
|
+
# @user = User.find(1) # GET /users/1
|
126
|
+
# @user.destroy # DELETE /users/1
|
127
|
+
def destroy # {{{
|
128
|
+
params = @data.dup
|
129
|
+
self.class.perform_hook(self, :before, :destroy)
|
130
|
+
self.class.request(params.merge(:_method => :delete, :_path => "#{self.class.collection_path}/#{id}")) do |parsed_data|
|
131
|
+
@data = parsed_data[:data]
|
132
|
+
end
|
133
|
+
self.class.perform_hook(self, :after, :destroy)
|
134
|
+
self
|
135
|
+
end # }}}
|
136
|
+
|
137
|
+
# Destroy an existing resource
|
138
|
+
#
|
139
|
+
# @example
|
140
|
+
# User.destroy_existing(1) # DELETE /users/1
|
141
|
+
def destroy_existing(id) # {{{
|
142
|
+
params = {}
|
143
|
+
request(params.merge(:_method => :delete, :_path => "#{collection_path}/#{id}")) do |parsed_data|
|
144
|
+
new(parsed_data[:data])
|
70
145
|
end
|
71
146
|
end # }}}
|
72
147
|
end
|
data/lib/her/version.rb
CHANGED
@@ -0,0 +1,264 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.join(File.dirname(__FILE__), "../spec_helper.rb")
|
3
|
+
|
4
|
+
describe Her::Model::Hooks do
|
5
|
+
context "adding hooks to a model" do
|
6
|
+
before do # {{{
|
7
|
+
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
8
|
+
class User
|
9
|
+
include Her::Model
|
10
|
+
end
|
11
|
+
end # }}}
|
12
|
+
|
13
|
+
describe "method hooks" do
|
14
|
+
it "handles “before save” method hooks" do # {{{
|
15
|
+
User.before_save :set_internal_id
|
16
|
+
User.hooks[:before_save].length.should == 1
|
17
|
+
User.hooks[:before_save].first.class.should == Symbol
|
18
|
+
end # }}}
|
19
|
+
|
20
|
+
it "handles “before create” method hooks" do # {{{
|
21
|
+
User.before_create :set_internal_id
|
22
|
+
User.hooks[:before_create].length.should == 1
|
23
|
+
User.hooks[:before_create].first.class.should == Symbol
|
24
|
+
end # }}}
|
25
|
+
|
26
|
+
it "handles “before update” method hooks" do # {{{
|
27
|
+
User.before_update :set_internal_id
|
28
|
+
User.hooks[:before_update].length.should == 1
|
29
|
+
User.hooks[:before_update].first.class.should == Symbol
|
30
|
+
end # }}}
|
31
|
+
|
32
|
+
it "handles “before destroy” method hooks" do # {{{
|
33
|
+
User.before_destroy :set_internal_id
|
34
|
+
User.hooks[:before_destroy].length.should == 1
|
35
|
+
User.hooks[:before_destroy].first.class.should == Symbol
|
36
|
+
end # }}}
|
37
|
+
|
38
|
+
it "handles “after save” method hooks" do # {{{
|
39
|
+
User.after_save :set_internal_id
|
40
|
+
User.hooks[:after_save].length.should == 1
|
41
|
+
User.hooks[:after_save].first.class.should == Symbol
|
42
|
+
end # }}}
|
43
|
+
|
44
|
+
it "handles “after create” method hooks" do # {{{
|
45
|
+
User.after_create :set_internal_id
|
46
|
+
User.hooks[:after_create].length.should == 1
|
47
|
+
User.hooks[:after_create].first.class.should == Symbol
|
48
|
+
end # }}}
|
49
|
+
|
50
|
+
it "handles “after update” method hooks" do # {{{
|
51
|
+
User.after_update :set_internal_id
|
52
|
+
User.hooks[:after_update].length.should == 1
|
53
|
+
User.hooks[:after_update].first.class.should == Symbol
|
54
|
+
end # }}}
|
55
|
+
|
56
|
+
it "handles “after destroy” method hooks" do # {{{
|
57
|
+
User.after_destroy :set_internal_id
|
58
|
+
User.hooks[:after_destroy].length.should == 1
|
59
|
+
User.hooks[:after_destroy].first.class.should == Symbol
|
60
|
+
end # }}}
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "block hooks" do
|
64
|
+
it "handles “before save” block hooks" do # {{{
|
65
|
+
User.before_save { |record| record.internal_id = 42 }
|
66
|
+
User.hooks[:before_save].length.should == 1
|
67
|
+
User.hooks[:before_save].first.class.should == Proc
|
68
|
+
end # }}}
|
69
|
+
|
70
|
+
it "handles “before create” block hooks" do # {{{
|
71
|
+
User.before_create { |record| record.internal_id = 42 }
|
72
|
+
User.hooks[:before_create].length.should == 1
|
73
|
+
User.hooks[:before_create].first.class.should == Proc
|
74
|
+
end # }}}
|
75
|
+
|
76
|
+
it "handles “before update” block hooks" do # {{{
|
77
|
+
User.before_update { |record| record.internal_id = 42 }
|
78
|
+
User.hooks[:before_update].length.should == 1
|
79
|
+
User.hooks[:before_update].first.class.should == Proc
|
80
|
+
end # }}}
|
81
|
+
|
82
|
+
it "handles “before destroy” block hooks" do # {{{
|
83
|
+
User.before_destroy { |record| record.internal_id = 42 }
|
84
|
+
User.hooks[:before_destroy].length.should == 1
|
85
|
+
User.hooks[:before_destroy].first.class.should == Proc
|
86
|
+
end # }}}
|
87
|
+
|
88
|
+
it "handles “after save” block hooks" do # {{{
|
89
|
+
User.after_save { |record| record.internal_id = 42 }
|
90
|
+
User.hooks[:after_save].length.should == 1
|
91
|
+
User.hooks[:after_save].first.class.should == Proc
|
92
|
+
end # }}}
|
93
|
+
|
94
|
+
it "handles “after create” block hooks" do # {{{
|
95
|
+
User.after_create { |record| record.internal_id = 42 }
|
96
|
+
User.hooks[:after_create].length.should == 1
|
97
|
+
User.hooks[:after_create].first.class.should == Proc
|
98
|
+
end # }}}
|
99
|
+
|
100
|
+
it "handles “after update” block hooks" do # {{{
|
101
|
+
User.after_update { |record| record.internal_id = 42 }
|
102
|
+
User.hooks[:after_update].length.should == 1
|
103
|
+
User.hooks[:after_update].first.class.should == Proc
|
104
|
+
end # }}}
|
105
|
+
|
106
|
+
it "handles “after destroy” block hooks" do # {{{
|
107
|
+
User.after_destroy { |record| record.internal_id = 42 }
|
108
|
+
User.hooks[:after_destroy].length.should == 1
|
109
|
+
User.hooks[:after_destroy].first.class.should == Proc
|
110
|
+
end # }}}
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context "perform hooks on a model" do
|
115
|
+
before do # {{{
|
116
|
+
Her::API.setup :base_uri => "https://api.example.com"
|
117
|
+
FakeWeb.register_uri(:post, "https://api.example.com/users", :body => { :data => { :id => 1, :name => "Tobias Fünke" } }.to_json)
|
118
|
+
FakeWeb.register_uri(:get, "https://api.example.com/users/1", :body => { :data => { :id => 1, :name => "Tobias Fünke" } }.to_json)
|
119
|
+
FakeWeb.register_uri(:put, "https://api.example.com/users/1", :body => { :data => { :id => 1, :name => "Tobias Fünke" } }.to_json)
|
120
|
+
FakeWeb.register_uri(:delete, "https://api.example.com/users/1", :body => { :data => { :id => 1, :name => "Tobias Fünke" } }.to_json)
|
121
|
+
|
122
|
+
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
123
|
+
class User
|
124
|
+
include Her::Model
|
125
|
+
attr_accessor :internal_save_id, :internal_create_id, :internal_update_id, :internal_destroy_id
|
126
|
+
attr_accessor :internal_after_save_id, :internal_after_create_id, :internal_after_update_id, :internal_after_destroy_id
|
127
|
+
|
128
|
+
def change_internal_save_id; @internal_save_id = 100; end
|
129
|
+
def change_internal_create_id; @internal_create_id = 101; end
|
130
|
+
def change_internal_update_id; @internal_update_id = 102; end
|
131
|
+
def change_internal_destroy_id; @internal_destroy_id = 103; end
|
132
|
+
|
133
|
+
def change_internal_after_save_id; @internal_after_save_id = 100; end
|
134
|
+
def change_internal_after_create_id; @internal_after_create_id = 101; end
|
135
|
+
def change_internal_after_update_id; @internal_after_update_id = 102; end
|
136
|
+
def change_internal_after_destroy_id; @internal_after_destroy_id = 103; end
|
137
|
+
end
|
138
|
+
end # }}}
|
139
|
+
|
140
|
+
describe "method hooks" do
|
141
|
+
before do # {{{
|
142
|
+
User.before_save :change_internal_save_id
|
143
|
+
User.before_update :change_internal_update_id
|
144
|
+
User.before_create :change_internal_create_id
|
145
|
+
User.before_destroy :change_internal_destroy_id
|
146
|
+
|
147
|
+
User.after_save :change_internal_after_save_id
|
148
|
+
User.after_update :change_internal_after_update_id
|
149
|
+
User.after_create :change_internal_after_create_id
|
150
|
+
User.after_destroy :change_internal_after_destroy_id
|
151
|
+
end # }}}
|
152
|
+
|
153
|
+
it "perform “before save” “before create” method hook on Model#save without an ID" do # {{{
|
154
|
+
@user = User.new(:fullname => "Tobias Fünke")
|
155
|
+
@user.save
|
156
|
+
@user.internal_save_id.should == 100
|
157
|
+
@user.internal_create_id.should == 101
|
158
|
+
@user.internal_update_id.should == nil
|
159
|
+
end # }}}
|
160
|
+
|
161
|
+
it "perform “before save” and “before update” method hook on Model#save with an ID" do # {{{
|
162
|
+
@user = User.find(1)
|
163
|
+
@user.save
|
164
|
+
@user.internal_save_id.should == 100
|
165
|
+
@user.internal_create_id.should == nil
|
166
|
+
@user.internal_update_id.should == 102
|
167
|
+
end # }}}
|
168
|
+
|
169
|
+
it "perform “before destroy” method hook on Model#destroy" do # {{{
|
170
|
+
@user = User.find(1)
|
171
|
+
@user.destroy
|
172
|
+
@user.internal_save_id.should == nil
|
173
|
+
@user.internal_create_id.should == nil
|
174
|
+
@user.internal_update_id.should == nil
|
175
|
+
@user.internal_destroy_id.should == 103
|
176
|
+
end # }}}
|
177
|
+
|
178
|
+
it "perform “after save” “after create” method hook on Model#save without an ID" do # {{{
|
179
|
+
@user = User.new(:fullname => "Tobias Fünke")
|
180
|
+
@user.save
|
181
|
+
@user.internal_after_save_id.should == 100
|
182
|
+
@user.internal_after_create_id.should == 101
|
183
|
+
@user.internal_after_update_id.should == nil
|
184
|
+
end # }}}
|
185
|
+
|
186
|
+
it "perform “after save” “after update” method hook on Model#save with an ID" do # {{{
|
187
|
+
@user = User.find(1)
|
188
|
+
@user.save
|
189
|
+
@user.internal_after_save_id.should == 100
|
190
|
+
@user.internal_after_create_id.should == nil
|
191
|
+
@user.internal_after_update_id.should == 102
|
192
|
+
end # }}}
|
193
|
+
|
194
|
+
it "perform “after save” “after update” method hook on Model.save_existing" do # {{{
|
195
|
+
@user = User.save_existing(1, { :fullname => "Tobias Fünke" })
|
196
|
+
@user.internal_after_save_id.should == 100
|
197
|
+
@user.internal_after_create_id.should == nil
|
198
|
+
@user.internal_after_update_id.should == 102
|
199
|
+
end # }}}
|
200
|
+
|
201
|
+
it "perform “after save” “after create” method hook on Model.create" do # {{{
|
202
|
+
@user = User.create({ :fullname => "Tobias Fünke" })
|
203
|
+
@user.internal_after_save_id.should == 100
|
204
|
+
@user.internal_after_create_id.should == 101
|
205
|
+
@user.internal_after_update_id.should == nil
|
206
|
+
end # }}}
|
207
|
+
end
|
208
|
+
|
209
|
+
describe "block hooks" do
|
210
|
+
before do # {{{
|
211
|
+
User.before_save { |record| record.internal_save_id = 200 }
|
212
|
+
User.before_create { |record| record.internal_create_id = 201 }
|
213
|
+
User.before_update { |record| record.internal_update_id = 202 }
|
214
|
+
User.before_destroy { |record| record.internal_destroy_id = 203 }
|
215
|
+
|
216
|
+
User.after_save { |record| record.internal_after_save_id = 200 }
|
217
|
+
User.after_create { |record| record.internal_after_create_id = 201 }
|
218
|
+
User.after_update { |record| record.internal_after_update_id = 202 }
|
219
|
+
User.after_destroy { |record| record.internal_after_destroy_id = 203 }
|
220
|
+
end # }}}
|
221
|
+
|
222
|
+
it "perform “before save” and “before create” block hook on Model#save without an ID" do # {{{
|
223
|
+
@user = User.new(:fullname => "Tobias Fünke")
|
224
|
+
@user.save
|
225
|
+
@user.internal_save_id.should == 200
|
226
|
+
@user.internal_create_id.should == 201
|
227
|
+
@user.internal_update_id.should == nil
|
228
|
+
end # }}}
|
229
|
+
|
230
|
+
it "perform “before save” and “before update” block hook on Model#save with an ID" do # {{{
|
231
|
+
@user = User.find(1)
|
232
|
+
@user.save
|
233
|
+
@user.internal_save_id.should == 200
|
234
|
+
@user.internal_create_id.should == nil
|
235
|
+
@user.internal_update_id.should == 202
|
236
|
+
end # }}}
|
237
|
+
|
238
|
+
it "perform “before destroy” block hook on Model#destroy" do # {{{
|
239
|
+
@user = User.find(1)
|
240
|
+
@user.destroy
|
241
|
+
@user.internal_save_id.should == nil
|
242
|
+
@user.internal_create_id.should == nil
|
243
|
+
@user.internal_update_id.should == nil
|
244
|
+
@user.internal_destroy_id.should == 203
|
245
|
+
end # }}}
|
246
|
+
|
247
|
+
it "perform “after save” “after create” block hook on Model#save without an ID" do # {{{
|
248
|
+
@user = User.new(:fullname => "Tobias Fünke")
|
249
|
+
@user.save
|
250
|
+
@user.internal_after_save_id.should == 200
|
251
|
+
@user.internal_after_create_id.should == 201
|
252
|
+
@user.internal_after_update_id.should == nil
|
253
|
+
end # }}}
|
254
|
+
|
255
|
+
it "perform “after save” “after update” block hook on Model#save with an ID" do # {{{
|
256
|
+
@user = User.find(1)
|
257
|
+
@user.save
|
258
|
+
@user.internal_after_save_id.should == 200
|
259
|
+
@user.internal_after_create_id.should == nil
|
260
|
+
@user.internal_after_update_id.should == 202
|
261
|
+
end # }}}
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.join(File.dirname(__FILE__), "../spec_helper.rb")
|
3
|
+
|
4
|
+
describe Her::Model::HTTP do
|
5
|
+
context "binding a model with an API" do
|
6
|
+
it "binds a model to an instance of Her::API" do # {{{
|
7
|
+
@api = Her::API.new
|
8
|
+
@api.setup :base_uri => "https://api.example.com"
|
9
|
+
|
10
|
+
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
11
|
+
class User
|
12
|
+
include Her::Model
|
13
|
+
end
|
14
|
+
User.uses_api @api
|
15
|
+
|
16
|
+
User.class_eval do
|
17
|
+
@her_api.should_not == nil
|
18
|
+
@her_api.base_uri.should == "https://api.example.com"
|
19
|
+
end
|
20
|
+
end # }}}
|
21
|
+
|
22
|
+
it "binds a model directly to Her::API" do # {{{
|
23
|
+
Her::API.setup :base_uri => "https://api.example.com"
|
24
|
+
|
25
|
+
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
26
|
+
class User
|
27
|
+
include Her::Model
|
28
|
+
end
|
29
|
+
|
30
|
+
User.class_eval do
|
31
|
+
@her_api.should_not == nil
|
32
|
+
@her_api.base_uri.should == "https://api.example.com"
|
33
|
+
end
|
34
|
+
end # }}}
|
35
|
+
|
36
|
+
it "binds two models to two different instances of Her::API" do # {{{
|
37
|
+
@api1 = Her::API.new
|
38
|
+
@api1.setup :base_uri => "https://api1.example.com"
|
39
|
+
|
40
|
+
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
41
|
+
class User; include Her::Model; end
|
42
|
+
User.uses_api @api1
|
43
|
+
|
44
|
+
User.class_eval do
|
45
|
+
@her_api.base_uri.should == "https://api1.example.com"
|
46
|
+
end
|
47
|
+
|
48
|
+
@api2 = Her::API.new
|
49
|
+
@api2.setup :base_uri => "https://api2.example.com"
|
50
|
+
|
51
|
+
Object.instance_eval { remove_const :Comment } if Object.const_defined?(:Comment)
|
52
|
+
class Comment; include Her::Model; end
|
53
|
+
Comment.uses_api @api2
|
54
|
+
|
55
|
+
Comment.class_eval do
|
56
|
+
@her_api.base_uri.should == "https://api2.example.com"
|
57
|
+
end
|
58
|
+
end # }}}
|
59
|
+
|
60
|
+
it "binds one model to Her::API and another one to an instance of Her::API" do # {{{
|
61
|
+
Her::API.setup :base_uri => "https://api1.example.com"
|
62
|
+
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
63
|
+
class User; include Her::Model; end
|
64
|
+
|
65
|
+
User.class_eval do
|
66
|
+
@her_api.base_uri.should == "https://api1.example.com"
|
67
|
+
end
|
68
|
+
|
69
|
+
@api = Her::API.new
|
70
|
+
@api.setup :base_uri => "https://api2.example.com"
|
71
|
+
|
72
|
+
Object.instance_eval { remove_const :Comment } if Object.const_defined?(:Comment)
|
73
|
+
class Comment; include Her::Model; end
|
74
|
+
Comment.uses_api @api
|
75
|
+
|
76
|
+
Comment.class_eval do
|
77
|
+
@her_api.base_uri.should == "https://api2.example.com"
|
78
|
+
end
|
79
|
+
end # }}}
|
80
|
+
end
|
81
|
+
|
82
|
+
context "making HTTP requests" do
|
83
|
+
before do # {{{
|
84
|
+
@api = Her::API.new
|
85
|
+
@api.setup :base_uri => "https://api.example.com"
|
86
|
+
FakeWeb.register_uri(:get, "https://api.example.com/users", :body => { :data => [{ :id => 1 }] }.to_json)
|
87
|
+
FakeWeb.register_uri(:get, "https://api.example.com/users?page=2", :body => { :data => [{ :id => 2 }] }.to_json)
|
88
|
+
FakeWeb.register_uri(:get, "https://api.example.com/users/popular", :body => { :data => [{ :id => 1 }, { :id => 2 }] }.to_json)
|
89
|
+
FakeWeb.register_uri(:get, "https://api.example.com/users/1", :body => { :data => { :id => 1 } }.to_json)
|
90
|
+
FakeWeb.register_uri(:post, "https://api.example.com/users", :body => { :data => [{ :id => 3 }] }.to_json)
|
91
|
+
FakeWeb.register_uri(:put, "https://api.example.com/users/4", :body => { :data => [{ :id => 4 }] }.to_json)
|
92
|
+
FakeWeb.register_uri(:patch, "https://api.example.com/users/6", :body => { :data => [{ :id => 6 }] }.to_json)
|
93
|
+
FakeWeb.register_uri(:delete, "https://api.example.com/users/5", :body => { :data => [{ :id => 5 }] }.to_json)
|
94
|
+
|
95
|
+
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
96
|
+
class User
|
97
|
+
include Her::Model
|
98
|
+
end
|
99
|
+
User.uses_api @api
|
100
|
+
end # }}}
|
101
|
+
|
102
|
+
it "handle raw GET" do # {{{
|
103
|
+
User.get_raw("/users") do |parsed_data|
|
104
|
+
parsed_data[:data].should == [{ :id => 1 }]
|
105
|
+
end
|
106
|
+
end # }}}
|
107
|
+
|
108
|
+
it "handle raw POST" do # {{{
|
109
|
+
User.post_raw("/users") do |parsed_data|
|
110
|
+
parsed_data[:data].should == [{ :id => 3 }]
|
111
|
+
end
|
112
|
+
end # }}}
|
113
|
+
|
114
|
+
it "handle raw PUT" do # {{{
|
115
|
+
User.put_raw("/users/4") do |parsed_data|
|
116
|
+
parsed_data[:data].should == [{ :id => 4 }]
|
117
|
+
end
|
118
|
+
end # }}}
|
119
|
+
|
120
|
+
it "handle raw PATCH" do # {{{
|
121
|
+
User.patch_raw("/users/6") do |parsed_data|
|
122
|
+
parsed_data[:data].should == [{ :id => 6 }]
|
123
|
+
end
|
124
|
+
end # }}}
|
125
|
+
|
126
|
+
it "handle raw DELETE" do # {{{
|
127
|
+
User.delete_raw("/users/5") do |parsed_data|
|
128
|
+
parsed_data[:data].should == [{ :id => 5 }]
|
129
|
+
end
|
130
|
+
end # }}}
|
131
|
+
|
132
|
+
it "handle querystring parameters" do # {{{
|
133
|
+
User.get_raw("/users", :page => 2) do |parsed_data|
|
134
|
+
parsed_data[:data].should == [{ :id => 2 }]
|
135
|
+
end
|
136
|
+
end # }}}
|
137
|
+
|
138
|
+
it "handle GET collection" do # {{{
|
139
|
+
@users = User.get_collection("/users/popular")
|
140
|
+
@users.length.should == 2
|
141
|
+
@users.first.id.should == 1
|
142
|
+
end # }}}
|
143
|
+
|
144
|
+
it "handle GET resource" do # {{{
|
145
|
+
@user = User.get_resource("/users/1")
|
146
|
+
@user.id.should == 1
|
147
|
+
end # }}}
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.join(File.dirname(__FILE__), "../spec_helper.rb")
|
3
|
+
|
4
|
+
describe Her::Model::ORM do
|
5
|
+
context "mapping data to Ruby objects" do
|
6
|
+
before do # {{{
|
7
|
+
@api = Her::API.new
|
8
|
+
@api.setup :base_uri => "https://api.example.com"
|
9
|
+
FakeWeb.register_uri(:get, "https://api.example.com/users/1", :body => { :data => { :id => 1, :name => "Tobias Fünke" } }.to_json)
|
10
|
+
FakeWeb.register_uri(:get, "https://api.example.com/users", :body => { :data => [{ :id => 1, :name => "Tobias Fünke" }, { :id => 2, :name => "Lindsay Fünke" }] }.to_json)
|
11
|
+
|
12
|
+
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
13
|
+
class User
|
14
|
+
include Her::Model
|
15
|
+
end
|
16
|
+
User.uses_api @api
|
17
|
+
end # }}}
|
18
|
+
|
19
|
+
it "maps a single resource to a Ruby object" do # {{{
|
20
|
+
@user = User.find(1)
|
21
|
+
@user.id.should == 1
|
22
|
+
@user.name.should == "Tobias Fünke"
|
23
|
+
end # }}}
|
24
|
+
|
25
|
+
it "maps a collection of resources to an array of Ruby objects" do # {{{
|
26
|
+
@users = User.all
|
27
|
+
@users.length.should == 2
|
28
|
+
@users.first.name.should == "Tobias Fünke"
|
29
|
+
end # }}}
|
30
|
+
end
|
31
|
+
|
32
|
+
context "creating resources" do
|
33
|
+
before do # {{{
|
34
|
+
Her::API.setup :base_uri => "https://api.example.com"
|
35
|
+
FakeWeb.register_uri(:post, "https://api.example.com/users", :body => { :data => { :id => 1, :fullname => "Tobias Fünke" } }.to_json)
|
36
|
+
|
37
|
+
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
38
|
+
class User
|
39
|
+
include Her::Model
|
40
|
+
end
|
41
|
+
end # }}}
|
42
|
+
|
43
|
+
it "handle one-line resource creation" do # {{{
|
44
|
+
@user = User.create(:fullname => "Tobias Fünke")
|
45
|
+
@user.id.should == 1
|
46
|
+
@user.fullname.should == "Tobias Fünke"
|
47
|
+
end # }}}
|
48
|
+
|
49
|
+
it "handle resource creation through Model.new + #save" do # {{{
|
50
|
+
@user = User.new(:fullname => "Tobias Fünke")
|
51
|
+
@user.save
|
52
|
+
@user.fullname.should == "Tobias Fünke"
|
53
|
+
end # }}}
|
54
|
+
end
|
55
|
+
|
56
|
+
context "updating resources" do
|
57
|
+
before do # {{{
|
58
|
+
@api = Her::API.new
|
59
|
+
@api.setup :base_uri => "https://api.example.com"
|
60
|
+
FakeWeb.register_uri(:get, "https://api.example.com/users/1", :body => { :data => { :id => 1, :fullname => "Tobias Fünke" } }.to_json)
|
61
|
+
FakeWeb.register_uri(:put, "https://api.example.com/users/1", :body => { :data => { :id => 1, :fullname => "Lindsay Fünke" } }.to_json)
|
62
|
+
|
63
|
+
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
64
|
+
class User
|
65
|
+
include Her::Model
|
66
|
+
end
|
67
|
+
end # }}}
|
68
|
+
|
69
|
+
it "handle resource data update without saving it" do # {{{
|
70
|
+
@user = User.find(1)
|
71
|
+
@user.fullname.should == "Tobias Fünke"
|
72
|
+
@user.fullname = "Kittie Sanchez"
|
73
|
+
@user.fullname.should == "Kittie Sanchez"
|
74
|
+
end # }}}
|
75
|
+
|
76
|
+
it "handle resource update through the .update class method" do # {{{
|
77
|
+
@user = User.save_existing(1, { :fullname => "Lindsay Fünke" })
|
78
|
+
@user.fullname.should == "Lindsay Fünke"
|
79
|
+
end # }}}
|
80
|
+
|
81
|
+
it "handle resource update through #save on an existing resource" do # {{{
|
82
|
+
@user = User.find(1)
|
83
|
+
@user.fullname = "Lindsay Fünke"
|
84
|
+
@user.save
|
85
|
+
@user.fullname.should == "Lindsay Fünke"
|
86
|
+
end # }}}
|
87
|
+
end
|
88
|
+
|
89
|
+
context "deleting resources" do
|
90
|
+
before do # {{{
|
91
|
+
@api = Her::API.new
|
92
|
+
@api.setup :base_uri => "https://api.example.com"
|
93
|
+
FakeWeb.register_uri(:get, "https://api.example.com/users/1", :body => { :data => { :id => 1, :fullname => "Tobias Fünke", :active => true } }.to_json)
|
94
|
+
FakeWeb.register_uri(:delete, "https://api.example.com/users/1", :body => { :data => { :id => 1, :fullname => "Lindsay Fünke", :active => false } }.to_json)
|
95
|
+
|
96
|
+
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
97
|
+
class User
|
98
|
+
include Her::Model
|
99
|
+
end
|
100
|
+
end # }}}
|
101
|
+
|
102
|
+
it "handle resource deletion through the .destroy class method" do # {{{
|
103
|
+
@user = User.destroy_existing(1)
|
104
|
+
@user.active.should be_false
|
105
|
+
end # }}}
|
106
|
+
|
107
|
+
it "handle resource deletion through #destroy on an existing resource" do # {{{
|
108
|
+
@user = User.find(1)
|
109
|
+
@user.destroy
|
110
|
+
@user.active.should be_false
|
111
|
+
end # }}}
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.join(File.dirname(__FILE__), "../spec_helper.rb")
|
3
|
+
|
4
|
+
describe Her::Model::Relationships do
|
5
|
+
context "setting relationships" do
|
6
|
+
before do # {{{
|
7
|
+
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
8
|
+
class User
|
9
|
+
include Her::Model
|
10
|
+
end
|
11
|
+
end # }}}
|
12
|
+
|
13
|
+
it "handles a single 'has_many' relationship" do # {{{
|
14
|
+
User.has_many :comments
|
15
|
+
User.relationships[:has_many].should == [{ :name => :comments }]
|
16
|
+
end # }}}
|
17
|
+
|
18
|
+
it "handles multiples 'has_many' relationship" do # {{{
|
19
|
+
User.has_many :comments
|
20
|
+
User.has_many :posts
|
21
|
+
User.relationships[:has_many].should == [{ :name => :comments }, { :name => :posts }]
|
22
|
+
end # }}}
|
23
|
+
|
24
|
+
it "handles a single 'has_one' relationship" do # {{{
|
25
|
+
User.has_one :category
|
26
|
+
User.relationships[:has_one].should == [{ :name => :category }]
|
27
|
+
end # }}}
|
28
|
+
|
29
|
+
it "handles multiples 'has_one' relationship" do # {{{
|
30
|
+
User.has_one :category
|
31
|
+
User.has_one :role
|
32
|
+
User.relationships[:has_one].should == [{ :name => :category }, { :name => :role }]
|
33
|
+
end # }}}
|
34
|
+
|
35
|
+
it "handles a single belongs_to relationship" do # {{{
|
36
|
+
User.belongs_to :organization
|
37
|
+
User.relationships[:belongs_to].should == [{ :name => :organization }]
|
38
|
+
end # }}}
|
39
|
+
|
40
|
+
it "handles multiples 'belongs_to' relationship" do # {{{
|
41
|
+
User.belongs_to :organization
|
42
|
+
User.belongs_to :family
|
43
|
+
User.relationships[:belongs_to].should == [{ :name => :organization }, { :name => :family }]
|
44
|
+
end # }}}
|
45
|
+
end
|
46
|
+
|
47
|
+
context "handling relationships" do
|
48
|
+
before do # {{{
|
49
|
+
Her::API.setup :base_uri => "https://api.example.com"
|
50
|
+
FakeWeb.register_uri(:get, "https://api.example.com/users/1", :body => { :data => { :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)
|
51
|
+
FakeWeb.register_uri(:get, "https://api.example.com/users/2", :body => { :data => { :id => 2, :name => "Lindsay Fünke", :organization_id => 1 } }.to_json)
|
52
|
+
FakeWeb.register_uri(:get, "https://api.example.com/users/2/comments", :body => { :data => [{ :id => 4, :body => "They're having a FIRESALE?" }, { :id => 5, :body => "Is this the tiny town from Footloose?" }] }.to_json)
|
53
|
+
FakeWeb.register_uri(:get, "https://api.example.com/users/2/role", :body => { :data => { :id => 2, :body => "User" } }.to_json)
|
54
|
+
FakeWeb.register_uri(:get, "https://api.example.com/organizations/1", :body => { :data => { :id => 1, :name => "Bluth Company" } }.to_json)
|
55
|
+
|
56
|
+
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
57
|
+
class User
|
58
|
+
include Her::Model
|
59
|
+
has_many :comments
|
60
|
+
has_one :role
|
61
|
+
belongs_to :organization
|
62
|
+
end
|
63
|
+
|
64
|
+
Object.instance_eval { remove_const :Organization } if Object.const_defined?(:Organization)
|
65
|
+
class Organization
|
66
|
+
include Her::Model
|
67
|
+
end
|
68
|
+
|
69
|
+
Object.instance_eval { remove_const :Comment } if Object.const_defined?(:Comment)
|
70
|
+
class Comment
|
71
|
+
include Her::Model
|
72
|
+
end
|
73
|
+
|
74
|
+
Object.instance_eval { remove_const :Role } if Object.const_defined?(:Role)
|
75
|
+
class Role
|
76
|
+
include Her::Model
|
77
|
+
end
|
78
|
+
|
79
|
+
@user_with_included_data = User.find(1)
|
80
|
+
@user_without_included_data = User.find(2)
|
81
|
+
end # }}}
|
82
|
+
|
83
|
+
it "maps an array of included data through has_many" do # {{{
|
84
|
+
@user_with_included_data.comments.length.should == 2
|
85
|
+
@user_with_included_data.comments.first.id.should == 2
|
86
|
+
@user_with_included_data.comments.first.body.should == "Tobias, you blow hard!"
|
87
|
+
end # }}}
|
88
|
+
|
89
|
+
it "fetches data that was not included through has_many" do # {{{
|
90
|
+
@user_without_included_data.comments.length.should == 2
|
91
|
+
@user_without_included_data.comments.first.id.should == 4
|
92
|
+
@user_without_included_data.comments.first.body.should == "They're having a FIRESALE?"
|
93
|
+
end # }}}
|
94
|
+
|
95
|
+
it "maps an array of included data through has_one" do # {{{
|
96
|
+
@user_with_included_data.role.id.should == 1
|
97
|
+
@user_with_included_data.role.body.should == "Admin"
|
98
|
+
end # }}}
|
99
|
+
|
100
|
+
it "fetches data that was not included through has_one" do # {{{
|
101
|
+
@user_without_included_data.role.id.should == 2
|
102
|
+
@user_without_included_data.role.body.should == "User"
|
103
|
+
end # }}}
|
104
|
+
|
105
|
+
it "maps an array of included data through belongs_to" do # {{{
|
106
|
+
@user_with_included_data.organization.id.should == 1
|
107
|
+
@user_with_included_data.organization.name.should == "Bluth Company"
|
108
|
+
end # }}}
|
109
|
+
|
110
|
+
it "fetches data that was not included through belongs_to" do # {{{
|
111
|
+
@user_without_included_data.organization.id.should == 1
|
112
|
+
@user_without_included_data.organization.name.should == "Bluth Company"
|
113
|
+
end # }}}
|
114
|
+
end
|
115
|
+
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.1.
|
4
|
+
version: 0.1.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-04-
|
12
|
+
date: 2012-04-15 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -173,12 +173,16 @@ files:
|
|
173
173
|
- lib/her/api.rb
|
174
174
|
- lib/her/model.rb
|
175
175
|
- lib/her/model/base.rb
|
176
|
+
- lib/her/model/hooks.rb
|
176
177
|
- lib/her/model/http.rb
|
177
178
|
- lib/her/model/orm.rb
|
178
179
|
- lib/her/model/relationships.rb
|
179
180
|
- lib/her/version.rb
|
180
181
|
- spec/api_spec.rb
|
181
|
-
- spec/
|
182
|
+
- spec/model/hooks_spec.rb
|
183
|
+
- spec/model/http_spec.rb
|
184
|
+
- spec/model/orm_spec.rb
|
185
|
+
- spec/model/relationships_spec.rb
|
182
186
|
- spec/spec_helper.rb
|
183
187
|
homepage: https://github.com/remiprev/her
|
184
188
|
licenses: []
|
@@ -194,7 +198,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
194
198
|
version: '0'
|
195
199
|
segments:
|
196
200
|
- 0
|
197
|
-
hash:
|
201
|
+
hash: 1685847524195482169
|
198
202
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
199
203
|
none: false
|
200
204
|
requirements:
|
@@ -203,7 +207,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
203
207
|
version: '0'
|
204
208
|
segments:
|
205
209
|
- 0
|
206
|
-
hash:
|
210
|
+
hash: 1685847524195482169
|
207
211
|
requirements: []
|
208
212
|
rubyforge_project:
|
209
213
|
rubygems_version: 1.8.18
|
@@ -213,6 +217,9 @@ summary: A simple Representational State Transfer-based Hypertext Transfer Proto
|
|
213
217
|
Object Relational Mapper. Her?
|
214
218
|
test_files:
|
215
219
|
- spec/api_spec.rb
|
216
|
-
- spec/
|
220
|
+
- spec/model/hooks_spec.rb
|
221
|
+
- spec/model/http_spec.rb
|
222
|
+
- spec/model/orm_spec.rb
|
223
|
+
- spec/model/relationships_spec.rb
|
217
224
|
- spec/spec_helper.rb
|
218
225
|
has_rdoc:
|
data/spec/model_spec.rb
DELETED
@@ -1,336 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require File.join(File.dirname(__FILE__), "spec_helper.rb")
|
3
|
-
|
4
|
-
describe Her::Model do
|
5
|
-
describe Her::Model::HTTP do
|
6
|
-
context "binding a model with an API" do
|
7
|
-
it "binds a model to an instance of Her::API" do # {{{
|
8
|
-
@api = Her::API.new
|
9
|
-
@api.setup :base_uri => "https://api.example.com"
|
10
|
-
|
11
|
-
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
12
|
-
class User
|
13
|
-
include Her::Model
|
14
|
-
end
|
15
|
-
User.uses_api @api
|
16
|
-
|
17
|
-
User.class_eval do
|
18
|
-
@her_api.should_not == nil
|
19
|
-
@her_api.base_uri.should == "https://api.example.com"
|
20
|
-
end
|
21
|
-
end # }}}
|
22
|
-
|
23
|
-
it "binds a model directly to Her::API" do # {{{
|
24
|
-
Her::API.setup :base_uri => "https://api.example.com"
|
25
|
-
|
26
|
-
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
27
|
-
class User
|
28
|
-
include Her::Model
|
29
|
-
end
|
30
|
-
|
31
|
-
User.class_eval do
|
32
|
-
@her_api.should_not == nil
|
33
|
-
@her_api.base_uri.should == "https://api.example.com"
|
34
|
-
end
|
35
|
-
end # }}}
|
36
|
-
|
37
|
-
it "binds two models to two different instances of Her::API" do # {{{
|
38
|
-
@api1 = Her::API.new
|
39
|
-
@api1.setup :base_uri => "https://api1.example.com"
|
40
|
-
|
41
|
-
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
42
|
-
class User; include Her::Model; end
|
43
|
-
User.uses_api @api1
|
44
|
-
|
45
|
-
User.class_eval do
|
46
|
-
@her_api.base_uri.should == "https://api1.example.com"
|
47
|
-
end
|
48
|
-
|
49
|
-
@api2 = Her::API.new
|
50
|
-
@api2.setup :base_uri => "https://api2.example.com"
|
51
|
-
|
52
|
-
Object.instance_eval { remove_const :Comment } if Object.const_defined?(:Comment)
|
53
|
-
class Comment; include Her::Model; end
|
54
|
-
Comment.uses_api @api2
|
55
|
-
|
56
|
-
Comment.class_eval do
|
57
|
-
@her_api.base_uri.should == "https://api2.example.com"
|
58
|
-
end
|
59
|
-
end # }}}
|
60
|
-
|
61
|
-
it "binds one model to Her::API and another one to an instance of Her::API" do # {{{
|
62
|
-
Her::API.setup :base_uri => "https://api1.example.com"
|
63
|
-
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
64
|
-
class User; include Her::Model; end
|
65
|
-
|
66
|
-
User.class_eval do
|
67
|
-
@her_api.base_uri.should == "https://api1.example.com"
|
68
|
-
end
|
69
|
-
|
70
|
-
@api = Her::API.new
|
71
|
-
@api.setup :base_uri => "https://api2.example.com"
|
72
|
-
|
73
|
-
Object.instance_eval { remove_const :Comment } if Object.const_defined?(:Comment)
|
74
|
-
class Comment; include Her::Model; end
|
75
|
-
Comment.uses_api @api
|
76
|
-
|
77
|
-
Comment.class_eval do
|
78
|
-
@her_api.base_uri.should == "https://api2.example.com"
|
79
|
-
end
|
80
|
-
end # }}}
|
81
|
-
end
|
82
|
-
|
83
|
-
context "making HTTP requests" do
|
84
|
-
before do # {{{
|
85
|
-
@api = Her::API.new
|
86
|
-
@api.setup :base_uri => "https://api.example.com"
|
87
|
-
FakeWeb.register_uri(:get, "https://api.example.com/users", :body => { :data => [{ :id => 1 }] }.to_json)
|
88
|
-
FakeWeb.register_uri(:get, "https://api.example.com/users?page=2", :body => { :data => [{ :id => 2 }] }.to_json)
|
89
|
-
FakeWeb.register_uri(:get, "https://api.example.com/users/popular", :body => { :data => [{ :id => 1 }, { :id => 2 }] }.to_json)
|
90
|
-
FakeWeb.register_uri(:get, "https://api.example.com/users/1", :body => { :data => { :id => 1 } }.to_json)
|
91
|
-
FakeWeb.register_uri(:post, "https://api.example.com/users", :body => { :data => [{ :id => 3 }] }.to_json)
|
92
|
-
FakeWeb.register_uri(:put, "https://api.example.com/users/4", :body => { :data => [{ :id => 4 }] }.to_json)
|
93
|
-
FakeWeb.register_uri(:patch, "https://api.example.com/users/6", :body => { :data => [{ :id => 6 }] }.to_json)
|
94
|
-
FakeWeb.register_uri(:delete, "https://api.example.com/users/5", :body => { :data => [{ :id => 5 }] }.to_json)
|
95
|
-
|
96
|
-
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
97
|
-
class User
|
98
|
-
include Her::Model
|
99
|
-
end
|
100
|
-
User.uses_api @api
|
101
|
-
end # }}}
|
102
|
-
|
103
|
-
it "handle raw GET" do # {{{
|
104
|
-
User.get_raw("/users") do |parsed_data|
|
105
|
-
parsed_data[:data].should == [{ :id => 1 }]
|
106
|
-
end
|
107
|
-
end # }}}
|
108
|
-
|
109
|
-
it "handle raw POST" do # {{{
|
110
|
-
User.post_raw("/users") do |parsed_data|
|
111
|
-
parsed_data[:data].should == [{ :id => 3 }]
|
112
|
-
end
|
113
|
-
end # }}}
|
114
|
-
|
115
|
-
it "handle raw PUT" do # {{{
|
116
|
-
User.put_raw("/users/4") do |parsed_data|
|
117
|
-
parsed_data[:data].should == [{ :id => 4 }]
|
118
|
-
end
|
119
|
-
end # }}}
|
120
|
-
|
121
|
-
it "handle raw PATCH" do # {{{
|
122
|
-
User.patch_raw("/users/6") do |parsed_data|
|
123
|
-
parsed_data[:data].should == [{ :id => 6 }]
|
124
|
-
end
|
125
|
-
end # }}}
|
126
|
-
|
127
|
-
it "handle raw DELETE" do # {{{
|
128
|
-
User.delete_raw("/users/5") do |parsed_data|
|
129
|
-
parsed_data[:data].should == [{ :id => 5 }]
|
130
|
-
end
|
131
|
-
end # }}}
|
132
|
-
|
133
|
-
it "handle querystring parameters" do # {{{
|
134
|
-
User.get_raw("/users", :page => 2) do |parsed_data|
|
135
|
-
parsed_data[:data].should == [{ :id => 2 }]
|
136
|
-
end
|
137
|
-
end # }}}
|
138
|
-
|
139
|
-
it "handle GET collection" do # {{{
|
140
|
-
@users = User.get_collection("/users/popular")
|
141
|
-
@users.length.should == 2
|
142
|
-
@users.first.id.should == 1
|
143
|
-
end # }}}
|
144
|
-
|
145
|
-
it "handle GET resource" do # {{{
|
146
|
-
@user = User.get_resource("/users/1")
|
147
|
-
@user.id.should == 1
|
148
|
-
end # }}}
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
describe Her::Model::ORM do
|
153
|
-
context "mapping data to Ruby objects" do
|
154
|
-
before do # {{{
|
155
|
-
@api = Her::API.new
|
156
|
-
@api.setup :base_uri => "https://api.example.com"
|
157
|
-
FakeWeb.register_uri(:get, "https://api.example.com/users/1", :body => { :data => { :id => 1, :name => "Tobias Fünke" } }.to_json)
|
158
|
-
FakeWeb.register_uri(:get, "https://api.example.com/users", :body => { :data => [{ :id => 1, :name => "Tobias Fünke" }, { :id => 2, :name => "Lindsay Fünke" }] }.to_json)
|
159
|
-
|
160
|
-
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
161
|
-
class User
|
162
|
-
include Her::Model
|
163
|
-
end
|
164
|
-
User.uses_api @api
|
165
|
-
end # }}}
|
166
|
-
|
167
|
-
it "maps a single resource to a Ruby object" do # {{{
|
168
|
-
@user = User.find(1)
|
169
|
-
@user.id.should == 1
|
170
|
-
@user.name.should == "Tobias Fünke"
|
171
|
-
end # }}}
|
172
|
-
|
173
|
-
it "maps a collection of resources to an array of Ruby objects" do # {{{
|
174
|
-
@users = User.all
|
175
|
-
@users.length.should == 2
|
176
|
-
@users.first.name.should == "Tobias Fünke"
|
177
|
-
end # }}}
|
178
|
-
end
|
179
|
-
|
180
|
-
context "creating resources" do
|
181
|
-
before do # {{{
|
182
|
-
Her::API.setup :base_uri => "https://api.example.com"
|
183
|
-
FakeWeb.register_uri(:post, "https://api.example.com/users", :body => { :data => { :id => 1, :fullname => "Tobias Fünke" } }.to_json)
|
184
|
-
|
185
|
-
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
186
|
-
class User
|
187
|
-
include Her::Model
|
188
|
-
end
|
189
|
-
end # }}}
|
190
|
-
|
191
|
-
it "handle one-line resource creation" do # {{{
|
192
|
-
@user = User.create(:fullname => "Tobias Fünke")
|
193
|
-
@user.id.should == 1
|
194
|
-
@user.fullname.should == "Tobias Fünke"
|
195
|
-
end # }}}
|
196
|
-
|
197
|
-
it "handle resource creation through Model.new + #save" do # {{{
|
198
|
-
@user = User.new(:fullname => "Tobias Fünke")
|
199
|
-
@user.save
|
200
|
-
@user.fullname.should == "Tobias Fünke"
|
201
|
-
end # }}}
|
202
|
-
end
|
203
|
-
|
204
|
-
context "updating resources" do
|
205
|
-
before do # {{{
|
206
|
-
@api = Her::API.new
|
207
|
-
@api.setup :base_uri => "https://api.example.com"
|
208
|
-
FakeWeb.register_uri(:get, "https://api.example.com/users/1", :body => { :data => { :id => 1, :fullname => "Tobias Fünke" } }.to_json)
|
209
|
-
FakeWeb.register_uri(:put, "https://api.example.com/users/1", :body => { :data => { :id => 1, :fullname => "Lindsay Fünke" } }.to_json)
|
210
|
-
|
211
|
-
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
212
|
-
class User
|
213
|
-
include Her::Model
|
214
|
-
end
|
215
|
-
end # }}}
|
216
|
-
|
217
|
-
it "handle resource data update without saving it" do # {{{
|
218
|
-
@user = User.find(1)
|
219
|
-
@user.fullname.should == "Tobias Fünke"
|
220
|
-
@user.fullname = "Kittie Sanchez"
|
221
|
-
@user.fullname.should == "Kittie Sanchez"
|
222
|
-
end # }}}
|
223
|
-
|
224
|
-
it "handle resource update through #save on an existing resource" do # {{{
|
225
|
-
@user = User.find(1)
|
226
|
-
@user.fullname = "Lindsay Fünke"
|
227
|
-
@user.save
|
228
|
-
@user.fullname.should == "Lindsay Fünke"
|
229
|
-
end # }}}
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
233
|
-
describe Her::Model::Relationships do
|
234
|
-
context "setting relationships" do
|
235
|
-
before do # {{{
|
236
|
-
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
237
|
-
class User
|
238
|
-
include Her::Model
|
239
|
-
end
|
240
|
-
end # }}}
|
241
|
-
|
242
|
-
it "handles a single 'has_many' relationship" do # {{{
|
243
|
-
User.has_many :comments
|
244
|
-
User.relationships[:has_many].should == [{ :name => :comments }]
|
245
|
-
end # }}}
|
246
|
-
|
247
|
-
it "handles multiples 'has_many' relationship" do # {{{
|
248
|
-
User.has_many :comments
|
249
|
-
User.has_many :posts
|
250
|
-
User.relationships[:has_many].should == [{ :name => :comments }, { :name => :posts }]
|
251
|
-
end # }}}
|
252
|
-
|
253
|
-
it "handles a single 'has_one' relationship" do # {{{
|
254
|
-
User.has_one :category
|
255
|
-
User.relationships[:has_one].should == [{ :name => :category }]
|
256
|
-
end # }}}
|
257
|
-
|
258
|
-
it "handles multiples 'has_one' relationship" do # {{{
|
259
|
-
User.has_one :category
|
260
|
-
User.has_one :role
|
261
|
-
User.relationships[:has_one].should == [{ :name => :category }, { :name => :role }]
|
262
|
-
end # }}}
|
263
|
-
|
264
|
-
it "handles a single belongs_to relationship" do # {{{
|
265
|
-
User.belongs_to :organization
|
266
|
-
User.relationships[:belongs_to].should == [{ :name => :organization }]
|
267
|
-
end # }}}
|
268
|
-
|
269
|
-
it "handles multiples 'belongs_to' relationship" do # {{{
|
270
|
-
User.belongs_to :organization
|
271
|
-
User.belongs_to :family
|
272
|
-
User.relationships[:belongs_to].should == [{ :name => :organization }, { :name => :family }]
|
273
|
-
end # }}}
|
274
|
-
end
|
275
|
-
|
276
|
-
context "handling relationships" do
|
277
|
-
before do # {{{
|
278
|
-
Her::API.setup :base_uri => "https://api.example.com"
|
279
|
-
FakeWeb.register_uri(:get, "https://api.example.com/users/1", :body => { :data => { :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)
|
280
|
-
FakeWeb.register_uri(:get, "https://api.example.com/users/2", :body => { :data => { :id => 2, :name => "Lindsay Fünke", :organization_id => 1 } }.to_json)
|
281
|
-
FakeWeb.register_uri(:get, "https://api.example.com/users/2/comments", :body => { :data => [{ :id => 4, :body => "They're having a FIRESALE?" }, { :id => 5, :body => "Is this the tiny town from Footloose?" }] }.to_json)
|
282
|
-
FakeWeb.register_uri(:get, "https://api.example.com/users/2/role", :body => { :data => { :id => 2, :body => "User" } }.to_json)
|
283
|
-
FakeWeb.register_uri(:get, "https://api.example.com/organizations/1", :body => { :data => { :id => 1, :name => "Bluth Company" } }.to_json)
|
284
|
-
|
285
|
-
Object.instance_eval { remove_const :User } if Object.const_defined?(:User)
|
286
|
-
class User
|
287
|
-
include Her::Model
|
288
|
-
has_many :comments
|
289
|
-
has_one :role
|
290
|
-
belongs_to :organization
|
291
|
-
end
|
292
|
-
|
293
|
-
Object.instance_eval { remove_const :Organization } if Object.const_defined?(:Organization)
|
294
|
-
class Organization
|
295
|
-
include Her::Model
|
296
|
-
end
|
297
|
-
|
298
|
-
Object.instance_eval { remove_const :Comment } if Object.const_defined?(:Comment)
|
299
|
-
class Comment
|
300
|
-
include Her::Model
|
301
|
-
end
|
302
|
-
|
303
|
-
Object.instance_eval { remove_const :Role } if Object.const_defined?(:Role)
|
304
|
-
class Role
|
305
|
-
include Her::Model
|
306
|
-
end
|
307
|
-
end # }}}
|
308
|
-
|
309
|
-
it "maps an array of included data" do # {{{
|
310
|
-
@user = User.find(1)
|
311
|
-
@user.comments.length.should == 2
|
312
|
-
@user.comments.first.id.should == 2
|
313
|
-
@user.comments.first.body.should == "Tobias, you blow hard!"
|
314
|
-
|
315
|
-
@user.role.id.should == 1
|
316
|
-
@user.role.body.should == "Admin"
|
317
|
-
|
318
|
-
@user.organization.id.should == 1
|
319
|
-
@user.organization.name.should == "Bluth Company"
|
320
|
-
end # }}}
|
321
|
-
|
322
|
-
it "fetches data that was not included" do # {{{
|
323
|
-
@user = User.find(2)
|
324
|
-
@user.comments.length.should == 2
|
325
|
-
@user.comments.first.id.should == 4
|
326
|
-
@user.comments.first.body.should == "They're having a FIRESALE?"
|
327
|
-
|
328
|
-
@user.role.id.should == 2
|
329
|
-
@user.role.body.should == "User"
|
330
|
-
|
331
|
-
@user.organization.id.should == 1
|
332
|
-
@user.organization.name.should == "Bluth Company"
|
333
|
-
end # }}}
|
334
|
-
end
|
335
|
-
end
|
336
|
-
end
|