dm-mnesia 0.2.8

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.
@@ -0,0 +1,2 @@
1
+ *.gem
2
+ *.swp
@@ -0,0 +1,7 @@
1
+ Manifest
2
+ README.rdoc
3
+ Rakefile
4
+ lib/dm-mnesia-adapter.rb
5
+ spec/dm-mnesia-adapter_spec.rb
6
+ spec/spec.opts
7
+ spec/spec_helper.rb
@@ -0,0 +1,46 @@
1
+ = dm-mnesia
2
+
3
+ A DataMapper adapter for Erlang's Mnesia db
4
+
5
+ ==Requirements
6
+
7
+ You will need to install Rbridge[http://github.com/grockit/rulang].
8
+
9
+ ==Installation
10
+
11
+ sudo gem install dm-mnesia
12
+
13
+ ==Usage
14
+
15
+ Require the gem in your enviroment file
16
+ config.gem "dm-mnesia"
17
+ Initialize a connection using the adapter
18
+ connection = { :adapter => 'Mnesia',
19
+ :hostname => 'localhost',
20
+ :port => 9900,
21
+ :ids_table => 'NAME OF THE TABLE TO STORE UNIQUE IDS.',
22
+ :id_function=> 'NAME OF AN ERLANG FUNCTION TO CREATE UNIQUE IDS FOR YOUR RECORDS'}
23
+ DataMapper.setup(:default, connection )
24
+
25
+ We used this[http://dudefrommangalore.blogspot.com/2009/03/auto-increment-in-mnesia-database.html] example for the ids_table option </p>
26
+
27
+ Now you can define your models just like you would if you were using the DataMapper default adapters. All CRUD operations work the same as well.
28
+
29
+ ==Issues
30
+
31
+ When using a BinaryString type as a key for your model, you must set it to ":require=>false", otherwise it won't work
32
+ When using a List type, you must assign the value to the column like so:
33
+ Model.mycolumn = ["value", "value"]
34
+
35
+
36
+ ==To Do List
37
+
38
+ In certain cases, the read command retrieves all record from Mnesia and filters, sorts them in ruby, more work needs to be done make this more efficient. This won't be a problem if you are working with records around 2000.
39
+ Add missing Erlang types: Tuple, Atom.
40
+
41
+ Authors: Chad DePue (mailto:chad@inakanetworks.com), Manuel Gómez (manuel@inakanetworks.com)
42
+
43
+
44
+
45
+
46
+
@@ -0,0 +1,21 @@
1
+ require 'rake'
2
+ begin
3
+ require 'jeweler'
4
+ Jeweler::Tasks.new do |gemspec|
5
+ gemspec.name = "dm-mnesia"
6
+ gemspec.summary = "DataMapper adapter for Erlang's mnesia db"
7
+ gemspec.description = ""
8
+ gemspec.email = "chad@inakanetworks.com,manuel@inakanetworks.com"
9
+ gemspec.homepage = "http://github.com/mergoc/dm-mnesia"
10
+ gemspec.authors = ["Chad DePue","Manuel Gomez"]
11
+ end
12
+ Jeweler::GemcutterTasks.new
13
+ rescue LoadError
14
+ puts "Jeweler not available. Install it with: gem install jeweler"
15
+ end
16
+
17
+ require 'spec/rake/spectask'
18
+ Spec::Rake::SpecTask.new(:spec) do |spec|
19
+ spec.libs << 'lib' << 'spec'
20
+ spec.spec_files = FileList['spec/**/*_spec.rb']
21
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.8
@@ -0,0 +1,55 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{dm-mnesia}
8
+ s.version = "0.2.8"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Chad DePue", "Manuel Gomez"]
12
+ s.date = %q{2010-08-30}
13
+ s.description = %q{}
14
+ s.email = %q{chad@inakanetworks.com,manuel@inakanetworks.com}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "Manifest",
21
+ "README.rdoc",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "dm-mnesia.gemspec",
25
+ "lib/dm-mnesia-adapter.rb",
26
+ "lib/dm-mnesia.rb",
27
+ "lib/types/binary_string.rb",
28
+ "lib/types/list.rb",
29
+ "lib/types/record.rb",
30
+ "lib/types/tuple.rb",
31
+ "pkg/dm-mnesia-0.0.0.gem",
32
+ "spec/dm-mnesia-adapter_spec.rb",
33
+ "spec/spec.opts",
34
+ "spec/spec_helper.rb"
35
+ ]
36
+ s.homepage = %q{http://github.com/mergoc/dm-mnesia}
37
+ s.rdoc_options = ["--charset=UTF-8"]
38
+ s.require_paths = ["lib"]
39
+ s.rubygems_version = %q{1.3.7}
40
+ s.summary = %q{DataMapper adapter for Erlang's mnesia db}
41
+ s.test_files = [
42
+ "spec/dm-mnesia-adapter_spec.rb",
43
+ "spec/spec_helper.rb"
44
+ ]
45
+
46
+ if s.respond_to? :specification_version then
47
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
48
+ s.specification_version = 3
49
+
50
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
51
+ else
52
+ end
53
+ else
54
+ end
55
+ end
@@ -0,0 +1,265 @@
1
+ require 'dm-core'
2
+ require 'dm-core/adapters/abstract_adapter'
3
+ require 'rbridge'
4
+ module DataMapper::Adapters
5
+
6
+ class MnesiaAdapter < AbstractAdapter
7
+
8
+
9
+ #Initialize the adapter with the given options
10
+ # 1. hostname: location of the rbridge server as an Ip or url, default is localhost
11
+ # 2. port: the port to comunicate with rbridge, default is 9900
12
+ # 3. mod: rbridge offers several modes of operation.
13
+ # 4. ids_table: the table in mnesia that will handle the ids of each record created
14
+ # 5. id_function: in case an erlang function is wished to be used for record id generations, it can be set here. If the id is a BinaryString, this is required
15
+ def initialize(name, options)
16
+ super
17
+
18
+ @hosname = @options[:hostname] ||= "localhost"
19
+ @port = @options[:port] ||= 9900
20
+ @mod = @options[:mod]
21
+ @ids_table = @options[:ids_table]
22
+ @id_function = @options[:id_function]
23
+ @con = con
24
+ @table_attributes = nil
25
+
26
+ ## Refreshes the spec table so that spec test run properly
27
+ con.erl("mnesia:clear_table(heffalump).") if name == :default
28
+ end
29
+
30
+ # Initializes the connection to the rbridge server
31
+ def con(&blk)
32
+ # connect to the server
33
+ if @con.nil?
34
+ @con = RBridge.new(@mod,@hostname, @port)
35
+ if @con.erl("1*1.") != 1
36
+ raise "Error opening connection to database: #{@con.erl("1*1.")}"
37
+ end
38
+ end
39
+ @con
40
+ end
41
+
42
+ # Create function required by the adapter spec
43
+ def create(resources)
44
+ resources.each do |resource|
45
+ initialize_identity_field(resource,get_key(resource))
46
+ save(con, parse_resource(resource))
47
+ end
48
+ end
49
+
50
+ # Read function required by the adapter spec
51
+ #TODO change query so that the mnesia filters the results
52
+ # and does not bring all records on the table every time
53
+ def read(query)
54
+ temp_records = erl_read(query)
55
+ records = []
56
+ temp_records.each do |value|
57
+ records << value if value
58
+ end
59
+ result = query.filter_records(records.reverse)
60
+ result
61
+ end
62
+
63
+ # Update function required by the adapter spec
64
+ def update(attributes, collection)
65
+ attributes = attributes_as_fields(attributes)
66
+ collection.each do |resource|
67
+ attributes = resource.attributes(:field).merge(attributes)
68
+ save(con, parse_resource(resource))
69
+ end
70
+ end
71
+
72
+ # Delete function required by the adapter spec
73
+ def delete(collection)
74
+ collection.each do |resource|
75
+ erl_delete(resource.model,resource.key.first)
76
+ end
77
+ end
78
+
79
+
80
+ protected
81
+
82
+ # On record creation the primary id must be generated
83
+ # If the id_function option is set and the key for the model is a binary string, an erlan function is used.
84
+ # otherwise, and mnesia unique id table can be used or a random number
85
+ def get_key resource
86
+ model = resource.model
87
+ identity_field = get_identity_field resource
88
+ if !@id_function.nil? and identity_field.is_a? DataMapper::Property::BinaryString
89
+ con.erl("#{@id_function}.")
90
+ elsif @ids_tables.nil?
91
+ con.erl("mnesia:dirty_update_counter(#{@ids_table},#{model.to_s.downcase},1).")
92
+ else
93
+ rand(2**32)
94
+ end
95
+ end
96
+
97
+
98
+ # Before update or create the resource must be parse to be properly pass to rbridge
99
+ def parse_resource(resource)
100
+ model = resource.model
101
+ fields = []
102
+ types = Hash.new
103
+ # Get all properties defined on the datamapper model
104
+ resource.send(:properties).map do |property|
105
+ if property.is_a? DataMapper::Property::Serial
106
+ types[property.name] = 0
107
+ elsif property.is_a? DataMapper::Property::Integer
108
+ types[property.name] = 0
109
+ elsif property.is_a? DataMapper::Property::Boolean
110
+ types[property.name] = false
111
+ elsif property.is_a? DataMapper::Property::Record
112
+ types[property.name] = {}
113
+ elsif property.is_a? DataMapper::Property::List
114
+ types[property.name] = []
115
+ elsif property.is_a? DataMapper::Property::BinaryString
116
+ types[property.name] = 0b100
117
+ elsif property.is_a? DataMapper::Property::DateTime
118
+ types[property.name] = Time.now
119
+ elsif property.is_a? DataMapper::Property::String
120
+ types[property.name] = ""
121
+ end
122
+
123
+
124
+ end
125
+
126
+ # Set the value for the identity field
127
+ identity_field = get_identity_field resource
128
+ fields.push(identity_field.dump(resource.key.first))
129
+
130
+ # Loop through the attributes defined in the mnesia record
131
+ # and set the value for each one except the identity field
132
+ table_attributes(model).each do |p|
133
+ if p.to_s!= identity_field.field
134
+ if types[p.to_sym].is_a? Integer
135
+ value = resource[p.to_sym] == nil ? 'undefined' : resource[p.to_sym]
136
+ fields.push(value)
137
+ elsif types[p.to_sym].is_a? Array
138
+ field = resource.model.properties.detect{|f|f.field == p.to_s}
139
+ value = resource[p.to_sym]
140
+ if resource[p.to_sym].length == 0
141
+ fields.push("'undefined'")
142
+ else
143
+ fields.push(field.dump(value))
144
+ end
145
+ elsif types[p.to_sym].is_a? Hash
146
+ value = resource[p.to_sym]
147
+ if resource[p.to_sym].nil?
148
+ fields.push("'undefined'")
149
+ else
150
+ fields.push(value)
151
+ end
152
+ else
153
+ if resource[p.to_sym].nil?
154
+ fields.push("'undefined'")
155
+ else
156
+ fields.push("\"" + resource[p.to_sym].to_s + "\"")
157
+ end
158
+ end
159
+
160
+ end
161
+ end
162
+ "#{model.to_s.downcase},{#{model.to_s.downcase},#{fields.join(',').to_s}}"
163
+ end
164
+
165
+
166
+ # Function called by create and update
167
+ def save(con, record)
168
+ if !erl_save(con,record)
169
+ raise WriteError, erl_save(con,record)
170
+ end
171
+ end
172
+
173
+ # Retrived the attribues defined for the table in mnesia
174
+ def table_attributes model
175
+ if @table_attribute.nil?
176
+ @table_attributes = con.erl("mnesia:table_info(" + model.to_s.downcase + ",attributes).")
177
+ end
178
+ @table_attributes
179
+ end
180
+
181
+ # Set the initial value for the identity field on record creation
182
+ def initialize_identity_field resource, value
183
+ identity_field = get_identity_field resource
184
+ identity_field.set(resource,value)
185
+ end
186
+
187
+ # Returns the field object defined as a key in the model
188
+ def get_identity_field resource
189
+ resource.model.key(name).detect{|p|p.key?}
190
+ end
191
+
192
+ # Gets the id value from the conditions passed in the query
193
+ def get_id_from_conditions conditions
194
+ conditions.map do |condition|
195
+ return condition.loaded_value if condition.subject.name == :id
196
+ end
197
+ nil
198
+ end
199
+
200
+ # ERLAND FUNCTIONS
201
+ def erl_save(con,record)
202
+ con.erl("mnesia:dirty_write(#{record}).")
203
+ end
204
+
205
+ # Reads the recuested records from mnesia and parses them to be pased to the Read function
206
+ def erl_read query
207
+ model = query.model
208
+ if query.limit.nil?
209
+ records = con.erl("mnesia:transaction(fun() ->qlc:eval( qlc:q([ X || X <- mnesia:table(#{model.to_s.downcase}) ]))end ).")[1]
210
+ elsif query.limit == 1 && !get_id_from_conditions(query.conditions).nil?
211
+ id = get_id_from_conditions(query.conditions)
212
+ if id.is_a? Integer
213
+ records = con.erl("mnesia:dirty_read({#{model.to_s.downcase},#{id}}).")
214
+ else
215
+ records = con.erl("mnesia:dirty_read({#{model.to_s.downcase},<<\"" + id.to_s + "\">>}).")
216
+ end
217
+ elsif query.limit > 1
218
+ records = con.erl("mnesia:transaction(fun() ->qlc:eval( qlc:next_answers(qlc:cursor(qlc:q([ X || X <- mnesia:table(#{query.model.to_s.downcase}) ])),#{query.limit}) )end ).")[1]
219
+ else
220
+ records = con.erl("mnesia:transaction(fun() ->qlc:eval( qlc:q([ X || X <- mnesia:table(#{model.to_s.downcase}) ]))end ).")[1]
221
+ end
222
+
223
+ result = []
224
+ attributes = table_attributes model
225
+ records.each do | r |
226
+ r.delete(model.to_s.downcase)
227
+ h = Hash.new
228
+ r.each_with_index do |item,i|
229
+ h[attributes[i]] = r[i] == 'undefined' ? nil : r[i]
230
+ end
231
+ result << h
232
+ end
233
+ result.reverse
234
+ end
235
+
236
+ # Sends the delete command to erland.
237
+ def erl_delete model, id
238
+ command = ""
239
+ if id.is_a? Integer
240
+ command = "mnesia:dirty_delete({#{model.to_s.downcase},#{id.to_s}})."
241
+ else
242
+ command = "mnesia:dirty_delete({#{model.to_s.downcase},<<\"" + id.to_s + "\">>})."
243
+ end
244
+ if !con.erl(command)
245
+ raise WriteError, con.erl(command)
246
+ end
247
+
248
+ end
249
+
250
+
251
+
252
+
253
+ class ConnectError < StandardError
254
+ end
255
+
256
+ class WriteError < StandardError
257
+ end
258
+
259
+ class ReadError < StandardError
260
+ end
261
+
262
+
263
+ end
264
+
265
+ end
@@ -0,0 +1,8 @@
1
+ require 'dm-mnesia-adapter'
2
+ module DataMapper
3
+ class Property
4
+ autoload :Record, 'types/record'
5
+ autoload :BinaryString, 'types/binary_string'
6
+ autoload :List, 'types/list'
7
+ end
8
+ end
@@ -0,0 +1,37 @@
1
+ module DataMapper
2
+ class Property
3
+ class BinaryString < Text
4
+ def custom?
5
+ true
6
+ end
7
+
8
+ def primitive?(value)
9
+ value.kind_of?(::String)
10
+ end
11
+
12
+ def valid?(value, negated = false)
13
+ super || dump(value).kind_of?(::String)
14
+ end
15
+
16
+ def load(value)
17
+ if value.nil?
18
+ nil
19
+ elsif value.is_a?(::String)
20
+ value
21
+ else
22
+ raise ArgumentError.new("not sure what type #{value.class}")
23
+ end
24
+ end
25
+
26
+
27
+ def dump(value)
28
+ "<<\"#{value.to_s}\">>"
29
+ end
30
+
31
+ def typecast_to_primitive(value)
32
+ value.to_s
33
+ end
34
+ end
35
+ end
36
+ end
37
+
@@ -0,0 +1,43 @@
1
+ module DataMapper
2
+ class Property
3
+ class List < Text
4
+ def custom?
5
+ true
6
+ end
7
+
8
+ def primitive?(value)
9
+ value.kind_of?(::Array) || value.kind_of?(::String)
10
+ end
11
+
12
+ def valid?(value, negated = false)
13
+ super || dump(value).kind_of?(::String)
14
+ end
15
+
16
+ def load(value)
17
+ if value.nil?
18
+ Array.new
19
+ elsif value.is_a?(::Array)
20
+ value
21
+ elsif value.is_a?(::String)
22
+ value
23
+ else
24
+ raise ArgumentError.new("not sure what type #{value.class}")
25
+ end
26
+ end
27
+
28
+
29
+ def dump(value)
30
+ if value.is_a?(::String)
31
+ value
32
+ else
33
+ "[#{value.join(",").to_s}]"
34
+ end
35
+ end
36
+
37
+ def typecast_to_primitive(value)
38
+ value.to_s
39
+ end
40
+ end
41
+ end
42
+ end
43
+
@@ -0,0 +1,42 @@
1
+ module DataMapper
2
+ class Property
3
+ class Record < Text
4
+ def custom?
5
+ true
6
+ end
7
+
8
+ def primitive?(value)
9
+ value.kind_of?(::Array) || value.kind_of?(::Hash)
10
+ end
11
+
12
+ def valid?(value, negated = false)
13
+ super || dump(value).kind_of?(::String)
14
+ end
15
+
16
+ def load(value)
17
+ if value.nil?
18
+ nil
19
+ elsif value.is_a?(::Array)
20
+ klass = value.shift.capitalize
21
+ theklass = Kernel.const_get(klass).new
22
+ theklass.send('properties').map(&:name).each_with_index() do |k,i|
23
+ theklass.send("#{k}=",value[i])
24
+ end
25
+ theklass
26
+ else
27
+ raise ArgumentError.new("not sure what type #{value.class}")
28
+ end
29
+ end
30
+
31
+
32
+ def dump(value)
33
+ value
34
+ end
35
+
36
+ def typecast_to_primitive(value)
37
+ value.to_s
38
+ end
39
+ end
40
+ end
41
+ end
42
+
@@ -0,0 +1,12 @@
1
+ require 'dm-core'
2
+
3
+ module DataMapper
4
+ class Property
5
+ class BinaryString < Text
6
+ def custom?
7
+ true
8
+ end
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,15 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require 'dm-core/spec/shared/adapter_spec'
3
+
4
+ describe DataMapper::Adapters::MnesiaAdapter do
5
+ before :all do
6
+ @adapter = DataMapper.setup(:default, :adapter => 'mnesia',
7
+ :hostname => 'localhost',
8
+ :port => 9900,
9
+ :mod => nil,
10
+ :ids_table => 'unique_ids')
11
+ end
12
+
13
+ it_should_behave_like 'An Adapter'
14
+
15
+ end
@@ -0,0 +1,4 @@
1
+ --colour
2
+ --loadby random
3
+ --format progress
4
+ --backtrace
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+
4
+ require 'dm-mnesia'
5
+
6
+ require 'dm-core'
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dm-mnesia
3
+ version: !ruby/object:Gem::Version
4
+ hash: 7
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 8
10
+ version: 0.2.8
11
+ platform: ruby
12
+ authors:
13
+ - Chad DePue
14
+ - Manuel Gomez
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-08-30 00:00:00 -03:00
20
+ default_executable:
21
+ dependencies: []
22
+
23
+ description: ""
24
+ email: chad@inakanetworks.com,manuel@inakanetworks.com
25
+ executables: []
26
+
27
+ extensions: []
28
+
29
+ extra_rdoc_files:
30
+ - README.rdoc
31
+ files:
32
+ - .gitignore
33
+ - Manifest
34
+ - README.rdoc
35
+ - Rakefile
36
+ - VERSION
37
+ - dm-mnesia.gemspec
38
+ - lib/dm-mnesia-adapter.rb
39
+ - lib/dm-mnesia.rb
40
+ - lib/types/binary_string.rb
41
+ - lib/types/list.rb
42
+ - lib/types/record.rb
43
+ - lib/types/tuple.rb
44
+ - pkg/dm-mnesia-0.0.0.gem
45
+ - spec/dm-mnesia-adapter_spec.rb
46
+ - spec/spec.opts
47
+ - spec/spec_helper.rb
48
+ has_rdoc: true
49
+ homepage: http://github.com/mergoc/dm-mnesia
50
+ licenses: []
51
+
52
+ post_install_message:
53
+ rdoc_options:
54
+ - --charset=UTF-8
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ hash: 3
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ hash: 3
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ requirements: []
76
+
77
+ rubyforge_project:
78
+ rubygems_version: 1.3.7
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: DataMapper adapter for Erlang's mnesia db
82
+ test_files:
83
+ - spec/dm-mnesia-adapter_spec.rb
84
+ - spec/spec_helper.rb