dm-xml-adapter 0.57 → 0.58
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 +1 -0
- data/lib/dm-xml-adapter.rb +80 -111
- data/spec/dm-xml-adapter_spec.rb +13 -0
- data/spec/spec_helper.rb +1 -0
- metadata +22 -2
data/README
CHANGED
data/lib/dm-xml-adapter.rb
CHANGED
@@ -9,25 +9,22 @@ require 'tempfile'
|
|
9
9
|
require 'simple_memoize'
|
10
10
|
require 'log4r'
|
11
11
|
|
12
|
-
|
12
|
+
# the XML adapter is an adapter for DataMapper that allows you to store
|
13
|
+
# data in XML files. beware, it's not transactionally safe.
|
13
14
|
module DataMapper::Adapters
|
14
15
|
|
15
16
|
class XmlAdapterCache
|
16
17
|
|
17
18
|
def initialize
|
18
|
-
#@@logger = Log4r::Logger.new 'cache'
|
19
|
-
#@@logger.outputters = Log4r::Outputter.stdout
|
20
19
|
@mtimes = Hash.new()
|
21
20
|
@classes = Hash.new()
|
22
21
|
end
|
23
22
|
|
24
23
|
def set_mtime(file, mtime)
|
25
|
-
|
26
|
-
@mtimes[file] = mtime
|
24
|
+
@mtimes[file] = mtime
|
27
25
|
end
|
28
26
|
|
29
27
|
def delete_mtime(file)
|
30
|
-
#@@logger.info("deleting the following mtime: #{file}")
|
31
28
|
@mtimes.delete(file)
|
32
29
|
end
|
33
30
|
|
@@ -35,9 +32,7 @@ module DataMapper::Adapters
|
|
35
32
|
return @mtimes[file]
|
36
33
|
end
|
37
34
|
|
38
|
-
def put(classname, id, object)
|
39
|
-
#@@logger.info("CACHE INSERT: #{classname} #{id} #{object.inspect}")
|
40
|
-
|
35
|
+
def put(classname, id, object)
|
41
36
|
# check to see if we have a classname entry
|
42
37
|
if (@classes[classname] == nil)
|
43
38
|
@classes[classname] = Hash.new
|
@@ -45,35 +40,31 @@ module DataMapper::Adapters
|
|
45
40
|
|
46
41
|
hash = @classes[classname]
|
47
42
|
hash[id.to_s] = object
|
48
|
-
|
49
|
-
end
|
43
|
+
end
|
50
44
|
|
51
45
|
def delete(classname, id)
|
52
|
-
#@@logger.info("deleted the following from cache: #{classname} #{id.to_s}")
|
53
46
|
hash = @classes[classname]
|
54
|
-
#@@logger.info("the hash is: #{hash.inspect}")
|
55
47
|
unless hash == nil
|
56
48
|
hash.delete(id.to_s)
|
57
49
|
end
|
58
|
-
|
59
|
-
end
|
50
|
+
end
|
60
51
|
|
61
52
|
def get(classname, id)
|
62
|
-
#@@logger.info("CACHE FETCH: #{classname} #{id}")
|
63
|
-
#@@logger.info("Master classes: #{@classes.inspect}")
|
64
53
|
hash = @classes[classname]
|
65
|
-
|
66
|
-
object = hash[id]
|
67
|
-
#@@logger.info("The object is: #{object}")
|
68
|
-
return object
|
54
|
+
return hash[id]
|
69
55
|
end
|
70
56
|
end
|
71
57
|
|
72
58
|
class XmlAdapter < AbstractAdapter
|
73
59
|
|
60
|
+
def self.threads=(count)
|
61
|
+
@@threadcount = count
|
62
|
+
end
|
63
|
+
|
74
64
|
def initialize(name, options)
|
75
65
|
super
|
76
66
|
|
67
|
+
@@threadcount = 1
|
77
68
|
@options = Hash.new
|
78
69
|
@options[:directory] = options[:directory]
|
79
70
|
@options[:directory] ||= './db'
|
@@ -81,10 +72,7 @@ module DataMapper::Adapters
|
|
81
72
|
@last_used_id = Hash.new
|
82
73
|
|
83
74
|
@cache = XmlAdapterCache.new
|
84
|
-
|
85
|
-
#@@logger = Log4r::Logger.new 'adapter'
|
86
|
-
#@@logger.outputters = Log4r::Outputter.stdout
|
87
|
-
|
75
|
+
|
88
76
|
end
|
89
77
|
|
90
78
|
def destroy_model_storage(repository, model)
|
@@ -95,27 +83,27 @@ module DataMapper::Adapters
|
|
95
83
|
FileUtils.mkdir_p(classname_to_dir(model.to_s))
|
96
84
|
end
|
97
85
|
|
98
|
-
def create(resources)
|
86
|
+
def create(resources)
|
87
|
+
key = resources.first.model.key.first.name
|
99
88
|
resources.each do |resource|
|
100
|
-
|
101
|
-
id = find_free_id_for(resource.class.to_s)
|
89
|
+
id = find_free_id_for(resources.first.class.to_s)
|
102
90
|
# find name of key attribute
|
103
|
-
key = model.key.first.name
|
104
91
|
resource.attributes[key] = id
|
105
92
|
resource.instance_variable_set("@" + key.to_s, id)
|
106
93
|
save(resource)
|
107
|
-
end
|
108
|
-
return resources.size
|
94
|
+
end.size
|
109
95
|
end
|
110
96
|
|
111
97
|
def delete(collection)
|
112
98
|
collection.each do |result|
|
113
|
-
|
99
|
+
key = result.key.first
|
100
|
+
class_name = result.model.to_s
|
101
|
+
@last_used_id[class_name] = key
|
114
102
|
xml_destroy(result)
|
115
103
|
# also remove from cache
|
116
|
-
@cache.delete(
|
104
|
+
@cache.delete(class_name, key)
|
117
105
|
# also remove from mtimes
|
118
|
-
@cache.delete_mtime(class_name_to_file(
|
106
|
+
@cache.delete_mtime(class_name_to_file(class_name, key))
|
119
107
|
end
|
120
108
|
return collection.size
|
121
109
|
end
|
@@ -146,116 +134,101 @@ module DataMapper::Adapters
|
|
146
134
|
# take an objects class and ID and figure
|
147
135
|
# out what file it's in
|
148
136
|
# and then remove that file
|
149
|
-
|
150
|
-
id = resource.key.first
|
151
|
-
file = class_name_to_file(class_name, id)
|
152
|
-
File.unlink(file)
|
137
|
+
File.unlink class_name_to_file(resource.class.to_s, resource.key.first)
|
153
138
|
end
|
154
139
|
|
155
140
|
def filter_result_set(objects, query)
|
156
|
-
#puts "before filter: #{objects.inspect}"
|
157
141
|
result_set = objects.clone
|
158
|
-
result_set = query.filter_records(result_set)
|
159
|
-
#puts "After filter: #{result_set.inspect}"
|
160
142
|
return query.filter_records(result_set)
|
161
143
|
end
|
162
144
|
|
163
145
|
def get_all(model)
|
164
|
-
|
165
|
-
directory = classname_to_dir(
|
146
|
+
model_name = model.to_s
|
147
|
+
directory = classname_to_dir(model_name)
|
166
148
|
if ! File.exists?(directory)
|
167
|
-
#@@logger.error("The directory doesn't exist!")
|
168
149
|
return []
|
169
150
|
end
|
151
|
+
|
170
152
|
objects = []
|
171
|
-
Dir
|
172
|
-
filename = directory + "/" + filename
|
153
|
+
Dir[directory + "/*.xml"].each do |filename|
|
173
154
|
# see if we have a nice cached version
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
if (mtime != nil and cache_mtime != nil)
|
178
|
-
#puts "comparing! #{mtime < cache_mtime}"
|
179
|
-
end
|
180
|
-
if (mtime != nil and cache_mtime != nil and mtime <= cache_mtime)
|
181
|
-
# we hav ea nice cached object..
|
182
|
-
objects << @cache.get(model.to_s, file_to_id(filename))
|
155
|
+
if has_cached_version?(filename)
|
156
|
+
# we have a nice cached object..
|
157
|
+
objects << @cache.get(model_name, file_to_id(filename))
|
183
158
|
else
|
184
|
-
|
185
|
-
# the file's newer, so we gotta load it up
|
159
|
+
# the file's newer OR older (aha!!), so we gotta load it up
|
186
160
|
object = file_to_object(filename, model)
|
187
161
|
objects << object
|
188
|
-
@cache.put(
|
162
|
+
@cache.put(model_name, object.key.first, object)
|
189
163
|
@cache.set_mtime(filename, File.mtime(filename))
|
190
164
|
end
|
191
165
|
end
|
192
166
|
return objects
|
193
167
|
end
|
168
|
+
|
169
|
+
def has_cached_version?(filename)
|
170
|
+
mtime = File.mtime(filename)
|
171
|
+
cache_mtime = @cache.get_mtime(filename)
|
172
|
+
return true if (mtime != nil and cache_mtime != nil and mtime == cache_mtime)
|
173
|
+
end
|
194
174
|
|
195
175
|
def file_to_id(file)
|
196
176
|
return file.match(/([0-9]+)\.xml/)[1]
|
197
177
|
end
|
198
|
-
|
199
|
-
def is_integer?(number)
|
200
|
-
if number.class == String and number.match(/^\-*[0-9]+$/)
|
201
|
-
return true
|
202
|
-
end
|
203
|
-
return false
|
204
|
-
end
|
205
178
|
|
206
179
|
def file_to_object(file, model)
|
207
|
-
|
180
|
+
model_name = model.to_s
|
208
181
|
# allocate new object to receive these fields
|
209
|
-
if (
|
210
|
-
modname, clazz =
|
182
|
+
if (model_name.match(/(.*)\:\:(.*)/))
|
183
|
+
modname, clazz = [$1, $2]
|
211
184
|
new_obj = Kernel.const_get(modname).const_get(clazz).new
|
212
185
|
else
|
213
|
-
|
214
|
-
new_obj = Kernel.const_get(clazz).new
|
186
|
+
new_obj = Kernel.const_get(model_name).new
|
215
187
|
end
|
216
188
|
|
217
|
-
# file is our XML file
|
218
|
-
string = IO.read(file)
|
219
|
-
parser = XML::Parser.string(string)
|
220
|
-
doc = parser.parse
|
221
|
-
root_node = doc.root
|
222
189
|
# iterate over children
|
223
|
-
|
190
|
+
XML::Parser.string(IO.read(file)).parse.root.children.each do |child|
|
224
191
|
child_class = child.attributes["class"]
|
225
192
|
if child_class != nil
|
226
193
|
# this means we have an attribute
|
227
|
-
|
228
|
-
new_obj.instance_variable_set("@#{child.name}", child.content.to_i)
|
229
|
-
elsif (child_class == "String")
|
230
|
-
new_obj.instance_variable_set("@#{child.name}", child.content)
|
231
|
-
elsif (child_class == "TrueClass")
|
232
|
-
new_obj.instance_variable_set("@#{child.name}", true)
|
233
|
-
elsif (child_class == "FalseClass")
|
234
|
-
new_obj.instance_variable_set("@#{child.name}", false)
|
235
|
-
elsif (child_class == "BigDecimal")
|
236
|
-
new_obj.instance_variable_set("@#{child.name}", BigDecimal.new(child.content))
|
237
|
-
elsif (child_class == "DateTime")
|
238
|
-
new_obj.instance_variable_set("@#{child.name}", DateTime.parse(child.content))
|
239
|
-
end
|
194
|
+
populate_instance(new_obj, child, child_class)
|
240
195
|
end
|
241
196
|
end
|
242
197
|
|
243
198
|
return new_obj
|
244
199
|
end
|
200
|
+
|
201
|
+
def populate_instance(new_obj, child, child_class)
|
202
|
+
if (child_class == "String")
|
203
|
+
new_obj.instance_variable_set("@#{child.name}", child.content)
|
204
|
+
elsif (child_class == "Integer")
|
205
|
+
new_obj.instance_variable_set("@#{child.name}", child.content.to_i)
|
206
|
+
elsif (child_class == "TrueClass")
|
207
|
+
new_obj.instance_variable_set("@#{child.name}", true)
|
208
|
+
elsif (child_class == "FalseClass")
|
209
|
+
new_obj.instance_variable_set("@#{child.name}", false)
|
210
|
+
elsif (child_class == "BigDecimal")
|
211
|
+
new_obj.instance_variable_set("@#{child.name}", BigDecimal.new(child.content))
|
212
|
+
elsif (child_class == "DateTime")
|
213
|
+
new_obj.instance_variable_set("@#{child.name}", DateTime.parse(child.content))
|
214
|
+
elsif (child_class == "Date")
|
215
|
+
new_obj.instance_variable_set("@#{child.name}", Date.parse(child.content))
|
216
|
+
end
|
217
|
+
end
|
245
218
|
|
246
|
-
|
219
|
+
def find_free_id_for(class_name)
|
247
220
|
# if there are no entries in the directory
|
248
221
|
# or the directory doesn't exist
|
249
222
|
# we need to create it...
|
250
223
|
if ! File.exists?(classname_to_dir(class_name))
|
251
224
|
# default ID
|
252
|
-
return
|
225
|
+
return 1
|
253
226
|
end
|
254
227
|
directory = Dir.new(classname_to_dir(class_name))
|
255
228
|
if directory.entries.size == 0
|
256
|
-
return
|
229
|
+
return 1
|
257
230
|
end
|
258
|
-
id = @last_used_id[class_name] ||
|
231
|
+
id = @last_used_id[class_name] || 1
|
259
232
|
while true do
|
260
233
|
if ! File.exists?(File.join(directory.path, id.to_s + ".xml"))
|
261
234
|
@last_used_id[class_name] = id
|
@@ -266,43 +239,39 @@ module DataMapper::Adapters
|
|
266
239
|
end
|
267
240
|
|
268
241
|
def save(resource)
|
269
|
-
|
270
242
|
# since we're saving, purge the cache
|
271
|
-
|
272
|
-
|
243
|
+
resource_class = resource.class.to_s
|
244
|
+
resource_key = resource.key.first
|
245
|
+
@cache.delete(resource_class, resource_key)
|
246
|
+
@cache.delete_mtime(class_name_to_file(resource_class, resource_key))
|
273
247
|
|
274
|
-
# file = File.join(class_name_to_file(resource.class.to_s, resource.key.first))
|
275
248
|
# see if the directory exists, if it doesn't, we need to create it
|
276
|
-
if ! File.exists?(classname_to_dir(
|
277
|
-
FileUtils.mkdir_p(classname_to_dir(
|
249
|
+
if ! File.exists?(classname_to_dir(resource_class))
|
250
|
+
FileUtils.mkdir_p(classname_to_dir(resource_class))
|
278
251
|
end
|
279
252
|
|
280
|
-
# puts resource.model.properties.inspect
|
281
|
-
|
282
253
|
# set up builder
|
283
254
|
out_string = ""
|
284
255
|
xml = Builder::XmlMarkup.new(:target => out_string, :indent => 1)
|
285
256
|
|
286
257
|
# iterate over the resource and figure out the fields
|
287
|
-
# class_name = resource.model.to_s.gsub(/\:/, "_")
|
288
258
|
xml.tag!("RubyObject", :class => resource.model.to_s) do
|
289
259
|
resource.model.properties.each do |property|
|
290
|
-
|
291
|
-
|
292
|
-
value = resource.instance_variable_get("@" + property.name.to_s)
|
260
|
+
property_name = property.name.to_s
|
261
|
+
value = resource.instance_variable_get("@" + property_name)
|
293
262
|
# special case for false
|
294
263
|
if property.primitive == TrueClass and value == false
|
295
|
-
|
296
|
-
elsif (
|
297
|
-
|
264
|
+
xml.tag!(property_name, false, :class => FalseClass.to_s)
|
265
|
+
elsif (property.primitive == BigDecimal and value != nil)
|
266
|
+
xml.tag!(property_name, value.to_s("F"), :class => BigDecimal)
|
298
267
|
elsif (value != nil)
|
299
|
-
|
268
|
+
xml.tag!(property_name, value, :class => property.primitive)
|
300
269
|
end
|
301
270
|
end
|
302
271
|
end
|
303
272
|
|
304
|
-
xmlfile = File.new(class_name_to_file(
|
305
|
-
tempfile = Tempfile.new("dm-xml-adapter",
|
273
|
+
xmlfile = File.new(class_name_to_file(resource_class, resource_key), "w")
|
274
|
+
tempfile = Tempfile.new("dm-xml-adapter", Dir.tmpdir())
|
306
275
|
tempfile.puts out_string
|
307
276
|
tempfile.close
|
308
277
|
FileUtils.mv(tempfile.path,xmlfile.path)
|
data/spec/dm-xml-adapter_spec.rb
CHANGED
@@ -3,6 +3,8 @@ require 'dm-core'
|
|
3
3
|
require 'pathname'
|
4
4
|
require Pathname(__FILE__).dirname.expand_path + 'spec_helper'
|
5
5
|
|
6
|
+
DataMapper::Adapters::XmlAdapter.threads = 2
|
7
|
+
|
6
8
|
describe DataMapper::Adapters::XmlAdapter do
|
7
9
|
before(:each) do
|
8
10
|
@adapter = DataMapper.setup(:default, {:adapter => 'xml', :directory => 'db'})
|
@@ -49,6 +51,17 @@ describe DataMapper::Adapters::XmlAdapter do
|
|
49
51
|
end
|
50
52
|
end
|
51
53
|
|
54
|
+
describe "date" do
|
55
|
+
it "should do date" do
|
56
|
+
u1 = XMLTest::User.new
|
57
|
+
u1.name = "cool!"
|
58
|
+
u1.date = Date.parse("01/01/2001")
|
59
|
+
u1.save
|
60
|
+
u2 = XMLTest::User.first(:name => "cool!")
|
61
|
+
u2.date.class.should == Date
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
52
65
|
describe "boolean" do
|
53
66
|
it "should be boolean" do
|
54
67
|
u1 = XMLTest::User.new
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dm-xml-adapter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: "0.
|
4
|
+
version: "0.58"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshua Harding
|
@@ -9,7 +9,7 @@ autorequire: dm-xml-adapter
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-01
|
12
|
+
date: 2010-07-01 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -42,6 +42,26 @@ dependencies:
|
|
42
42
|
- !ruby/object:Gem::Version
|
43
43
|
version: "0"
|
44
44
|
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: log4r
|
47
|
+
type: :runtime
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: peach
|
57
|
+
type: :runtime
|
58
|
+
version_requirement:
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
45
65
|
description:
|
46
66
|
email: josh@statewidesoftware.com
|
47
67
|
executables: []
|