dm-xml-adapter 0.2

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 ADDED
@@ -0,0 +1 @@
1
+ This is an XML adapter.
@@ -0,0 +1,217 @@
1
+ require 'rubygems'
2
+ require 'dm-core'
3
+ require 'builder'
4
+ require 'dm-core/adapters/abstract_adapter'
5
+ require 'pp'
6
+ gem 'libxml-ruby', '>= 0.8.3'
7
+ require 'xml'
8
+
9
+ module DataMapper::Adapters
10
+
11
+ class XmlAdapter < AbstractAdapter
12
+
13
+ def initialize(name, options)
14
+ super
15
+
16
+ @options = Hash.new
17
+ @options[:directory] = options[:directory]
18
+ @options[:directory] ||= './db'
19
+
20
+ @last_used_id = Hash.new
21
+
22
+ end
23
+
24
+ def destroy_model_storage(repository, model)
25
+ FileUtils.rm_rf(classname_to_dir(model.to_s))
26
+ end
27
+
28
+ def create_model_storage(repository, model)
29
+ FileUtils.mkdir_p(classname_to_dir(model.to_s))
30
+ end
31
+
32
+ def create(resources)
33
+ resources.each do |resource|
34
+ model = resource.model
35
+ id = find_free_id_for(resource.class.to_s)
36
+ # find name of key attribute
37
+ key = model.key.first.name
38
+ resource.attributes[key] = id
39
+ resource.instance_variable_set("@" + key.to_s, id)
40
+ save(resource)
41
+ end
42
+ return resources.size
43
+ end
44
+
45
+ def delete(collection)
46
+ collection.each do |result|
47
+ @last_used_id[result.class.to_s] = result.key.first
48
+ xml_destroy(result)
49
+ end
50
+ return collection.size
51
+ end
52
+
53
+ def update(attributes, collection)
54
+ # ok, for each object found we have to update the attribs we found
55
+ # first thing is figure out what class we are dealing with
56
+ # iterate over every object in this set and set the given attributes
57
+ collection.each do |obj|
58
+ attributes.each do |attrib|
59
+ # attrib is an array
60
+ # first member is Property object
61
+ # second member is the value
62
+ obj.instance_variable_set("@" + attrib[0].name.to_s, attrib[1])
63
+ end
64
+ save(obj)
65
+ end
66
+ return collection
67
+ end
68
+
69
+ def read(query)
70
+ #puts "READING #{query.conditions}"
71
+ #puts query.conditions.operands.inspect
72
+ return filter_result_set(get_all(query.model), query)
73
+ end
74
+
75
+ private
76
+
77
+ def xml_destroy(resource)
78
+ # take an objects class and ID and figure
79
+ # out what file it's in
80
+ # and then remove that file
81
+ class_name = resource.class.to_s
82
+ id = resource.key.first
83
+ file = class_name_to_file(class_name, id)
84
+ File.unlink(file)
85
+ end
86
+
87
+ def filter_result_set(objects, query)
88
+ #puts "before filter: #{objects.inspect}"
89
+ result_set = objects.clone
90
+ result_set = query.filter_records(result_set)
91
+ #puts "After filter: #{result_set.inspect}"
92
+ return query.filter_records(result_set)
93
+ end
94
+
95
+ def get_all(model)
96
+ objects = Array.new
97
+ directory = classname_to_dir(model.to_s)
98
+ if ! File.exists?(directory)
99
+ return objects
100
+ end
101
+ Dir.glob(directory / "*.xml").each do |entry|
102
+ objects << file_to_object(entry, model)
103
+ end
104
+ return objects
105
+ end
106
+
107
+ def is_integer?(number)
108
+ if number.class == String and number.match(/^\-*[0-9]+$/)
109
+ return true
110
+ end
111
+ return false
112
+ end
113
+
114
+ def file_to_object(file, model)
115
+
116
+ # allocate new object to receive these fields
117
+ if (model.to_s.match(/\:\:/))
118
+ modname, clazz = model.to_s.split("::")
119
+ new_obj = Kernel.const_get(modname).const_get(clazz).new
120
+ else
121
+ clazz = model.to_s
122
+ new_obj = Kernel.const_get(clazz).new
123
+ end
124
+
125
+ # file is our XML file
126
+ string = IO.read(file)
127
+ parser = XML::Parser.string(string)
128
+ doc = parser.parse
129
+ root_node = doc.root
130
+ # iterate over children
131
+ root_node.children.each do |child|
132
+ child_class = child.attributes["class"]
133
+ if child_class != nil
134
+ # this means we have an attribute
135
+ if (child_class == "Integer")
136
+ new_obj.instance_variable_set("@#{child.name}", child.content.to_i)
137
+ elsif (child_class == "String")
138
+ new_obj.instance_variable_set("@#{child.name}", child.content)
139
+ elsif (child_class == "TrueClass")
140
+ new_obj.instance_variable_set("@#{child.name}", true)
141
+ elsif (child_class == "FalseClass")
142
+ new_obj.instance_variable_set("@#{child.name}", false)
143
+ elsif (child_class == "DateTime")
144
+ new_obj.instance_variable_set("@#{child.name}", DateTime.parse(child.content))
145
+ end
146
+ end
147
+ end
148
+
149
+ return new_obj
150
+ end
151
+
152
+ def find_free_id_for(class_name)
153
+ # if there are no entries in the directory
154
+ # or the directory doesn't exist
155
+ # we need to create it...
156
+ if ! File.exists?(classname_to_dir(class_name))
157
+ # default ID
158
+ return 1
159
+ end
160
+ directory = Dir.new(classname_to_dir(class_name))
161
+ if directory.entries.size == 0
162
+ return 1
163
+ end
164
+ id = @last_used_id[class_name] || 0
165
+ while true do
166
+ if ! File.exists?(File.join(directory.path, id.to_s + ".xml"))
167
+ @last_used_id[class_name] = id
168
+ return id
169
+ end
170
+ id += 1
171
+ end
172
+ end
173
+
174
+ def save(resource)
175
+ file = File.join(class_name_to_file(resource.class.to_s, resource.key.first))
176
+ # see if the directory exists, if it doesn't, we need to create it
177
+ if ! File.exists?(classname_to_dir(resource.class.to_s))
178
+ FileUtils.mkdir_p(classname_to_dir(resource.class.to_s))
179
+ end
180
+ xmlfile = File.new(class_name_to_file(resource.class.to_s, resource.key.first), "w")
181
+ # puts resource.model.properties.inspect
182
+
183
+ # set up builder
184
+ outString = ""
185
+ xml = Builder::XmlMarkup.new(:target => outString, :indent => 1)
186
+
187
+ # iterate over the resource and figure out the fields
188
+ class_name = resource.model.to_s.gsub(/\:/, "_")
189
+ xml.tag!("RubyObject", :class => resource.model.to_s) do
190
+ resource.model.properties.each do |property|
191
+ #puts "saving prop: #{property.name}"
192
+ #puts "primitive: #{property.primitive}"
193
+ value = resource.instance_variable_get("@" + property.name.to_s)
194
+ # special case for false
195
+ if property.primitive == TrueClass and value == false
196
+ xml.tag!(property.name.to_s, false, :class => FalseClass.to_s)
197
+ elsif (value != nil)
198
+ xml.tag!(property.name.to_s, value, :class => property.primitive)
199
+ end
200
+ end
201
+ end
202
+ xmlfile.puts outString
203
+ xmlfile.close
204
+ end
205
+
206
+ def classname_to_dir(class_name)
207
+ return File.join(@options[:directory],
208
+ class_name.gsub("/", "_").gsub(":", "_"))
209
+ end
210
+
211
+ def class_name_to_file(class_name, id)
212
+ return File.join(classname_to_dir(class_name), id.to_s + ".xml")
213
+ end
214
+
215
+ end
216
+ end
217
+
@@ -0,0 +1,23 @@
1
+ class RateCounter
2
+
3
+ attr_accessor :samples, :sampledata
4
+
5
+ def initialize(samples = 50)
6
+ self.samples = samples
7
+ self.sampledata = Array.new
8
+ end
9
+
10
+ def tick
11
+ self.sampledata << Time.now
12
+ while (self.sampledata.length > 50) do
13
+ self.sampledata.delete(self.sampledata.first)
14
+ end
15
+ end
16
+
17
+ def rate
18
+ numSeconds = self.sampledata.last - self.sampledata.first
19
+ return 0 if self.sampledata.length == 0 || numSeconds == 0
20
+ return self.sampledata.length.to_f / numSeconds.to_f
21
+ end
22
+
23
+ end
@@ -0,0 +1,129 @@
1
+ require 'rubygems'
2
+ require 'dm-core'
3
+ require 'pathname'
4
+ require Pathname(__FILE__).dirname.expand_path + 'spec_helper'
5
+
6
+ describe DataMapper::Adapters::XmlAdapter do
7
+ before(:all) do
8
+ @adapter = DataMapper.setup(:default, {:adapter => 'xml', :directory => 'db'})
9
+ end
10
+
11
+ after(:all) do
12
+ XMLTest::User.all.each {|u| u.destroy }
13
+ XMLTest::Post.all.each {|p| p.destroy }
14
+ AutoQuote.all.each {|aq| aq.destroy }
15
+ end
16
+
17
+
18
+ describe "CRUD" do
19
+
20
+ describe "non-namedspaced class" do
21
+ it "should be able to persist" do
22
+ AutoQuote.create(:fullname => "Joshua Harding")
23
+ AutoQuote.all.size.should == 1
24
+ end
25
+ end
26
+
27
+ describe "content attribute" do
28
+ it "should be able to handle a content attribute" do
29
+ XMLTest::User.create(:name => "contentking", :content => "cool")
30
+ XMLTest::User.all.size.should == 1
31
+ end
32
+ end
33
+
34
+ describe "datetime" do
35
+ it "should do datetime" do
36
+ u1 = XMLTest::User.new
37
+ u1.name = "cool!"
38
+ u1.created = DateTime.now
39
+ u1.save
40
+ u2 = XMLTest::User.first(:name => "cool!")
41
+ u2.created.class.should == DateTime
42
+ end
43
+ end
44
+
45
+ describe "boolean" do
46
+ it "should be boolean" do
47
+ u1 = XMLTest::User.new
48
+ u1.name = "yes"
49
+ u1.cool = true
50
+ u1.save
51
+ u2 = XMLTest::User.first(:name => "yes")
52
+ u2.cool.class.should == TrueClass
53
+ end
54
+ it "falso thing" do
55
+ u1 = XMLTest::User.new
56
+ u1.name = "yes2"
57
+ u1.cool = false
58
+ u1.save
59
+ u2 = XMLTest::User.first(:name => "yes2")
60
+ u2.cool.class.should == FalseClass
61
+ end
62
+ end
63
+
64
+ describe "associations" do
65
+ it "should have associations" do
66
+ u1 = XMLTest::User.new
67
+ u1.name = "bob"
68
+ u1.save
69
+ p1 = XMLTest::Post.new
70
+ p1.title = "title o' post"
71
+ u1.posts << p1
72
+ p1.save
73
+ p2 = XMLTest::Post.new
74
+ p2.title = "title of the second post"
75
+ u1.posts << p2
76
+ p2.save
77
+ u1.posts.size.should == 2
78
+ u3 = XMLTest::User.first(:name => "bob")
79
+ u3.posts.size.should == 2
80
+ end
81
+ end
82
+
83
+ describe "get all" do
84
+ it "should get all" do
85
+ XMLTest::User.create(:name => :yeah)
86
+ XMLTest::User.all.should_not == nil
87
+ end
88
+ end
89
+
90
+ describe "update" do
91
+ it "should update" do
92
+ u1 = XMLTest::User.new
93
+ u1.name = "bob"
94
+ u1.save
95
+ u1.name = "steve"
96
+ u1.save
97
+ u1.name.should_not == "bob"
98
+ end
99
+ end
100
+
101
+ describe "create" do
102
+ it "should create" do
103
+ u = XMLTest::User.new
104
+ u.save
105
+ u.user_id.should_not == nil
106
+ end
107
+ end
108
+
109
+ describe "delete" do
110
+ it "should delete someone" do
111
+ user = XMLTest::User.create
112
+ user.destroy.should == true
113
+ end
114
+ end
115
+
116
+ describe "find not" do
117
+ it "should find someone with a not clause" do
118
+ user1 = XMLTest::User.create(:name => "joe")
119
+ user2 = XMLTest::User.create(:name => "bob")
120
+ users = XMLTest::User.all(:user_id.not => user1.user_id)
121
+ users.first.user_id.should_not == user1.user_id
122
+ end
123
+ end
124
+
125
+ end
126
+
127
+
128
+ end
129
+
@@ -0,0 +1,37 @@
1
+ require 'rubygems'
2
+ require 'dm-core'
3
+ require 'pathname'
4
+ require Pathname(__FILE__).dirname.parent.expand_path + 'lib/dm-xml-adapter'
5
+
6
+
7
+ class AutoQuote
8
+ include DataMapper::Resource
9
+
10
+ property :quote_id, Serial
11
+ property :fullname, String
12
+ end
13
+
14
+ module XMLTest
15
+ class Post
16
+ include DataMapper::Resource
17
+
18
+ property :post_id, Serial
19
+ property :title, String
20
+ property :created, DateTime
21
+
22
+ belongs_to :user, :model => "User"
23
+ end
24
+
25
+ class User
26
+ include DataMapper::Resource
27
+
28
+ property :user_id, Serial
29
+ property :name, String
30
+ property :age, Integer
31
+ property :created, DateTime
32
+ property :cool, Boolean
33
+ property :content, String
34
+
35
+ has n, :posts, :model => "Post"
36
+ end
37
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dm-xml-adapter
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.2"
5
+ platform: ruby
6
+ authors:
7
+ - Joshua Harding
8
+ autorequire: dm-xml-adapter
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-23 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: libxml-ruby
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: builder
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ description:
36
+ email: josh@statewidesoftware.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - README
43
+ files:
44
+ - lib/dm-xml-adapter.rb
45
+ - lib/ratecounter.rb
46
+ - spec/dm-xml-adapter_spec.rb
47
+ - spec/spec_helper.rb
48
+ - README
49
+ has_rdoc: true
50
+ homepage:
51
+ licenses: []
52
+
53
+ post_install_message:
54
+ rdoc_options: []
55
+
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ version:
70
+ requirements: []
71
+
72
+ rubyforge_project:
73
+ rubygems_version: 1.3.5
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: a XML adapter for DataMapper. this adapter allows you to use DataMapper with XML files as a backing store.
77
+ test_files:
78
+ - spec/dm-xml-adapter_spec.rb
79
+ - spec/spec_helper.rb