dm-datastore-adapter 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Genki Takiuchi
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/README ADDED
@@ -0,0 +1,30 @@
1
+ dm-datastore-adapter
2
+ =======
3
+
4
+ This is a DataMapper adapter to DataStore of Google App Engine.
5
+
6
+ Requirement
7
+ ===========
8
+
9
+ This adapter is designed to be used with JRuby.
10
+
11
+ Setup
12
+ =====
13
+
14
+ First of all, you must install Google AppEngine SDK and configure its
15
+ environment. After that, you can install this adapter by gem command like
16
+ this.
17
+
18
+ sudo gem install dm-datastore-adapter
19
+
20
+ Setting up
21
+ ----------
22
+
23
+ In your config/database.yml,
24
+
25
+ production:
26
+ :adapter: datastore
27
+ :host: localhost
28
+
29
+ You shouldn't use this adapter for environments other than production
30
+ environment.
data/Rakefile ADDED
@@ -0,0 +1,86 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+
4
+ require 'merb-core'
5
+ require 'merb-core/tasks/merb'
6
+
7
+ GEM_NAME = "dm-datastore-adapter"
8
+ GEM_VERSION = "0.1.0"
9
+ AUTHOR = "Genki Takiuchi"
10
+ EMAIL = "genki@s21g.com"
11
+ HOMEPAGE = "http://jmerbist.appspot.com/"
12
+ RUBYFORGE_PROJECT = 'asakusarb'
13
+ SUMMARY = "This is a DataMapper adapter to DataStore of Google App Engine."
14
+
15
+ spec = Gem::Specification.new do |s|
16
+ s.rubyforge_project = RUBYFORGE_PROJECT
17
+ s.name = GEM_NAME
18
+ s.version = GEM_VERSION
19
+ s.platform = Gem::Platform::RUBY
20
+ s.has_rdoc = true
21
+ s.extra_rdoc_files = ["README", "LICENSE", 'TODO']
22
+ s.summary = SUMMARY
23
+ s.description = s.summary
24
+ s.author = AUTHOR
25
+ s.email = EMAIL
26
+ s.homepage = HOMEPAGE
27
+ s.add_dependency('dm-core', '>= 0.9.10')
28
+ s.add_dependency('addressable', '>= 2.0.0')
29
+ s.require_path = 'lib'
30
+ s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{lib,spec}/**/*")
31
+ end
32
+
33
+ Rake::GemPackageTask.new(spec) do |pkg|
34
+ pkg.need_tar = true
35
+ pkg.gem_spec = spec
36
+ end
37
+
38
+ desc "install the plugin as a gem"
39
+ task :install do
40
+ Merb::RakeHelper.install(GEM_NAME, :version => GEM_VERSION)
41
+ end
42
+
43
+ desc "Uninstall the gem"
44
+ task :uninstall do
45
+ Merb::RakeHelper.uninstall(GEM_NAME, :version => GEM_VERSION)
46
+ end
47
+
48
+ desc "Create a gemspec file"
49
+ task :gemspec do
50
+ File.open("#{GEM_NAME}.gemspec", "w") do |file|
51
+ file.puts spec.to_ruby
52
+ end
53
+ end
54
+
55
+ desc "Run specs"
56
+ task :spec do
57
+ sh "jruby -S spec --color spec"
58
+ end
59
+
60
+ desc 'Package and upload the release to rubyforge.'
61
+ task :release => :package do |t|
62
+ require 'rubyforge'
63
+ v = ENV["VERSION"] or abort "Must supply VERSION=x.y.z"
64
+ abort "Versions don't match #{v} vs #{GEM_VERSION}" unless v == GEM_VERSION
65
+ pkg = "pkg/#{GEM_NAME}-#{GEM_VERSION}"
66
+
67
+ require 'rubyforge'
68
+ rf = RubyForge.new.configure
69
+ puts "Logging in"
70
+ rf.login
71
+
72
+ c = rf.userconfig
73
+ # c["release_notes"] = description if description
74
+ # c["release_changes"] = changes if changes
75
+ c["preformatted"] = true
76
+
77
+ files = [
78
+ "#{pkg}.tgz",
79
+ "#{pkg}.gem"
80
+ ].compact
81
+
82
+ puts "Releasing #{GEM_NAME} v. #{GEM_VERSION}"
83
+ rf.add_release RUBYFORGE_PROJECT, GEM_NAME, GEM_VERSION, *files
84
+ end
85
+
86
+ task :default => :spec
data/TODO ADDED
@@ -0,0 +1,2 @@
1
+ TODO:
2
+ Support various type of keys other than id/Serial
Binary file
@@ -0,0 +1,18 @@
1
+ # make sure we're running inside Merb
2
+ if defined?(Merb::Plugins)
3
+
4
+ # Merb gives you a Merb::Plugins.config hash...feel free to put your stuff in your piece of it
5
+ Merb::Plugins.config[:dm_datastore_adapter] = {
6
+ }
7
+
8
+ Merb::BootLoader.before_app_loads do
9
+ # require code that must be loaded before the application
10
+ require File.join(%w(dm-datastore-adapter datastore-adapter))
11
+ end
12
+
13
+ Merb::BootLoader.after_app_loads do
14
+ # code that can be required after the application loads
15
+ end
16
+
17
+ Merb::Plugins.add_rakefiles "dm-datastore-adapter/merbtasks"
18
+ end
@@ -0,0 +1,210 @@
1
+ require 'java'
2
+ require 'addressable/uri'
3
+ require 'appengine-api-1.0-sdk-1.2.0.jar'
4
+
5
+ module DataMapper
6
+ module Adapters
7
+ class DataStoreAdapter < AbstractAdapter
8
+ module DS
9
+ unless const_defined?("Service")
10
+ import com.google.appengine.api.datastore.DatastoreServiceFactory
11
+ import com.google.appengine.api.datastore.Entity
12
+ import com.google.appengine.api.datastore.FetchOptions
13
+ import com.google.appengine.api.datastore.KeyFactory
14
+ import com.google.appengine.api.datastore.Key
15
+ import com.google.appengine.api.datastore.EntityNotFoundException
16
+ import com.google.appengine.api.datastore.Query
17
+ import com.google.appengine.api.datastore.Text
18
+ Service = DatastoreServiceFactory.datastore_service
19
+ end
20
+ end
21
+
22
+ def create(resources)
23
+ created = 0
24
+ resources.each do |resource|
25
+ entity = DS::Entity.new(resource.class.name)
26
+ resource.attributes.each do |key, value|
27
+ ds_set(entity, key, value)
28
+ end
29
+ begin
30
+ ds_key = DS::Service.put(entity)
31
+ rescue Exception
32
+ else
33
+ ds_id = ds_key.get_id
34
+ resource.model.key.each do |property|
35
+ resource.attribute_set property.field, ds_id
36
+ ds_set(entity, property.field, ds_id)
37
+ end
38
+ DS::Service.put(entity)
39
+ created += 1
40
+ end
41
+ end
42
+ created
43
+ end
44
+
45
+ def update(attributes, query)
46
+ updated = 0
47
+ resources = read_many(query)
48
+ resources.each do |resource|
49
+ entity = DS::Service.get(ds_key_from_resource(resource))
50
+ resource.attributes.each do |key, value|
51
+ ds_set(entity, key, value)
52
+ end
53
+ begin
54
+ ds_key = DS::Service.put(entity)
55
+ rescue Exception
56
+ else
57
+ resource.model.key.each do |property|
58
+ resource.attribute_set property.field, ds_key.get_id
59
+ end
60
+ updated += 1
61
+ end
62
+ end
63
+ updated
64
+ end
65
+
66
+ def delete(query)
67
+ deleted = 0
68
+ resources = read_many(query)
69
+ resources.each do |resource|
70
+ begin
71
+ ds_key = ds_key_from_resource(resource)
72
+ DS::Service.delete([ds_key].to_java(DS::Key))
73
+ rescue Exception
74
+ else
75
+ deleted += 1
76
+ end
77
+ end
78
+ deleted
79
+ end
80
+
81
+ def read_many(query)
82
+ q = build_query(query)
83
+ fo = build_fetch_option(query)
84
+ iter = if fo
85
+ DS::Service.prepare(q).as_iterable(fo)
86
+ else
87
+ DS::Service.prepare(q).as_iterable
88
+ end
89
+ Collection.new(query) do |collection|
90
+ iter.each do |entity|
91
+ collection.load(query.fields.map do |property|
92
+ property.key? ? entity.key.get_id : ds_get(entity, property.field)
93
+ end)
94
+ end
95
+ end
96
+ end
97
+
98
+ def read_one(query)
99
+ q = build_query(query)
100
+ fo = build_fetch_option(query)
101
+ entity = if fo
102
+ DS::Service.prepare(q).as_iterable(fo).map{|i| break i}
103
+ else
104
+ DS::Service.prepare(q).asSingleEntity
105
+ end
106
+ return nil if entity.blank?
107
+ query.model.load(query.fields.map do |property|
108
+ property.key? ? entity.key.get_id : ds_get(entity, property.field)
109
+ end, query)
110
+ end
111
+
112
+ def aggregate(query)
113
+ op = query.fields.find{|p| p.kind_of?(DataMapper::Query::Operator)}
114
+ if op.nil?
115
+ raise NotImplementedError, "No operator supplied."
116
+ end
117
+ if respond_to?(op.operator)
118
+ self.send op.operator, query
119
+ else
120
+ raise NotImplementedError, "#{op.operator} is not supported yet."
121
+ end
122
+ end
123
+
124
+ def count(query)
125
+ q = build_query(query)
126
+ count = DS::Service.prepare(q).countEntities
127
+ [query.limit ? [count, query.limit].min : count]
128
+ end
129
+
130
+ protected
131
+ def normalize_uri(uri_or_options)
132
+ if uri_or_options.kind_of?(Hash)
133
+ uri_or_options = Addressable::URI.new(
134
+ :scheme => uri_or_options[:adapter].to_s,
135
+ :user => uri_or_options[:username],
136
+ :password => uri_or_options[:password],
137
+ :host => uri_or_options[:host],
138
+ :path => uri_or_options[:database]).to_s
139
+ end
140
+ Addressable::URI.parse(uri_or_options)
141
+ end
142
+
143
+ private
144
+ def ds_key_from_resource(resource)
145
+ DS::KeyFactory.create_key(resource.class.name, resource.key.first)
146
+ end
147
+
148
+ def build_query(query)
149
+ q = DS::Query.new(query.model.name)
150
+ query.conditions.each do |tuple|
151
+ next if tuple.size == 2
152
+ op, property, value = *tuple
153
+ ds_op = case op
154
+ when :eql; DS::Query::FilterOperator::EQUAL
155
+ when :gt; DS::Query::FilterOperator::GREATER_THAN
156
+ when :gte; DS::Query::FilterOperator::GREATER_THAN_OR_EQUAL
157
+ when :lt; DS::Query::FilterOperator::LESS_THAN
158
+ when :lte; DS::Query::FilterOperator::LESS_THAN_OR_EQUAL
159
+ else next
160
+ end
161
+ q = q.add_filter(property.name.to_s, ds_op, [value].flatten.first)
162
+ end
163
+ query.order.each do |o|
164
+ key = o.property.name.to_s
165
+ if o.direction == :asc
166
+ q = q.add_sort(key, DS::Query::SortDirection::ASCENDING)
167
+ else
168
+ q = q.add_sort(key, DS::Query::SortDirection::DESCENDING)
169
+ end
170
+ end
171
+ q
172
+ end
173
+
174
+ def build_fetch_option(query)
175
+ fo = nil
176
+ if query.limit && query.limit != 1
177
+ fo = DS::FetchOptions::Builder.with_limit(query.limit)
178
+ end
179
+ if query.offset
180
+ if fo
181
+ fo = fo.offset(query.offset)
182
+ else
183
+ fo = DS::FetchOptions::Builder.with_offset(query.offset)
184
+ end
185
+ end
186
+ fo
187
+ end
188
+
189
+ def ds_get(entity, name)
190
+ name = name.to_s
191
+ if entity.has_property(name)
192
+ result = entity.get_property(name)
193
+ result.is_a?(DS::Text) ? result.value : result
194
+ else
195
+ nil
196
+ end
197
+ end
198
+
199
+ def ds_set(entity, name, value)
200
+ if value.is_a?(String) && value.length >= 500
201
+ entity.set_property(name.to_s, DS::Text.new(value))
202
+ else
203
+ entity.set_property(name.to_s, value)
204
+ end
205
+ end
206
+ end
207
+
208
+ DatastoreAdapter = DataStoreAdapter
209
+ end
210
+ end
@@ -0,0 +1,6 @@
1
+ namespace :dm_datastore_adapter do
2
+ desc "Do something for dm-datastore-adapter"
3
+ task :default do
4
+ puts "dm-datastore-adapter doesn't do anything"
5
+ end
6
+ end
@@ -0,0 +1,36 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "dm-datastore-adapter" do
4
+ class ::Person
5
+ include DataMapper::Resource
6
+ def self.default_repository_name; :datastore end
7
+ property :id, Serial
8
+ property :name, String
9
+ property :age, Integer
10
+ property :weight, Float
11
+ property :created_at, DateTime
12
+ property :created_on, Date
13
+ belongs_to :company
14
+ end
15
+
16
+ class ::Company
17
+ include DataMapper::Resource
18
+ def self.default_repository_name; :datastore end
19
+ property :id, Serial
20
+ property :name, String
21
+ has n, :users
22
+ end
23
+
24
+ before do
25
+ @person = Person.new(:name => 'Jon', :age => 40, :weight => 100)
26
+ end
27
+
28
+ it "should build person" do
29
+ @person.should_not be_nil
30
+ end
31
+
32
+ it "should save person successfully" do
33
+ pending "Needing mocks"
34
+ @person.save.should be_true
35
+ end
36
+ end
@@ -0,0 +1,11 @@
1
+ $:.push File.join(File.dirname(__FILE__), '..', 'lib')
2
+
3
+ require 'rubygems'
4
+ require 'dm-core'
5
+ require 'dm-aggregates'
6
+ require 'dm-types'
7
+ require 'dm-datastore-adapter/datastore-adapter'
8
+
9
+ DataMapper.setup(:datastore,
10
+ :adapter => :datastore,
11
+ :database => 'sample')
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dm-datastore-adapter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Genki Takiuchi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-15 00:00:00 +09: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: addressable
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.0.0
34
+ version:
35
+ description: This is a DataMapper adapter to DataStore of Google App Engine.
36
+ email: genki@s21g.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - README
43
+ - LICENSE
44
+ - TODO
45
+ files:
46
+ - LICENSE
47
+ - README
48
+ - Rakefile
49
+ - TODO
50
+ - lib/appengine-api-1.0-sdk-1.2.0.jar
51
+ - lib/dm-datastore-adapter
52
+ - lib/dm-datastore-adapter/datastore-adapter.rb
53
+ - lib/dm-datastore-adapter/merbtasks.rb
54
+ - lib/dm-datastore-adapter.rb
55
+ - spec/dm-datastore-adapter_spec.rb
56
+ - spec/spec_helper.rb
57
+ has_rdoc: true
58
+ homepage: http://jmerbist.appspot.com/
59
+ post_install_message:
60
+ rdoc_options: []
61
+
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ version:
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: "0"
75
+ version:
76
+ requirements: []
77
+
78
+ rubyforge_project: asakusarb
79
+ rubygems_version: 1.3.1
80
+ signing_key:
81
+ specification_version: 2
82
+ summary: This is a DataMapper adapter to DataStore of Google App Engine.
83
+ test_files: []
84
+