her 0.1.5 → 0.1.6
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 +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
|