irjudson-dm-persevere-adapter 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ === 0.0.1 / 2009-02-19
2
+
3
+ * Release!
4
+
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Montana State University
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,13 @@
1
+ History.txt
2
+ LICENSE.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ TODO
7
+ lib/persevere_adapter.rb
8
+ lib/persevere_adapter/version.rb
9
+ spec/integration/persevere_adapter_spec.rb
10
+ spec/spec.opts
11
+ spec/spec_helper.rb
12
+ tasks/install.rb
13
+ tasks/spec.rb
data/README.txt ADDED
@@ -0,0 +1,86 @@
1
+ = dm-persevere-adapter
2
+
3
+ A DataMapper adapter for Persevere (http://www.persvr.org/)
4
+
5
+ This requires the persevere gem (http://github.com/irjudson/persevere) which provides a ruby interface to Persevere.
6
+
7
+ == Usage
8
+
9
+ DM Persevere Adapter is very simple and very similar to the REST
10
+ Adapter, however it has two differences: 1) instead of XML it uses
11
+ JSON, and 2) Persevere supports typing using JSON Schema. These
12
+ differences make it valuable to have a separate DM adapter
13
+ specifically for Persevere so it can leverage richer aspects of
14
+ persevere.
15
+
16
+ The setup and resource mapping is identical to standard datamapper
17
+ objects, as can be seen below.
18
+
19
+ DataMapper.setup(:default, {
20
+ :adapter => 'persevere',
21
+ :host => 'localhost',
22
+ :port => '8080'
23
+ })
24
+
25
+ class MyUser
26
+ include DataMapper::Resource
27
+
28
+ property :id, Serial
29
+ property :uuid, String
30
+ property :name, String
31
+ property :first_name, String
32
+ property :last_name, String
33
+ property :groupid, Integer
34
+ property :userid, Integer
35
+ property :username, String
36
+ property :homedirectory, String
37
+
38
+ end
39
+
40
+ To use with Rails, you can put this in your environment.rb:
41
+ config.gem "dm-core"
42
+ config.gem "data_objects"
43
+ config.gem "dm-persevere-adapter", :lib => 'persevere_adapter'
44
+
45
+ With a database.yml:
46
+
47
+ development: &defaults
48
+ :adapter: persevere
49
+ :host: localhost
50
+ :port: 8080
51
+
52
+ test:
53
+ <<: *defaults
54
+
55
+ production:
56
+ <<: *defaults
57
+
58
+ == Code
59
+
60
+ # Create
61
+ user = MyUser.new(:username => "dmtest", :uuid => UUID.random_create().to_s,
62
+ :name => "DataMapper Test", :homedirectory => "/home/dmtest",
63
+ :first_name => "DataMapperTest", :last_name => "User",
64
+ :userid => 3, :groupid => 500)
65
+ user.save
66
+
67
+ # Retrieve
68
+ user = MyUser.first(:netid => 'dmtest')
69
+ puts user
70
+
71
+ # Modify
72
+ if user.update_attributes(:name => 'DM Test')
73
+ puts user
74
+ else
75
+ puts "Failed to update attributes."
76
+ end
77
+
78
+ # Delete
79
+ result = user.destroy
80
+ puts "Result: #{result}"
81
+
82
+ == To Do:
83
+
84
+ - Make a do-adapter for persevere.
85
+ - Finish Query details (limit, order, etc)
86
+ - Cleanup Documentation
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ require 'pathname'
2
+ require 'rubygems'
3
+ require 'hoe'
4
+
5
+ ROOT = Pathname(__FILE__).dirname.expand_path
6
+ JRUBY = RUBY_PLATFORM =~ /java/
7
+ WINDOWS = Gem.win_platform?
8
+ SUDO = (WINDOWS || JRUBY) ? '' : ('sudo' unless ENV['SUDOLESS'])
9
+
10
+ require ROOT + 'lib/persevere_adapter/version'
11
+
12
+ # define some constants to help with task files
13
+ GEM_NAME = 'dm-persevere-adapter'
14
+ GEM_VERSION = DataMapper::PersevereAdapter::VERSION
15
+
16
+ Hoe.new(GEM_NAME, GEM_VERSION) do |p|
17
+ p.developer('Ivan R. Judson', 'irjudson [a] gmail [d] com')
18
+
19
+ p.description = 'A DataMapper Adapter for persevere'
20
+ p.summary = 'A DataMapper Adapter for persevere'
21
+ p.url = 'http://github.com/USERNAME/dm-persevere-adapter'
22
+
23
+ p.clean_globs |= %w[ log pkg coverage ]
24
+ p.spec_extras = {
25
+ :has_rdoc => true,
26
+ :extra_rdoc_files => %w[ README.txt LICENSE.txt TODO History.txt ]
27
+ }
28
+
29
+ p.extra_deps = [
30
+ ['dm-core', "~> 0.9.10"],
31
+ ['extlib', "~> 0.9.10"],
32
+ ['persevere', "~> 1.0.0"]
33
+ ]
34
+
35
+ end
36
+
37
+ Pathname.glob(ROOT.join('tasks/**/*.rb').to_s).each { |f| require f }
data/TODO ADDED
File without changes
@@ -0,0 +1,310 @@
1
+ gem 'dm-core', '~> 0.9.10'
2
+ require 'dm-core'
3
+ require 'rubygems'
4
+ require 'extlib'
5
+ require 'json'
6
+ require 'persevere'
7
+
8
+ module DataMapper
9
+ module Adapters
10
+ class PersevereAdapter < AbstractAdapter
11
+ ##
12
+ # Used by DataMapper to put records into a data-store: "INSERT"
13
+ # in SQL-speak. It takes an array of the resources (model
14
+ # instances) to be saved. Resources each have a key that can be
15
+ # used to quickly look them up later without searching, if the
16
+ # adapter supports it.
17
+ #
18
+ # @param [Array<DataMapper::Resource>] resources
19
+ # The set of resources (model instances)
20
+ #
21
+ # @return [Integer]
22
+ # The number of records that were actually saved into the
23
+ # data-store
24
+ #
25
+ # @api semipublic
26
+ def create(resources)
27
+ created = 0
28
+ resources.each do |resource|
29
+ #
30
+ # This isn't the best solution but for an adapter, it'd be nice
31
+ # to support objects being in *tables* instead of in one big icky
32
+ # sort of table.
33
+ #
34
+ tblname = Extlib::Inflection.classify(resource.class).pluralize
35
+
36
+ if ! @classes.include?(tblname)
37
+ payload = {
38
+ 'id' => tblname,
39
+ 'extends' => { "$ref" => "/Class/Object" }
40
+ }
41
+
42
+ response = @persevere.create("/Class/", payload)
43
+ end
44
+
45
+ path = "/#{tblname}/"
46
+ payload = resource.attributes
47
+ payload.delete(:id)
48
+
49
+ response = @persevere.create(path, payload)
50
+
51
+ # Check the response, this needs to be more robust and raise
52
+ # exceptions when there's a problem
53
+
54
+ if response.code == "201"# good:
55
+ rh = JSON.parse(response.body)
56
+ created += 1
57
+ else
58
+ return false
59
+ end
60
+ end
61
+
62
+ # Return the number of resources created in persevere.
63
+ return created
64
+ end
65
+
66
+ ##
67
+ # Used by DataMapper to update the attributes on existing
68
+ # records in a data-store: "UPDATE" in SQL-speak. It takes a
69
+ # hash of the attributes to update with, as well as a query
70
+ # object that specifies which resources should be updated.
71
+ #
72
+ # @param [Hash] attributes
73
+ # A set of key-value pairs of the attributes to update the
74
+ # resources with.
75
+ # @param [DataMapper::Query] query
76
+ # The query that should be used to find the resource(s) to
77
+ # update.
78
+ #
79
+ # @return [Integer]
80
+ # the number of records that were successfully updated
81
+ #
82
+ # @api semipublic
83
+ def update(attributes, query)
84
+ updated = 0
85
+ puts "In Update A: #{attributes} Q: #{query.conditions.inspect}"
86
+ resources = read_many(query)
87
+ puts "Resources found: #{resources}"
88
+ resources.each do |resource|
89
+ key = resource.class.key(self.name).map do |property|
90
+ resource.instance_variable_get(property.instance_variable_name)
91
+ end
92
+
93
+ tblname = Extlib::Inflection.classify(resource.class).pluralize
94
+ path = "/#{tblname}/#{resource.id}"
95
+
96
+ result = @persevere.update(path, resource.attributes)
97
+
98
+ if result # good:
99
+ updated += 1
100
+ else
101
+ return false
102
+ end
103
+ end
104
+ return updated
105
+ end
106
+
107
+ ##
108
+ # Look up a single record from the data-store. "SELECT ... LIMIT
109
+ # 1" in SQL. Used by Model#get to find a record by its
110
+ # identifier(s), and Model#first to find a single record by some
111
+ # search query.
112
+ #
113
+ # @param [DataMapper::Query] query
114
+ # The query to be used to locate the resource.
115
+ #
116
+ # @return [DataMapper::Resource]
117
+ # A Resource object representing the record that was found, or
118
+ # nil for no matching records.
119
+ #
120
+ # @api semipublic
121
+
122
+ def read_one(query)
123
+ read_many(query)[0]
124
+ end
125
+
126
+ ##
127
+ # Looks up a collection of records from the data-store: "SELECT"
128
+ # in SQL. Used by Model#all to search for a set of records;
129
+ # that set is in a DataMapper::Collection object.
130
+ #
131
+ # @param [DataMapper::Query] query
132
+ # The query to be used to seach for the resources
133
+ #
134
+ # @return [DataMapper::Collection]
135
+ # A collection of all the resources found by the query.
136
+ #
137
+ # @api semipublic
138
+ def read_many(query)
139
+ resources = Array.new
140
+
141
+ json_query = make_json_query(query.conditions)
142
+
143
+ tblname = Extlib::Inflection.classify(query.model).pluralize
144
+ path = "/#{tblname}/#{json_query}"
145
+
146
+ response = @persevere.retrieve(path)
147
+
148
+ if response.code == "200"
149
+ results = JSON.parse(response.body)
150
+ results.each do |result|
151
+ values = query.fields.collect do |field|
152
+ result[field.field.to_s]
153
+ end
154
+ resources << query.model.load(values, query)
155
+ end
156
+ else
157
+ return false
158
+ end
159
+
160
+ # Return results
161
+ resources
162
+ end
163
+
164
+ alias :read :read_many
165
+
166
+ ##
167
+ # Destroys all the records matching the given query. "DELETE" in SQL.
168
+ #
169
+ # @param [DataMapper::Query] query
170
+ # The query used to locate the resources to be deleted.
171
+ #
172
+ # @return [Integer]
173
+ # The number of records that were deleted.
174
+ #
175
+ # @api semipublic
176
+ def delete(query)
177
+ deleted = 0
178
+ resources = read_many(query)
179
+ resources.each do |resource|
180
+ key = resource.class.key(self.name).map do |property|
181
+ resource.instance_variable_get(property.instance_variable_name)
182
+ end
183
+
184
+ tblname = Extlib::Inflection.classify(resource.class).pluralize
185
+ path = "/#{tblname}/#{resource.id}"
186
+
187
+ result = @persevere.delete(path)
188
+
189
+ if result # ok
190
+ deleted += 1
191
+ end
192
+ end
193
+ return deleted
194
+ end
195
+
196
+ private
197
+
198
+ ##
199
+ # Make a new instance of the adapter. The @model_records ivar is
200
+ # the 'data-store' for this adapter. It is not shared amongst
201
+ # multiple incarnations of this adapter, eg
202
+ # DataMapper.setup(:default, :adapter => :in_memory);
203
+ # DataMapper.setup(:alternate, :adapter => :in_memory) do not
204
+ # share the data-store between them.
205
+ #
206
+ # @param [String, Symbol] name
207
+ # The name of the DataMapper::Repository using this adapter.
208
+ # @param [String, Hash] uri_or_options
209
+ # The connection uri string, or a hash of options to set up
210
+ # the adapter
211
+ #
212
+ # @api semipublic
213
+
214
+ def initialize(name, uri_or_options)
215
+ super
216
+ @persevere = Persevere.new(make_uri(uri_or_options))
217
+ @resource_naming_convention = NamingConventions::Resource::Underscored
218
+ @identity_maps = {}
219
+ @classes = []
220
+
221
+ # Because this is an AbstractAdapter and not a
222
+ # DataObjectAdapter, we can't assume there are any schemas
223
+ # present, so we retrieve the ones that exist and keep them up
224
+ # to date
225
+ result = @persevere.retrieve('/Class[=id]')
226
+ if result.code == "200"
227
+ hresult = JSON.parse(result.body)
228
+ hresult.each do |cname|
229
+ junk,name = cname.split("/")
230
+ @classes << name
231
+ end
232
+
233
+ else
234
+ puts "Error retrieving existing tables: ", result.message
235
+ end
236
+ end
237
+
238
+ def make_uri(uri_or_options)
239
+ if uri_or_options.is_a?(String)
240
+ begin
241
+ URI.parse(uri_or_options)
242
+ return uri_or_options.to_s
243
+ rescue URI::InvalidURIError => e
244
+ puts "Error parsing persevere URI: ", e
245
+ end
246
+ elsif uri_or_options.is_a?(Hash)
247
+ nh = uri_or_options.dup
248
+ nh[:scheme] = nh[:adapter]
249
+ nh.delete(:scheme)
250
+ return URI::HTTP.build(nh).to_s
251
+ end
252
+ end
253
+
254
+ ##
255
+ # Convert a DataMapper Resource to a JSON.
256
+ #
257
+ # @param [Query] query
258
+ # The DataMapper query object passed in
259
+ #
260
+ # @api semipublic
261
+ def make_json(resource)
262
+ json_rsrc = nil
263
+
264
+ # Gather up all the attributes
265
+ json_rsrc = resource.attributes.to_json
266
+ end
267
+
268
+ ##
269
+ # Convert a DataMapper Query to a JSON Query.
270
+ #
271
+ # @param [Query] query
272
+ # The DataMapper query object passed in
273
+ #
274
+ # @api semipublic
275
+
276
+ def make_json_query(conditions)
277
+ query_terms = Array.new
278
+ conditions.each do |condition|
279
+ v = condition[1].typecast(condition[2])
280
+ if v.is_a?(String)
281
+ value = "'#{condition[2]}'"
282
+ else
283
+ value = "#{condition[2]}"
284
+ end
285
+ case condition[0]
286
+ when :eql
287
+ query_terms << "#{condition[1].field()}=#{value}"
288
+ when :lt
289
+ query_terms << "#{condition[1].field()}<#{value}"
290
+ when :gt
291
+ query_terms << "#{condition[1].field()}>#{value}"
292
+ when :lte
293
+ query_terms << "#{condition[1].field()}<=#{value}"
294
+ when :gte
295
+ query_terms << "#{condition[1].field()}=>#{value}"
296
+ when :not
297
+ query_terms << "#{condition[1].field()}!=#{value}"
298
+ when :like
299
+ if condition[2].is_a?(String)
300
+ query_terms << "#{condition[1].field()}~'*#{condition[2].to_s}*'"
301
+ end
302
+ else
303
+ puts "Unknown condition: #{condition[0]}"
304
+ end
305
+ end
306
+ query = "?#{query_terms.join("&")}"
307
+ end
308
+ end
309
+ end
310
+ end
@@ -0,0 +1,5 @@
1
+ module DataMapper
2
+ module PersevereAdapter
3
+ VERSION = '0.0.2'
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
+
4
+ describe 'DataMapper::Adapters::PersevereAdapter' do
5
+
6
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,28 @@
1
+ require 'pathname'
2
+ require 'rubygems'
3
+
4
+ gem 'rspec'
5
+ require 'spec'
6
+
7
+ require Pathname(__FILE__).dirname.expand_path.parent + 'lib/persevere_adapter'
8
+
9
+ DataMapper.setup(:default, {
10
+ :adapter => 'persevere',
11
+ :host => 'localhost',
12
+ :port => '8080',
13
+ :uri => 'http://localhost:8080'
14
+ })
15
+
16
+ #
17
+ # I need to make the Book class for Books to relate to
18
+ #
19
+
20
+ class Book
21
+ include DataMapper::Resource
22
+
23
+ # Persevere only does id's as strings.
24
+ property :id, String, :serial => true
25
+ property :author, String
26
+ property :created_at, DateTime
27
+ property :title, String
28
+ end
data/tasks/install.rb ADDED
@@ -0,0 +1,13 @@
1
+ def sudo_gem(cmd)
2
+ sh "#{SUDO} #{RUBY} -S gem #{cmd}", :verbose => false
3
+ end
4
+
5
+ desc "Install #{GEM_NAME} #{GEM_VERSION}"
6
+ task :install => [ :package ] do
7
+ sudo_gem "install --local pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources"
8
+ end
9
+
10
+ desc "Uninstall #{GEM_NAME} #{GEM_VERSION}"
11
+ task :uninstall => [ :clobber ] do
12
+ sudo_gem "uninstall #{GEM_NAME} -v#{GEM_VERSION} -Ix"
13
+ end
data/tasks/spec.rb ADDED
@@ -0,0 +1,25 @@
1
+ begin
2
+ gem 'rspec'
3
+ require 'spec'
4
+ require 'spec/rake/spectask'
5
+
6
+ task :default => [ :spec ]
7
+
8
+ desc 'Run specifications'
9
+ Spec::Rake::SpecTask.new(:spec) do |t|
10
+ t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
11
+ t.spec_files = Pathname.glob((ROOT + 'spec/**/*_spec.rb').to_s)
12
+
13
+ begin
14
+ gem 'rcov', '~>0.8'
15
+ t.rcov = JRUBY ? false : (ENV.has_key?('NO_RCOV') ? ENV['NO_RCOV'] != 'true' : true)
16
+ t.rcov_opts << '--exclude' << 'spec'
17
+ t.rcov_opts << '--text-summary'
18
+ t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
19
+ rescue LoadError
20
+ # rcov not installed
21
+ end
22
+ end
23
+ rescue LoadError
24
+ # rspec not installed
25
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: irjudson-dm-persevere-adapter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Ivan R. Judson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-19 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: dm-core~
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.9.10
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: hoe
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.8.3
34
+ version:
35
+ description: A DataMapper Adapter for persevere
36
+ email:
37
+ - irjudson [a] gmail [d] com
38
+ executables: []
39
+
40
+ extensions: []
41
+
42
+ extra_rdoc_files:
43
+ - README.txt
44
+ - LICENSE.txt
45
+ - TODO
46
+ - History.txt
47
+ files:
48
+ - History.txt
49
+ - LICENSE.txt
50
+ - Manifest.txt
51
+ - README.txt
52
+ - Rakefile
53
+ - TODO
54
+ - lib/persevere_adapter.rb
55
+ - lib/persevere_adapter/version.rb
56
+ - spec/integration/persevere_adapter_spec.rb
57
+ - spec/spec.opts
58
+ - spec/spec_helper.rb
59
+ - tasks/install.rb
60
+ - tasks/spec.rb
61
+ has_rdoc: true
62
+ homepage: http://github.com/USERNAME/dm-persevere-adapter
63
+ post_install_message:
64
+ rdoc_options:
65
+ - --main
66
+ - README.txt
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: "0"
74
+ version:
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: "0"
80
+ version:
81
+ requirements: []
82
+
83
+ rubyforge_project: dm-persevere-adapter
84
+ rubygems_version: 1.2.0
85
+ signing_key:
86
+ specification_version: 2
87
+ summary: A DataMapper Adapter for persevere
88
+ test_files: []
89
+