dm-xml-adapter 0.2

Sign up to get free protection for your applications and to get access to all the features.
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