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 +1 -0
- data/lib/dm-xml-adapter.rb +217 -0
- data/lib/ratecounter.rb +23 -0
- data/spec/dm-xml-adapter_spec.rb +129 -0
- data/spec/spec_helper.rb +37 -0
- metadata +79 -0
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
|
+
|
data/lib/ratecounter.rb
ADDED
@@ -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
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -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
|