datamapper4rail 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +6 -0
- data/Rakefile +14 -9
- data/generators/rspec_dm_model/rspec_dm_model_generator.rb +2 -2
- data/generators/rspec_dm_model/templates/model.rb +7 -4
- data/lib/datamapper4rails.rb +1 -53
- data/lib/datamapper4rails/adapters/restful_adapter.rb +104 -51
- data/lib/datamapper4rails/database_config.rb +7 -2
- data/lib/datamapper4rails/datamapper_store.rb +6 -5
- data/lib/datamapper4rails/identity_maps.rb +23 -0
- data/lib/datamapper4rails/preload_models.rb +26 -0
- data/lib/datamapper4rails/restful_transactions.rb +4 -3
- data/lib/datamapper4rails/version.rb +1 -1
- data/spec/datamapper_store_spec.rb +127 -0
- data/spec/restful_adapter_spec.rb +187 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +21 -0
- metadata +8 -2
data/Manifest.txt
CHANGED
@@ -18,5 +18,11 @@ lib/datamapper4rails/adapters/base_adapter.rb
|
|
18
18
|
lib/datamapper4rails/adapters/restful_adapter.rb
|
19
19
|
lib/datamapper4rails/database_config.rb
|
20
20
|
lib/datamapper4rails/datamapper_store.rb
|
21
|
+
lib/datamapper4rails/identity_maps.rb
|
22
|
+
lib/datamapper4rails/preload_models.rb
|
21
23
|
lib/datamapper4rails/restful_transactions.rb
|
22
24
|
lib/datamapper4rails/version.rb
|
25
|
+
spec/datamapper_store_spec.rb
|
26
|
+
spec/restful_adapter_spec.rb
|
27
|
+
spec/spec.opts
|
28
|
+
spec/spec_helper.rb
|
data/Rakefile
CHANGED
@@ -2,6 +2,11 @@
|
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'hoe'
|
5
|
+
|
6
|
+
require 'spec'
|
7
|
+
require 'spec/rake/spectask'
|
8
|
+
require 'pathname'
|
9
|
+
|
5
10
|
require './lib/datamapper4rails/version.rb'
|
6
11
|
|
7
12
|
Hoe.new('datamapper4rail', Datamapper4rails::VERSION) do |p|
|
@@ -17,16 +22,16 @@ task :install => [:clean, :package] do
|
|
17
22
|
sh "gem install --local #{gem} --no-ri --no-rdoc"
|
18
23
|
end
|
19
24
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
desc 'Run specifications'
|
26
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
27
|
+
if File.exists?('spec/spec.opts')
|
28
|
+
t.spec_opts << '--options' << 'spec/spec.opts'
|
29
|
+
end
|
30
|
+
t.spec_files = Pathname.glob('./spec/**/*_spec.rb')
|
31
|
+
end
|
27
32
|
|
28
|
-
|
33
|
+
require 'yard'
|
29
34
|
|
30
|
-
|
35
|
+
YARD::Rake::YardocTask.new
|
31
36
|
|
32
37
|
# vim: syntax=Ruby
|
@@ -2,13 +2,13 @@ require 'rails_generator/generators/components/model/model_generator'
|
|
2
2
|
require 'active_record'
|
3
3
|
require File.dirname(__FILE__) + '/../rspec_default_values'
|
4
4
|
|
5
|
-
class
|
5
|
+
class RspecDmModelGenerator <ModelGenerator
|
6
6
|
|
7
7
|
def manifest
|
8
8
|
record do |m|
|
9
9
|
|
10
10
|
# Check for class naming collisions.
|
11
|
-
m.class_collisions class_path, class_name
|
11
|
+
#m.class_collisions class_path, class_name
|
12
12
|
|
13
13
|
# Model, spec, and fixture directories.
|
14
14
|
m.directory File.join('app/models', class_path)
|
@@ -1,16 +1,19 @@
|
|
1
1
|
class <%= class_name %>
|
2
2
|
include DataMapper::Resource
|
3
|
+
<% if options[:add_constraints] -%>
|
4
|
+
include DataMapper::Constraints
|
5
|
+
<% end -%>
|
6
|
+
|
3
7
|
property :id, Serial
|
4
8
|
|
5
9
|
<% for attribute in attributes -%>
|
6
|
-
property :<%= attribute.name %>, <%= attribute.type.to_s.camelize %>, :nullable => false <% if attribute.type == :string or attribute.type == :text -%>, :format => /^[^<'&">]*$/<% if attribute.type == :string %>, :length =>
|
7
|
-
<% else -%>
|
10
|
+
property :<%= attribute.name %>, <%= attribute.type.to_s.camelize %>, :nullable => false <% if attribute.type == :string or attribute.type == :text -%>, :format => /^[^<'&">]*$/<% if attribute.type == :string %>, :length => 255<% end -%>
|
11
|
+
<% #else -%>
|
8
12
|
|
9
13
|
<% end -%>
|
10
14
|
<% end -%>
|
11
15
|
<% unless options[:skip_timestamps] %>
|
12
|
-
|
13
|
-
property :updated_at, DateTime, :nullable => false
|
16
|
+
timestamps :at
|
14
17
|
|
15
18
|
<% end -%>
|
16
19
|
end
|
data/lib/datamapper4rails.rb
CHANGED
@@ -1,53 +1 @@
|
|
1
|
-
require '
|
2
|
-
def config_file()
|
3
|
-
RAILS_ROOT + "/config/database.yml"
|
4
|
-
end
|
5
|
-
|
6
|
-
def create_connection()
|
7
|
-
conf = config.dup
|
8
|
-
if repositories = conf.delete(:repositories)
|
9
|
-
repositories.each do |repo, conf|
|
10
|
-
::DataMapper.setup(repo, conf) unless conf.empty?
|
11
|
-
end
|
12
|
-
else
|
13
|
-
::DataMapper.setup(:default, conf) unless conf.empty?
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def get_config_for_environment
|
18
|
-
if hash = full_config[RAILS_ENV]
|
19
|
-
symbolize_keys(hash)
|
20
|
-
elsif hash = full_config[RAILS_ENV.to_sym]
|
21
|
-
hash
|
22
|
-
else
|
23
|
-
raise ArgumentError, "missing environment '#{RAILS_ENV}' in config file #{config_file}"
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def full_config
|
28
|
-
@full_config ||= YAML::load(ERB.new(IO.read(config_file)).result)
|
29
|
-
end
|
30
|
-
|
31
|
-
def config
|
32
|
-
@config ||= get_config_for_environment
|
33
|
-
end
|
34
|
-
|
35
|
-
def symbolize_keys(h)
|
36
|
-
config = {}
|
37
|
-
|
38
|
-
h.each do |k, v|
|
39
|
-
if k == 'port'
|
40
|
-
config[k.to_sym] = v.to_i
|
41
|
-
elsif k == 'adapter' && v == 'postgresql'
|
42
|
-
config[k.to_sym] = 'postgres'
|
43
|
-
elsif v.is_a?(Hash)
|
44
|
-
config[k.to_sym] = symbolize_keys(v)
|
45
|
-
else
|
46
|
-
config[k.to_sym] = v
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
config
|
51
|
-
end
|
52
|
-
|
53
|
-
create_connection()
|
1
|
+
require 'datamapper4rails/database_config'
|
@@ -1,7 +1,8 @@
|
|
1
|
-
require 'adapters/
|
1
|
+
require 'datamapper4rails/adapters/base_adapter'
|
2
2
|
require 'net/http'
|
3
3
|
require 'extlib/inflection'
|
4
4
|
require 'extlib/module'
|
5
|
+
require 'dm-serializer'
|
5
6
|
|
6
7
|
module DataMapper
|
7
8
|
module Adapters
|
@@ -44,6 +45,18 @@ module DataMapper
|
|
44
45
|
raise "compound keys are not supported"
|
45
46
|
end
|
46
47
|
end
|
48
|
+
|
49
|
+
def single_entity_query?(query)
|
50
|
+
query.conditions.count {|c| c[1].key? and c[0] == :eql} == query.model.key.size
|
51
|
+
end
|
52
|
+
|
53
|
+
def attributes_to_xml(name, attributes)
|
54
|
+
xml = "<#{name}>"
|
55
|
+
attributes.each do |attr, val|
|
56
|
+
xml += "<#{attr.name}>#{val}</#{attr.name}>"
|
57
|
+
end
|
58
|
+
xml += "</#{name}>"
|
59
|
+
end
|
47
60
|
|
48
61
|
def http_get(uri)
|
49
62
|
send_request do |http|
|
@@ -66,13 +79,16 @@ module DataMapper
|
|
66
79
|
end
|
67
80
|
end
|
68
81
|
|
69
|
-
def http_put(uri, data =
|
82
|
+
def http_put(uri, data = nil)
|
70
83
|
send_request do |http|
|
71
|
-
request = Net::HTTP::Put.new(uri
|
84
|
+
request = Net::HTTP::Put.new(uri, {
|
85
|
+
'content-type' => 'application/xml',
|
86
|
+
'content-length' => data.length.to_s
|
87
|
+
})
|
72
88
|
request.basic_auth(@uri[:login],
|
73
89
|
@uri[:password]) unless @uri[:login].blank?
|
74
|
-
request.set_form_data(data)
|
75
|
-
http.request(request)
|
90
|
+
# request.set_form_data(data)
|
91
|
+
http.request(request, data)
|
76
92
|
end
|
77
93
|
end
|
78
94
|
|
@@ -112,14 +128,25 @@ module DataMapper
|
|
112
128
|
#puts
|
113
129
|
#puts "elements"
|
114
130
|
#p elements
|
131
|
+
#p query
|
132
|
+
#p model
|
115
133
|
resource = model.load(model.properties.collect do |f|
|
116
134
|
elements[f.name]
|
117
135
|
end, query)
|
118
136
|
resource.send("#{keys_from_query(query)[0].name}=".to_sym, elements[keys_from_query(query)[0].name] )
|
119
|
-
#p resource
|
120
|
-
|
121
|
-
|
137
|
+
# p resource
|
138
|
+
#p associations
|
139
|
+
associations.each do |name, association|
|
140
|
+
# puts "asso"
|
141
|
+
# p model
|
142
|
+
# p name
|
143
|
+
# p association
|
144
|
+
#p model.relationships
|
145
|
+
is_one_to_one = false
|
146
|
+
asso_model =
|
122
147
|
if rel = model.relationships[name]
|
148
|
+
#puts "rel"
|
149
|
+
#p rel
|
123
150
|
if rel.child_model == model
|
124
151
|
rel.parent_model
|
125
152
|
else
|
@@ -129,17 +156,38 @@ module DataMapper
|
|
129
156
|
#::Extlib::Inflection.constantize(::Extlib::Inflection.classify(name))
|
130
157
|
# model.find_const(::Extlib::Inflection.classify(name))
|
131
158
|
end
|
159
|
+
# p asso_model
|
132
160
|
if resource.respond_to? "#{name}=".to_sym
|
161
|
+
# puts
|
162
|
+
# puts "association"
|
163
|
+
# puts name
|
164
|
+
# p model
|
165
|
+
# p asso_model
|
133
166
|
resource.send("#{name}=".to_sym,
|
134
|
-
parse_resource(association,
|
135
|
-
::DataMapper::Query.new(query.repository,
|
167
|
+
parse_resource(association, asso_model,
|
168
|
+
::DataMapper::Query.new(query.repository, asso_model ))) unless asso_model.nil?
|
136
169
|
else
|
137
170
|
resource.send(("#{name.to_s.pluralize}<" + "<").to_sym,
|
138
|
-
parse_resource(association,
|
139
|
-
::DataMapper::Query.new(query.repository,
|
171
|
+
parse_resource(association, asso_model,
|
172
|
+
::DataMapper::Query.new(query.repository, asso_model ))) unless asso_model.nil?
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
#puts "many 2 many"
|
177
|
+
#p many_to_many
|
178
|
+
many_to_many.each do |name, many|
|
179
|
+
if model.relationships[name]
|
180
|
+
# TODO
|
181
|
+
else
|
182
|
+
# p ::Extlib::Inflection.classify(name.to_s.singularize)
|
183
|
+
many_model = Object.const_get(::Extlib::Inflection.classify(name.to_s.singularize))
|
184
|
+
resource.send(name).send(("<" + "<").to_sym,
|
185
|
+
parse_resource(many, many_model,
|
186
|
+
::DataMapper::Query.new(query.repository, many_model ))) unless many_model.nil?
|
140
187
|
end
|
141
188
|
end
|
142
189
|
resource.instance_variable_set(:@new_record, false)
|
190
|
+
#p resource
|
143
191
|
resource
|
144
192
|
end
|
145
193
|
|
@@ -168,65 +216,70 @@ module DataMapper
|
|
168
216
|
|
169
217
|
# @see BaseAdapter
|
170
218
|
def read_resource(query)
|
171
|
-
|
172
|
-
|
219
|
+
key = key_value_from_query(query)
|
220
|
+
uri = "/#{resource_name_from_query(query).pluralize}/#{key}.xml"
|
221
|
+
logger.debug { "get #{uri}" }
|
222
|
+
response = http_get(uri)
|
223
|
+
if response.kind_of?(Net::HTTPSuccess)
|
224
|
+
logger.debug { response.body.to_s }
|
225
|
+
parse_resource(REXML::Document::new(response.body).root,
|
226
|
+
query.model,
|
227
|
+
query)
|
173
228
|
else
|
174
|
-
|
175
|
-
|
229
|
+
#TODO may act on different response codes differently
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# @see BaseAdapter
|
234
|
+
def read_resources(query)
|
235
|
+
if single_entity_query?(query)
|
236
|
+
[read_resource(query)]
|
237
|
+
else
|
238
|
+
uri = "/#{resource_name_from_query(query).pluralize}.xml"
|
176
239
|
logger.debug { "get #{uri}" }
|
177
240
|
response = http_get(uri)
|
178
241
|
if response.kind_of?(Net::HTTPSuccess)
|
179
|
-
|
180
|
-
|
181
|
-
|
242
|
+
result = []
|
243
|
+
logger.debug { response.body.to_s }
|
244
|
+
REXML::Document::new(response.body).root.each do |element|
|
245
|
+
result << parse_resource(element,
|
246
|
+
query.model,
|
247
|
+
query)
|
248
|
+
end
|
249
|
+
result
|
182
250
|
else
|
183
251
|
#TODO may act on different response codes differently
|
184
252
|
end
|
185
253
|
end
|
186
254
|
end
|
187
255
|
|
188
|
-
# @see BaseAdapter
|
189
|
-
def read_resources(query)
|
190
|
-
# raise "not implemented"
|
191
|
-
[read_resource(query)]
|
192
|
-
end
|
193
|
-
|
194
256
|
# @overwrite BaseAdapter
|
195
257
|
def update(attributes, query)
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
258
|
+
if query.limit == 1 or single_entity_query?(query)
|
259
|
+
xml = attributes_to_xml(resource_name_from_query(query), attributes)
|
260
|
+
key = key_value_from_query(query)
|
261
|
+
uri = "/#{resource_name_from_query(query).pluralize}/#{key}.xml"
|
262
|
+
logger.debug { "put #{uri}" }
|
263
|
+
response = http_put(uri, xml)
|
264
|
+
response.kind_of?(Net::HTTPSuccess)
|
265
|
+
else
|
266
|
+
super
|
200
267
|
end
|
201
|
-
key = key_value_from_query(query)
|
202
|
-
uri = "/#{name.pluralize}/#{key}.xml"
|
203
|
-
logger.debug { "put #{uri}" }
|
204
|
-
response = http_put(uri, params)
|
205
|
-
response.kind_of?(Net::HTTPSuccess)
|
206
268
|
end
|
207
269
|
|
208
270
|
# @see BaseAdapter
|
209
271
|
def update_resource(resource, attributes)
|
210
272
|
query = resource.to_query
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
attributes.each do |attr, val|
|
217
|
-
params["#{name}[#{attr.name}]"]=val
|
218
|
-
end
|
219
|
-
key = key_value_from_query(query)
|
220
|
-
logger.debug {resource.to_xml}
|
221
|
-
response = http_put("/#{resource_name_from_query(query).pluralize}/#{key}.xml", params)
|
222
|
-
response.kind_of?(Net::HTTPSuccess)
|
223
|
-
end
|
273
|
+
xml = attributes_to_xml(resource.name, attributes)
|
274
|
+
key = key_value_from_query(query)
|
275
|
+
logger.debug {resource.to_xml}
|
276
|
+
response = http_put("/#{resource_name_from_query(query).pluralize}/#{key}.xml", xml)
|
277
|
+
response.kind_of?(Net::HTTPSuccess)
|
224
278
|
end
|
225
|
-
|
279
|
+
|
226
280
|
# @overwrite BaseAdapter
|
227
281
|
def delete(query)
|
228
|
-
|
229
|
-
if query.limit == 1
|
282
|
+
if query.limit == 1 or single_entity_query?(query)
|
230
283
|
name = resource_name_from_query(query)
|
231
284
|
key = key_value_from_query(query)
|
232
285
|
uri = "/#{name.pluralize}/#{key}.xml"
|
@@ -241,7 +294,7 @@ module DataMapper
|
|
241
294
|
# @see BaseAdapter
|
242
295
|
def delete_resource(resource)
|
243
296
|
name = resource.name
|
244
|
-
key = key_value_from_query(resource.
|
297
|
+
key = key_value_from_query(resource.to_query)
|
245
298
|
uri = "/#{name.pluralize}/#{key}.xml"
|
246
299
|
logger.debug { "delete #{uri}" }
|
247
300
|
response = http_delete(uri)
|
@@ -6,8 +6,13 @@ end
|
|
6
6
|
|
7
7
|
def create_connection()
|
8
8
|
conf = config.dup
|
9
|
-
repositories = conf.delete(:repositories)
|
10
|
-
|
9
|
+
if repositories = conf.delete(:repositories)
|
10
|
+
repositories.each do |repo, conf|
|
11
|
+
::DataMapper.setup(repo, conf) unless conf.empty?
|
12
|
+
end
|
13
|
+
else
|
14
|
+
::DataMapper.setup(:default, conf) unless conf.empty?
|
15
|
+
end
|
11
16
|
end
|
12
17
|
|
13
18
|
def get_config_for_environment
|
@@ -8,8 +8,10 @@ module ActionController
|
|
8
8
|
super
|
9
9
|
if options.delete(:cache)
|
10
10
|
@@cache = {}
|
11
|
+
else
|
12
|
+
@@cache = nil unless self.class.class_variable_defined? :@@cache
|
11
13
|
end
|
12
|
-
@@session_class = ::DatamapperStore::Session
|
14
|
+
@@session_class = options.delete(:session_class) || ::DatamapperStore::Session unless (self.class.class_variable_defined? :@@session_class and @@session_class)
|
13
15
|
end
|
14
16
|
|
15
17
|
private
|
@@ -32,10 +34,8 @@ module ActionController
|
|
32
34
|
@@session_class.get(sid)
|
33
35
|
end || @@session_class.new(:session_id => sid)
|
34
36
|
session.data = session_data || {}
|
35
|
-
if session.
|
36
|
-
|
37
|
-
@@cache[sid] = session if @@cache
|
38
|
-
end
|
37
|
+
session.updated_at = Time.now if session.dirty?
|
38
|
+
@@cache[sid] = session if @@cache
|
39
39
|
session.save
|
40
40
|
end
|
41
41
|
end
|
@@ -60,6 +60,7 @@ module DatamapperStore
|
|
60
60
|
def data=(data)
|
61
61
|
attribute_set(:data, ::Base64.encode64(Marshal.dump(data)))
|
62
62
|
end
|
63
|
+
|
63
64
|
def data
|
64
65
|
Marshal.load(::Base64.decode64(attribute_get(:data)))
|
65
66
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Datamapper4rails
|
2
|
+
module IdentityMaps
|
3
|
+
|
4
|
+
module Base
|
5
|
+
def self.included(base)
|
6
|
+
base.prepend_around_filter(IdentityMapFilter)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class IdentityMapFilter
|
11
|
+
|
12
|
+
def self.filter(controller)
|
13
|
+
DataMapper.repository(:default) do |*block_args|
|
14
|
+
if block_given?
|
15
|
+
yield (*block_args)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
::ActionController::Base.send(:include, Datamapper4rails::IdentityMaps::Base)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# load all models before each request so relations in datamapper find their classes
|
2
|
+
MODELS = []
|
3
|
+
Dir[RAILS_ROOT + "/app/models/**/*.rb"].each do |model|
|
4
|
+
model.sub!(/.*models\//, '').sub!(/.rb/, '')
|
5
|
+
m = ::Extlib::Inflection.classify(model.to_s)
|
6
|
+
MODELS << m
|
7
|
+
Object.const_get(m)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ModelLoader
|
11
|
+
module Base
|
12
|
+
def self.included(base)
|
13
|
+
base.prepend_before_filter(ModelLoaderFilter)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class ModelLoaderFilter
|
18
|
+
def self.filter(controller)
|
19
|
+
MODELS.each do |model|
|
20
|
+
Object.const_get(model)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
::ActionController::Base.send(:include, ModelLoader::Base)
|
@@ -11,17 +11,18 @@ module Datamapper4rails
|
|
11
11
|
end
|
12
12
|
|
13
13
|
class TransactionFilter
|
14
|
+
|
14
15
|
def self.filter(controller)
|
15
16
|
case controller.request.method
|
16
17
|
when :post, :put, :delete then
|
17
18
|
begin
|
19
|
+
# TODO remove the :default repository and make it more general
|
18
20
|
DataMapper::Transaction.new(DataMapper.repository(:default)) do |*block_args|
|
19
21
|
if block_given?
|
20
22
|
yield (*block_args)
|
21
23
|
# added rollback for all actions which just render
|
22
|
-
# a page with validation errors and do not redirect to
|
23
|
-
# page
|
24
|
-
# restful paradigma
|
24
|
+
# a page (with validation errors) and do not redirect to
|
25
|
+
# another page
|
25
26
|
unless controller.response.redirected_to
|
26
27
|
raise Datamapper4rails::RestfulTransactions::Rollback
|
27
28
|
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
$LOAD_PATH << File.dirname(__FILE__)
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'datamapper4rails/datamapper_store'
|
4
|
+
|
5
|
+
describe DatamapperStore do
|
6
|
+
|
7
|
+
before :each do
|
8
|
+
load 'lib/datamapper4rails/datamapper_store.rb'
|
9
|
+
ActionController::Session::DatamapperStore.send(:class_variable_set, :@@session_class, nil)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should initialize with no cache and default session class' do
|
13
|
+
store = ActionController::Session::DatamapperStore.new(nil)
|
14
|
+
store.class.send(:class_variable_get, :@@cache).should be_nil
|
15
|
+
store.class.send(:class_variable_get, :@@session_class).should == DatamapperStore::Session
|
16
|
+
|
17
|
+
store = ActionController::Session::DatamapperStore.new(nil, :session_class => :some_class)
|
18
|
+
store.class.send(:class_variable_get, :@@cache).should be_nil
|
19
|
+
store.class.send(:class_variable_get, :@@session_class).should == DatamapperStore::Session
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should initialize with cache and default session class' do
|
23
|
+
store = ActionController::Session::DatamapperStore.new(nil, :cache => true)
|
24
|
+
store.class.send(:class_variable_get, :@@cache).instance_of?(Hash).should be_true
|
25
|
+
|
26
|
+
store = ActionController::Session::DatamapperStore.new(nil)
|
27
|
+
store.class.send(:class_variable_get, :@@cache).instance_of?(Hash).should be_true
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should initialize with custom session class' do
|
31
|
+
store = ActionController::Session::DatamapperStore.new(nil, :session_class => Session)
|
32
|
+
store.class.send(:class_variable_get, :@@session_class).should == Session
|
33
|
+
|
34
|
+
store = ActionController::Session::DatamapperStore.new(nil, :session_class => :some_class)
|
35
|
+
store.class.send(:class_variable_get, :@@session_class).should == Session
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'DatamapperStore without cache' do
|
40
|
+
|
41
|
+
def mock_session(stubs={})
|
42
|
+
@mock_session ||= mock(Session, stubs)
|
43
|
+
end
|
44
|
+
|
45
|
+
before :each do
|
46
|
+
load 'lib/datamapper4rails/datamapper_store.rb'
|
47
|
+
ActionController::Session::DatamapperStore.send(:class_variable_set, :@@cache, nil)
|
48
|
+
ActionController::Session::DatamapperStore.send(:class_variable_set, :@@session_class, nil)
|
49
|
+
@store = ActionController::Session::DatamapperStore.new(nil, :session_class => Session)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should get the session data' do
|
53
|
+
Session.stub!(:get)
|
54
|
+
@store.send(:get_session, nil, "sid").should == ["sid",{}]
|
55
|
+
Session.stub!(:get).and_return(mock_session)
|
56
|
+
mock_session.should_receive(:data).and_return({:id => "id"})
|
57
|
+
@store.send(:get_session, nil, "sid").should == ["sid",{:id => "id"}]
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should set the session data on new session' do
|
61
|
+
Session.should_receive(:get)
|
62
|
+
Session.should_receive(:new).with(:session_id => "sid").and_return(mock_session)
|
63
|
+
mock_session.should_receive(:data=).with({})
|
64
|
+
mock_session.should_receive(:dirty?).and_return(true)
|
65
|
+
mock_session.should_receive(:updated_at=)
|
66
|
+
mock_session.should_receive(:save).and_return(true)
|
67
|
+
@store.send(:set_session, nil, "sid", {}).should == true
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should set the session data' do
|
71
|
+
Session.should_receive(:get).and_return(mock_session)
|
72
|
+
mock_session.should_receive(:data=).with({})
|
73
|
+
mock_session.should_receive(:dirty?).and_return(true)
|
74
|
+
mock_session.should_receive(:updated_at=)
|
75
|
+
mock_session.should_receive(:save).and_return(true)
|
76
|
+
@store.send(:set_session, nil, "sid", {}).should == true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe 'DatamapperStore with cache' do
|
81
|
+
|
82
|
+
def mock_session(stubs={})
|
83
|
+
@mock_session ||= mock(Session, stubs)
|
84
|
+
end
|
85
|
+
|
86
|
+
before :each do
|
87
|
+
load 'lib/datamapper4rails/datamapper_store.rb'
|
88
|
+
ActionController::Session::DatamapperStore.send(:class_variable_set, :@@cache, nil)
|
89
|
+
ActionController::Session::DatamapperStore.send(:class_variable_set, :@@session_class, nil)
|
90
|
+
@store = ActionController::Session::DatamapperStore.new(nil, :cache => true, :session_class => Session)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should get the session data from storage' do
|
94
|
+
Session.stub!(:get)
|
95
|
+
@store.send(:get_session, nil, "sid").should == ["sid",{}]
|
96
|
+
Session.stub!(:get).and_return(mock_session)
|
97
|
+
mock_session.should_receive(:data).and_return({:id => "id"})
|
98
|
+
@store.send(:get_session, nil, "sid").should == ["sid",{:id => "id"}]
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should get the session data from cache' do
|
102
|
+
ActionController::Session::DatamapperStore.send(:class_variable_get, :@@cache)["sid"] = mock_session
|
103
|
+
mock_session.should_receive(:data).and_return({:id => "id"})
|
104
|
+
@store.send(:get_session, nil, "sid").should == ["sid",{:id => "id"}]
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should set the session data on new session' do
|
108
|
+
Session.should_receive(:get)
|
109
|
+
Session.should_receive(:new).with(:session_id => "sid").and_return(mock_session)
|
110
|
+
mock_session.should_receive(:data=).with({})
|
111
|
+
mock_session.should_receive(:dirty?).and_return(true)
|
112
|
+
mock_session.should_receive(:updated_at=)
|
113
|
+
mock_session.should_receive(:save).and_return(true)
|
114
|
+
@store.send(:set_session, nil, "sid", {}).should == true
|
115
|
+
ActionController::Session::DatamapperStore.send(:class_variable_get, :@@cache)["sid"].should == mock_session
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'should set the session data' do
|
120
|
+
Session.should_receive(:get).and_return(mock_session)
|
121
|
+
mock_session.should_receive(:data=).with({})
|
122
|
+
mock_session.should_receive(:dirty?).and_return(false)
|
123
|
+
mock_session.should_receive(:save).and_return(true)
|
124
|
+
@store.send(:set_session, nil, "sid", {}).should == true
|
125
|
+
ActionController::Session::DatamapperStore.send(:class_variable_get, :@@cache)["sid"].should == mock_session
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
$LOAD_PATH << File.dirname(__FILE__)
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'datamapper4rails/adapters/restful_adapter'
|
4
|
+
require 'slf4r/ruby_logger'
|
5
|
+
|
6
|
+
class Item
|
7
|
+
include DataMapper::Resource
|
8
|
+
|
9
|
+
property :id, Serial
|
10
|
+
property :name, String
|
11
|
+
|
12
|
+
belongs_to :container
|
13
|
+
end
|
14
|
+
class User
|
15
|
+
include DataMapper::Resource
|
16
|
+
|
17
|
+
property :id, Serial
|
18
|
+
|
19
|
+
belongs_to :container
|
20
|
+
has n, :groups, :through => Resource
|
21
|
+
end
|
22
|
+
class Group
|
23
|
+
include DataMapper::Resource
|
24
|
+
|
25
|
+
property :id, Serial
|
26
|
+
|
27
|
+
has n, :users, :through => Resource
|
28
|
+
end
|
29
|
+
|
30
|
+
class Container
|
31
|
+
include DataMapper::Resource
|
32
|
+
|
33
|
+
property :id, Serial
|
34
|
+
|
35
|
+
has 1, :user
|
36
|
+
has n, :items
|
37
|
+
end
|
38
|
+
|
39
|
+
def mock_attribute(name)
|
40
|
+
attr = Object.new
|
41
|
+
def attr.name
|
42
|
+
@name
|
43
|
+
end
|
44
|
+
def attr.name=(name)
|
45
|
+
@name = name
|
46
|
+
end
|
47
|
+
attr.name= name
|
48
|
+
attr
|
49
|
+
end
|
50
|
+
|
51
|
+
def mock_item(stubs={})
|
52
|
+
@mock_item ||= mock(Item, stubs)
|
53
|
+
end
|
54
|
+
|
55
|
+
def mock_query(stubs={})
|
56
|
+
@mock_query ||= mock(DataMapper::Query, stubs)
|
57
|
+
end
|
58
|
+
|
59
|
+
describe DataMapper::Adapters::RestfulAdapter do
|
60
|
+
|
61
|
+
before :each do
|
62
|
+
@adapter = DataMapper::Adapters::RestfulAdapter.new(:name, "uri://")
|
63
|
+
|
64
|
+
def @adapter.key_value_from_query(query)
|
65
|
+
432
|
66
|
+
end
|
67
|
+
def @adapter.resource_name_from_query(query)
|
68
|
+
"item"
|
69
|
+
end
|
70
|
+
def @adapter.http_put(uri, data)
|
71
|
+
@uri = uri
|
72
|
+
@data = data
|
73
|
+
end
|
74
|
+
def @adapter.http_delete(uri)
|
75
|
+
@uri = uri
|
76
|
+
end
|
77
|
+
def @adapter.data
|
78
|
+
@data
|
79
|
+
end
|
80
|
+
def @adapter.uri
|
81
|
+
@uri
|
82
|
+
end
|
83
|
+
def @adapter.body=(b)
|
84
|
+
@body = b
|
85
|
+
end
|
86
|
+
def @adapter.send_request
|
87
|
+
res = Object.new
|
88
|
+
def res.body=(b)
|
89
|
+
@body = b
|
90
|
+
end
|
91
|
+
def res.body
|
92
|
+
@body
|
93
|
+
end
|
94
|
+
def res.kind_of?(clazz)
|
95
|
+
true
|
96
|
+
end
|
97
|
+
res.body= @body
|
98
|
+
res
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should create an Item' do
|
104
|
+
@adapter.body = "<item><id>123</id><name>zappa</name></item>"
|
105
|
+
|
106
|
+
item = Item.new
|
107
|
+
@adapter.create_resource(item).should == item
|
108
|
+
item.id.should == 123
|
109
|
+
item.name.should == 'zappa'
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should update an Item' do
|
113
|
+
mock_item.should_receive(:to_query)
|
114
|
+
mock_item.should_receive(:name).and_return("item")
|
115
|
+
mock_item.should_receive(:to_xml)
|
116
|
+
|
117
|
+
@adapter.update_resource(mock_item, mock_attribute(:name) => "frank zappa")
|
118
|
+
|
119
|
+
@adapter.data.should == "<item><name>frank zappa</name></item>"
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'should update Items' do
|
123
|
+
mock_query.should_receive(:limit).and_return(1)
|
124
|
+
@adapter.update({mock_attribute(:name) => "frank zappa"}, mock_query)
|
125
|
+
|
126
|
+
@adapter.data.should == "<item><name>frank zappa</name></item>"
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'should delete an Item' do
|
130
|
+
mock_item.should_receive(:name).and_return("item")
|
131
|
+
mock_item.should_receive(:to_query)
|
132
|
+
@adapter.delete_resource(mock_item)
|
133
|
+
@adapter.uri.should == "/items/432.xml"
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'should delete Items' do
|
137
|
+
mock_query.should_receive(:limit).and_return(1)
|
138
|
+
@adapter.delete(mock_query)
|
139
|
+
|
140
|
+
@adapter.uri.should == "/items/432.xml"
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'should read an Item' do
|
144
|
+
@adapter.body = "<item><id>123</id><name>zappa</name></item>"
|
145
|
+
|
146
|
+
query = DataMapper::Query.new(Item.new.repository, Item)
|
147
|
+
item = @adapter.read_resource(query)
|
148
|
+
item.id.should == 123
|
149
|
+
item.name.should == 'zappa'
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should read Items' do
|
153
|
+
@adapter.body = "<items type='array'><item><id>123</id><name>zappa</name></item></items>"
|
154
|
+
|
155
|
+
query = DataMapper::Query.new(Item.new.repository, Item)
|
156
|
+
items = @adapter.read_resources(query)
|
157
|
+
items.size.should == 1
|
158
|
+
items[0].id.should == 123
|
159
|
+
items[0].name.should == 'zappa'
|
160
|
+
end
|
161
|
+
#end
|
162
|
+
|
163
|
+
|
164
|
+
#describe 'associations of ' + DataMapper::Adapters::RestfulAdapter.to_s do
|
165
|
+
it 'should read nested resource (belongs_to)' do
|
166
|
+
@adapter.body = "<item><id>123</id><name>zappa</name>" +
|
167
|
+
"<container><id>342</id>" + #<items tpye='array'><item><id>1234</id><name>frank zappa</name></item></items>" +
|
168
|
+
"</container>" +
|
169
|
+
"</item>"
|
170
|
+
|
171
|
+
query = DataMapper::Query.new(Item.new.repository, Item)
|
172
|
+
item = @adapter.read_resource(query)
|
173
|
+
item.id.should == 123
|
174
|
+
item.name.should == 'zappa'
|
175
|
+
item.container.id.should == 342
|
176
|
+
end
|
177
|
+
|
178
|
+
# it 'should read nested resource (has 1)' do
|
179
|
+
# @adapter.body = "<container><id>342</id>" +
|
180
|
+
# "<user><id>543</id></user>" +
|
181
|
+
# "</container>"
|
182
|
+
|
183
|
+
# query = DataMapper::Query.new(Container.new.repository, Container)
|
184
|
+
# c = @adapter.read_resource(query)
|
185
|
+
# c.id.should == 342
|
186
|
+
# end
|
187
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'dm-core'
|
3
|
+
$LOAD_PATH << Pathname(__FILE__).dirname.parent.expand_path + 'lib'
|
4
|
+
|
5
|
+
# just define a empty abstract store
|
6
|
+
module ActionController
|
7
|
+
module Session
|
8
|
+
class AbstractStore
|
9
|
+
def initialize(app, options = {})
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Session
|
16
|
+
|
17
|
+
include ::DataMapper::Resource
|
18
|
+
|
19
|
+
property :session_id, String, :key => true
|
20
|
+
|
21
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: datamapper4rail
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- mkristian
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-03-
|
12
|
+
date: 2009-03-28 00:00:00 +05:30
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -64,8 +64,14 @@ files:
|
|
64
64
|
- lib/datamapper4rails/adapters/restful_adapter.rb
|
65
65
|
- lib/datamapper4rails/database_config.rb
|
66
66
|
- lib/datamapper4rails/datamapper_store.rb
|
67
|
+
- lib/datamapper4rails/identity_maps.rb
|
68
|
+
- lib/datamapper4rails/preload_models.rb
|
67
69
|
- lib/datamapper4rails/restful_transactions.rb
|
68
70
|
- lib/datamapper4rails/version.rb
|
71
|
+
- spec/datamapper_store_spec.rb
|
72
|
+
- spec/restful_adapter_spec.rb
|
73
|
+
- spec/spec.opts
|
74
|
+
- spec/spec_helper.rb
|
69
75
|
has_rdoc: true
|
70
76
|
homepage: http://datamapper4rail.rubyforge.org
|
71
77
|
post_install_message:
|