dm-xml-adapter 0.57 → 0.58

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1 +1,2 @@
1
1
  This is an XML adapter.
2
+ Set DataMapper::Adapters::XmlAdapter.threads = 2 or more in jruby...
@@ -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
- #@@logger.info("Saved mtime, #{file} -> #{mtime}")
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
- # @@logger.info("the hash is: #{hash.inspect}")
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
- #@@logger.info("the hash is: #{hash.inspect}")
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
- #@@logger.info("the hash is: #{hash.inspect}")
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
- model = resource.model
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
- @last_used_id[result.class.to_s] = result.key.first
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(result.model.to_s, result.key.first)
104
+ @cache.delete(class_name, key)
117
105
  # also remove from mtimes
118
- @cache.delete_mtime(class_name_to_file(result.model.to_s, result.key.first))
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
- class_name = resource.class.to_s
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
- #@@logger.info("Trying to get all: #{model.to_s}")
165
- directory = classname_to_dir(model.to_s)
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.entries(directory).select {|f| f.match(/\.xml$/) }.each do |filename|
172
- filename = directory + "/" + filename
153
+ Dir[directory + "/*.xml"].each do |filename|
173
154
  # see if we have a nice cached version
174
- mtime = File.mtime(filename)
175
- cache_mtime = @cache.get_mtime(filename)
176
- #puts "file mtime: #{mtime} cache mtime: #{@cache.get_mtime(filename)}"
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
- #@@logger.info("CACHE MISS")
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(model.to_s, object.key.first, object)
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 (model.to_s.match(/\:\:/))
210
- modname, clazz = model.to_s.split("::")
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
- clazz = model.to_s
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
- root_node.children.each do |child|
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
- if (child_class == "Integer")
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
- def find_free_id_for(class_name)
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 0
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 0
229
+ return 1
257
230
  end
258
- id = @last_used_id[class_name] || 0
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
- @cache.delete(resource.class.to_s, resource.key.first)
272
- @cache.delete_mtime(class_name_to_file(resource.class.to_s, resource.key.first))
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(resource.class.to_s))
277
- FileUtils.mkdir_p(classname_to_dir(resource.class.to_s))
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
- #puts "saving prop: #{property.name}"
291
- #puts "primitive: #{property.primitive}"
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
- xml.tag!(property.name.to_s, false, :class => FalseClass.to_s)
296
- elsif (value != nil and property.primitive == BigDecimal)
297
- xml.tag!(property.name.to_s, value.to_s("F"), :class => BigDecimal)
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
- xml.tag!(property.name.to_s, value, :class => property.primitive)
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(resource.class.to_s, resource.key.first), "w")
305
- tempfile = Tempfile.new("dm-xml-adapter", "/tmp")
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)
@@ -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
@@ -45,6 +45,7 @@ class User
45
45
  property :created, DateTime
46
46
  property :cool, Boolean
47
47
  property :content, String
48
+ property :date, Date
48
49
 
49
50
  has n, :posts, :model => "Post"
50
51
  end
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.57"
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-07 00:00:00 -05:00
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: []