datamapper4rail 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2009-03-18
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
@@ -0,0 +1,22 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ generators/dm_install/dm_install_generator.rb
6
+ generators/dm_install/templates/datamapper.rake
7
+ generators/dm_migration/dm_migration_generator.rb
8
+ generators/dm_migration/templates/migration.rb
9
+ generators/dm_model/dm_model_generator.rb
10
+ generators/dm_model/templates/model.rb
11
+ generators/dm_model/templates/unit_test.rb
12
+ generators/rspec_default_values.rb
13
+ generators/rspec_dm_model/rspec_dm_model_generator.rb
14
+ generators/rspec_dm_model/templates/model.rb
15
+ generators/rspec_dm_model/templates/model_spec.rb
16
+ lib/datamapper4rails.rb
17
+ lib/datamapper4rails/adapters/base_adapter.rb
18
+ lib/datamapper4rails/adapters/restful_adapter.rb
19
+ lib/datamapper4rails/database_config.rb
20
+ lib/datamapper4rails/datamapper_store.rb
21
+ lib/datamapper4rails/restful_transactions.rb
22
+ lib/datamapper4rails/version.rb
@@ -0,0 +1,101 @@
1
+ = datamapper4rails
2
+
3
+ * http://datamapper4rail.rubyforge.org
4
+
5
+ == DESCRIPTION:
6
+
7
+ collection of datamapper related extensions. mostly needed to run within rails. the restful transactions is around filter for rails. the restful adapter can be outside of rails. datamapper store is a session store for rails which uses datamapper as persistent layer. the generators produces datamapper models for your rails application. quite a few things are "stolen" from dm-more/rails_datamapper. a lot of things do not work there and patches are still in process to be applied so until dm-more/rails_datamapper catches up, ut I hope these two project merge someday again.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ * restful adapter does handle associations partially and does not handle collections
12
+
13
+ == SYNOPSIS:
14
+
15
+ === restful transaction
16
+
17
+ wraps all modifying requests (POST, PUT, DELETE) into a transaction. any error/exception as well render of a page will rollback the transaction. typically if a POST, PUT or DELETE succeeds then a redirect gets send back to browser.
18
+
19
+ credits of the main idea goes to http://www.redhillonrails.org
20
+
21
+ is implemented as around filter in rails and gets prepend to the filters of the action_controller of rails
22
+
23
+ require 'datamapper4rails/restful_transations
24
+
25
+ === datamapper session store
26
+
27
+ this is just the datamapper compaign to the activerecord_store.
28
+
29
+ in config/environment.rb add
30
+
31
+ config.action_controller.session_store = :datamapper_store
32
+
33
+ in case you need a memory cache for your sessions add this
34
+
35
+ config.action_controller.session = { :cache => true }
36
+
37
+ === generators for datamapper models
38
+
39
+ this is taken from dm-more/rails_datamapper and extended it - to my liking. patches are submitted upstream until they get applied this will remain here.
40
+
41
+ * dm-install: rake task for datamapper: automigrate, autoupgrade
42
+ * dm_model
43
+ * rspec_dm_model
44
+
45
+ === database config
46
+
47
+ this is also taken from dm-more/rails_datamapper and just uses the
48
+ 'config/database.yml' to configure a database connection. it also allows to configure multiple repositories for a single environment just nest the config in such a way:
49
+
50
+ development:
51
+ repositories:
52
+ default:
53
+ adapter: sqlite3
54
+ database: db/development.sqlite3
55
+ pool: 5
56
+ timeout: 5000
57
+ users:
58
+ adapter: ldap
59
+ host: localhost
60
+ port: 389
61
+ base: dc=example,dc=com
62
+ bind_name: cn=admin,dc=example,dc=com
63
+ password: secret
64
+
65
+
66
+ === restful adapter
67
+
68
+ this comes partly from dm-ldap-adapter and partly from dm-more/adapter/rest_adapter. the restful adapter allows to Create, Retrieve, Update and Delete resources via a restful service.
69
+
70
+ == REQUIREMENTS:
71
+
72
+ * datamapper
73
+
74
+ == INSTALL:
75
+
76
+ * sudo gem install datamapper4rails
77
+
78
+ == LICENSE:
79
+
80
+ (The MIT License)
81
+
82
+ Copyright (c) 2009 Kristian Meier
83
+
84
+ Permission is hereby granted, free of charge, to any person obtaining
85
+ a copy of this software and associated documentation files (the
86
+ 'Software'), to deal in the Software without restriction, including
87
+ without limitation the rights to use, copy, modify, merge, publish,
88
+ distribute, sublicense, and/or sell copies of the Software, and to
89
+ permit persons to whom the Software is furnished to do so, subject to
90
+ the following conditions:
91
+
92
+ The above copyright notice and this permission notice shall be
93
+ included in all copies or substantial portions of the Software.
94
+
95
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
96
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
97
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
98
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
99
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
100
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
101
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,32 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/datamapper4rails/version.rb'
6
+
7
+ Hoe.new('datamapper4rail', Datamapper4rails::VERSION) do |p|
8
+ p.rubyforge_name = 'datamapper4rail'
9
+ p.developer('mkristian', 'm.kristian@web.de')
10
+ p.extra_deps = ['slf4r']
11
+ p.remote_rdoc_dir = '' # Release to root
12
+ end
13
+
14
+ desc 'Install the package as a gem.'
15
+ task :install => [:clean, :package] do
16
+ gem = Dir['pkg/*.gem'].first
17
+ sh "gem install --local #{gem} --no-ri --no-rdoc"
18
+ end
19
+
20
+ # desc 'Run specifications'
21
+ # Spec::Rake::SpecTask.new(:spec) do |t|
22
+ # if File.exists?('spec/spec.opts')
23
+ # t.spec_opts << '--options' << 'spec/spec.opts'
24
+ # end
25
+ # t.spec_files = Pathname.glob('./spec/**/*_spec.rb')
26
+ # end
27
+
28
+ # require 'yard'
29
+
30
+ # YARD::Rake::YardocTask.new
31
+
32
+ # vim: syntax=Ruby
@@ -0,0 +1,12 @@
1
+ require 'rails_generator/base'
2
+
3
+ class DmInstallGenerator < Rails::Generator::Base
4
+
5
+ def manifest
6
+ record do |m|
7
+ m.directory "lib/tasks"
8
+ m.template "datamapper.rake", "lib/tasks/datamapper.rake"
9
+ end
10
+ end
11
+
12
+ end
@@ -0,0 +1,42 @@
1
+ namespace :db do
2
+
3
+ desc "Perform automigration"
4
+ task :automigrate => :environment do
5
+ FileList["app/models/**/*.rb"].each do |model|
6
+ load model
7
+ end
8
+ ::DataMapper.auto_migrate!
9
+ end
10
+
11
+ desc "Perform non destructive automigration"
12
+ task :autoupgrade => :environment do
13
+ FileList["app/models/**/*.rb"].each do |model|
14
+ load model
15
+ end
16
+ ::DataMapper.auto_upgrade!
17
+ end
18
+
19
+ namespace :migrate do
20
+ task :load => :environment do
21
+ gem 'dm-migrations'
22
+ FileList["db/migrations/*.rb"].each do |migration|
23
+ load migration
24
+ end
25
+ end
26
+
27
+ desc "Migrate up using migrations"
28
+ task :up, :version, :needs => :load do |t, args|
29
+ version = args[:version]
30
+ migrate_up!(version)
31
+ end
32
+
33
+ desc "Migrate down using migrations"
34
+ task :down, :version, :needs => :load do |t, args|
35
+ version = args[:version]
36
+ migrate_down!(version)
37
+ end
38
+ end
39
+
40
+ desc "Migrate the database to the latest version"
41
+ task :migrate => 'db:migrate:up'
42
+ end
@@ -0,0 +1,10 @@
1
+ require 'rails_generator/generators/components/migration/migration_generator'
2
+
3
+ class DmMigrationGenerator < Rails::Generator::NamedBase
4
+
5
+ def manifest
6
+ record do |m|
7
+ m.migration_template "migration.rb", "db/migrate"
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ class <%= migration_name %> < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :<%= table_name %> do |t|
4
+ <% for attribute in attributes -%>
5
+ t.<%= attribute.type %> :<%= attribute.name %>
6
+ <% end -%>
7
+ <% unless options[:skip_timestamps] %>
8
+ t.timestamps
9
+ <% end -%>
10
+ end
11
+ end
12
+
13
+ def self.down
14
+ drop_table :<%= table_name %>
15
+ end
16
+ end
@@ -0,0 +1,27 @@
1
+ require 'rails_generator/generators/components/model/model_generator'
2
+ require 'active_record'
3
+
4
+ class DmModelGenerator <ModelGenerator
5
+
6
+ def manifest
7
+ record do |m|
8
+ # Check for class naming collisions.
9
+ m.class_collisions class_path, class_name, "#{class_name}Test"
10
+
11
+ # Model, test, and fixture directories.
12
+ m.directory File.join('app/models', class_path)
13
+ m.directory File.join('test/unit', class_path)
14
+
15
+ # Model class, unit test, and fixtures.
16
+ m.template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb")
17
+ m.template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb")
18
+
19
+ unless options[:skip_migration]
20
+ m.migration_template 'model:migration.rb', 'db/migrate', :assigns => {
21
+ :migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}"
22
+ }, :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}"
23
+ end
24
+ end
25
+ end
26
+
27
+ end
@@ -0,0 +1,14 @@
1
+ class <%= class_name %>
2
+ include DataMapper::Resource
3
+ property :id, Serial
4
+
5
+ <% for attribute in attributes -%>
6
+ property :<%= attribute.name %>, <%= attribute.type.to_s.capitalize %>, :nullable => false
7
+
8
+ <% end -%>
9
+ <% unless options[:skip_timestamps] %>
10
+ property :created_at, DateTime, :nullable => false
11
+ property :updated_at, DateTime, :nullable => false
12
+
13
+ <% end -%>
14
+ end
@@ -0,0 +1,8 @@
1
+ require 'test_helper'
2
+
3
+ class <%= class_name %>Test < ActiveSupport::TestCase
4
+ # Replace this with your real tests.
5
+ def test_truth
6
+ assert true
7
+ end
8
+ end
@@ -0,0 +1,20 @@
1
+ module Rails
2
+ module Generator
3
+ class GeneratedAttribute
4
+ def default_value
5
+ @default_value ||= case type
6
+ when :int, :integer then "1"
7
+ when :float then "1.5"
8
+ when :decimal, :big_decimal then "9.99"
9
+ when :date_time, :datetime,
10
+ :timestamp, :time then "Time.now"
11
+ when :date then "Date.today"
12
+ when :string, :text then "\"value for #{@name}\""
13
+ when :boolean then "false"
14
+ else
15
+ ""
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ require 'rails_generator/generators/components/model/model_generator'
2
+ require 'active_record'
3
+ require File.dirname(__FILE__) + '/../rspec_default_values'
4
+
5
+ class RspecDmModelExtendedGenerator <ModelGenerator
6
+
7
+ def manifest
8
+ record do |m|
9
+
10
+ # Check for class naming collisions.
11
+ m.class_collisions class_path, class_name
12
+
13
+ # Model, spec, and fixture directories.
14
+ m.directory File.join('app/models', class_path)
15
+ m.directory File.join('spec/models', class_path)
16
+
17
+ # Model class, spec and fixtures.
18
+ m.template 'model.rb', File.join('app/models',
19
+ class_path,
20
+ "#{file_name}.rb")
21
+ m.template 'model_spec.rb', File.join('spec/models',
22
+ class_path,
23
+ "#{file_name}_spec.rb")
24
+ end
25
+
26
+ # unless options[:skip_migration]
27
+ # m.migration_template 'model:migration.rb', 'db/migrate', :assigns => {
28
+ # :migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}"
29
+ # }, :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}"
30
+ # end
31
+ end
32
+
33
+ end
@@ -0,0 +1,16 @@
1
+ class <%= class_name %>
2
+ include DataMapper::Resource
3
+ property :id, Serial
4
+
5
+ <% for attribute in attributes -%>
6
+ property :<%= attribute.name %>, <%= attribute.type.to_s.camelize %>, :nullable => false <% if attribute.type == :string or attribute.type == :text -%>, :format => /^[^<'&">]*$/<% if attribute.type == :string %>, :length => 50<% end -%>
7
+ <% else -%>
8
+
9
+ <% end -%>
10
+ <% end -%>
11
+ <% unless options[:skip_timestamps] %>
12
+ property :created_at, DateTime, :nullable => false
13
+ property :updated_at, DateTime, :nullable => false
14
+
15
+ <% end -%>
16
+ end
@@ -0,0 +1,41 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '<%= '/..' * class_nesting_depth %>/../spec_helper')
2
+
3
+ describe <%= class_name %> do
4
+ before(:each) do
5
+ @valid_attributes = {
6
+ <% attributes.each_with_index do |attribute, attribute_index| -%>
7
+ :<%= attribute.name %> => <%= attribute.default_value %><%= attribute_index == attributes.length - 1 ? '' : ','%>
8
+ <% end -%>
9
+ }
10
+ end
11
+
12
+ <% attributes.each do |attribute| -%>
13
+ it "should require <%= attribute.name %>" do
14
+ <%= singular_name %> = <%= class_name %>.create(@valid_attributes.merge(:<%= attribute.name %> => nil))
15
+ <%= singular_name %>.errors.on(:<%= attribute.name %>).should_not == nil
16
+ end
17
+
18
+ <% if attribute.type == :string or attribute.type == :text -%>
19
+ it 'should not match <%= attribute.name %>' do
20
+ <%= singular_name %> = <%= class_name %>.create(@valid_attributes.merge(:<%= attribute.name %> => "<script" ))
21
+ <%= singular_name %>.errors.on(:<%= attribute.name %>).should_not == nil
22
+ <%= singular_name %> = <%= class_name %>.create(@valid_attributes.merge(:<%= attribute.name %> => "sc'ript" ))
23
+ <%= singular_name %>.errors.on(:<%= attribute.name %>).should_not == nil
24
+ <%= singular_name %> = <%= class_name %>.create(@valid_attributes.merge(:<%= attribute.name %> => "scr&ipt" ))
25
+ <%= singular_name %>.errors.on(:<%= attribute.name %>).should_not == nil
26
+ <%= singular_name %> = <%= class_name %>.create(@valid_attributes.merge(:<%= attribute.name %> => 'scr"ipt' ))
27
+ <%= singular_name %>.errors.on(:<%= attribute.name %>).should_not == nil
28
+ <%= singular_name %> = <%= class_name %>.create(@valid_attributes.merge(:<%= attribute.name %> => "script>" ))
29
+ <%= singular_name %>.errors.on(:<%= attribute.name %>).should_not == nil
30
+ end
31
+
32
+ <% elsif [:integer, :big_decimal, :float].member? attribute.type %>
33
+ it "should be numerical <%= attribute.name %>" do
34
+ <%= singular_name %> = <%= class_name %>.create(@valid_attributes.merge(:<%= attribute.name %> => "none-numberic" ))
35
+ <%= singular_name %>.<%= attribute.name %>.to_i.should == 0
36
+ <%= singular_name %>.errors.size.should == 0
37
+ end
38
+
39
+ <% end -%>
40
+ <% end -%>
41
+ end
@@ -0,0 +1,53 @@
1
+ require 'dm-core'
2
+ def config_file()
3
+ RAILS_ROOT + "/config/database.yml"
4
+ end
5
+
6
+ def create_connection()
7
+ conf = config.dup
8
+ if repositories = conf.delete(:repositories)
9
+ repositories.each do |repo, conf|
10
+ ::DataMapper.setup(repo, conf) unless conf.empty?
11
+ end
12
+ else
13
+ ::DataMapper.setup(:default, conf) unless conf.empty?
14
+ end
15
+ end
16
+
17
+ def get_config_for_environment
18
+ if hash = full_config[RAILS_ENV]
19
+ symbolize_keys(hash)
20
+ elsif hash = full_config[RAILS_ENV.to_sym]
21
+ hash
22
+ else
23
+ raise ArgumentError, "missing environment '#{RAILS_ENV}' in config file #{config_file}"
24
+ end
25
+ end
26
+
27
+ def full_config
28
+ @full_config ||= YAML::load(ERB.new(IO.read(config_file)).result)
29
+ end
30
+
31
+ def config
32
+ @config ||= get_config_for_environment
33
+ end
34
+
35
+ def symbolize_keys(h)
36
+ config = {}
37
+
38
+ h.each do |k, v|
39
+ if k == 'port'
40
+ config[k.to_sym] = v.to_i
41
+ elsif k == 'adapter' && v == 'postgresql'
42
+ config[k.to_sym] = 'postgres'
43
+ elsif v.is_a?(Hash)
44
+ config[k.to_sym] = symbolize_keys(v)
45
+ else
46
+ config[k.to_sym] = v
47
+ end
48
+ end
49
+
50
+ config
51
+ end
52
+
53
+ create_connection()
@@ -0,0 +1,198 @@
1
+ require 'dm-core'
2
+ require 'slf4r'
3
+
4
+ module DataMapper
5
+ module Adapters
6
+ class NoopTransaction
7
+
8
+ def close ; end
9
+ def begin ; end
10
+ def prepare ; end
11
+ def commit ; end
12
+ def rollback ; end
13
+ def rollback_prepared ; end
14
+
15
+ end
16
+ class BaseAdapter < AbstractAdapter
17
+
18
+ include Slf4r::Logger
19
+
20
+ # @see AbstractAdapter
21
+ def transaction_primitive
22
+ NoopTransaction.new
23
+ end
24
+
25
+ def initialize(name, uri_or_options)
26
+ super(name, uri_or_options)
27
+ end
28
+
29
+ protected
30
+
31
+ # checks whether a given resource fullfils the conditions
32
+ # @param [DataMapper::Resource] resource
33
+ # @param [Array<Condition>] conditions
34
+ # @return [Boolean]
35
+ # true if the resource are within the conditions otherwise false
36
+ def filter_resource(resource, conditions)
37
+ #puts "condi"
38
+ #p conditions
39
+ # no conditation => no filter
40
+ if conditions.size == 0
41
+ true
42
+ else
43
+ conditions.all? do |tuple|
44
+ operator, property, bind_value = *tuple
45
+
46
+ value = property.get!(resource)
47
+ case operator
48
+ when :eql, :in then equality_comparison(bind_value, value)
49
+ when :not then !equality_comparison(bind_value, value)
50
+ when :like then Regexp.new(bind_value.gsub(/%/, ".*")) =~ value
51
+ when :gt then !value.nil? && value > bind_value
52
+ when :gte then !value.nil? && value >= bind_value
53
+ when :lt then !value.nil? && value < bind_value
54
+ when :lte then !value.nil? && value <= bind_value
55
+ else raise "Invalid query operator: #{operator.inspect}"
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ # helper method to dispatch the equality test for different
62
+ # classes
63
+ def equality_comparison(bind_value, value)
64
+ case bind_value
65
+ when Array, Range then bind_value.include?(value)
66
+ when NilClass then value.nil?
67
+ else bind_value == value
68
+ end
69
+ end
70
+
71
+ public
72
+
73
+ # @see AbstractAdapter
74
+ # @param [Array<DataMapper::Resources>] resources
75
+ # aaaa
76
+ # @return [Fixnum]
77
+ # number of the newly created resources
78
+ def create(resources)
79
+ resources.select do |resource|
80
+
81
+ create_resource(resource)
82
+
83
+ end.size # just return the number of create resources
84
+ end
85
+
86
+ # @see AbstractAdapter
87
+ # @param [Hash] attributes
88
+ # collection of attribute, i.e. the name/value pairs which
89
+ # needs to be updated
90
+ # @param [Query]
91
+ # on all resources which are selected by that query the
92
+ # update will be applied
93
+ # @return [Fixnum]
94
+ # number of the updated resources
95
+ def update(attributes, query)
96
+ read_many(query).select do |resource|
97
+
98
+ update_resource(resource, attributes)
99
+
100
+ end.size
101
+ end
102
+
103
+ # @see AbstractAdapter
104
+ # @param [DataMapper::Query] query
105
+ # which selects the resource
106
+ # @return [DataMapper::Resource]
107
+ # the found Resource or nil
108
+ def read_one(query)
109
+ result = read_resource(query)
110
+ if result.is_a? Resource
111
+ result
112
+ elsif result # assume result to be Array with the values
113
+ #puts "------------------"
114
+ #p result
115
+ query.model.load(result, query)
116
+ end
117
+ end
118
+
119
+ # @see AbstractAdapter
120
+ # @param [DataMapper::Query] query
121
+ # which selects the resources
122
+ # @return [DataMapper::Collection]
123
+ # collection of Resources
124
+ def read_many(query)
125
+ Collection.new(query) do |set|
126
+ result = read_resources(query)
127
+ #puts "read_many"
128
+ #p result
129
+ if result.size > 0 and result.first.is_a? Resource
130
+ set.replace(result)
131
+ else
132
+ result.each do |values|
133
+ set.load(values)
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+ # @see AbstractAdapter
140
+ # @param [Query] query
141
+ # which selects the resources to be deleted
142
+ # @return [Fixnum]
143
+ # number of the deleted resources
144
+ def delete(query)
145
+ read_many(query).each do |resource|
146
+
147
+ delete_resource(resource)
148
+
149
+ end.size
150
+ end
151
+
152
+ private
153
+
154
+ # @param [DataMapper::Resource] resource
155
+ # which will be created
156
+ # @return [DataMapper::Resource]
157
+ # either the resource itself if the creation was successful or nil
158
+ def create_resource(resource)
159
+ raise NotImplementedError.new
160
+ end
161
+
162
+ # @param [DataMapper::Query] query
163
+ # which selects the resource
164
+ # @return [DataMapper::Resource,Array<String>]
165
+ # the resource or a set of values ordered in the same manner as query.fields attributes
166
+ def read_resource(query)
167
+ raise NotImplementedError.new
168
+ end
169
+
170
+ # @param [DataMapper::Query] query
171
+ # which selects the resources
172
+ # @return [Array<DataMapper::Resource>,Array<String>]
173
+ # resources or ordered values
174
+ # @see #read_resource
175
+ def read_resources(query)
176
+ raise NotImplementedError.new
177
+ end
178
+
179
+ # @param [DataMapper::Resource] resource
180
+ # which will be updated with the given attributes.
181
+ # @param [Hash] attributes
182
+ # the keys are the property names and the values are the new values of that property.
183
+ # @return [DataMapper::Resource]
184
+ # resource on success otherwise nil
185
+ def update_resource(resource, attributes)
186
+ raise NotImplementedError.new
187
+ end
188
+
189
+ # @param [DataMapper::Resource] resource
190
+ # which will be deleted
191
+ # @return [DataMapper::Resource]
192
+ # either the resource if the deletion was successful or nil
193
+ def delete_resource(resource)
194
+ raise NotImplementedError.new
195
+ end
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,252 @@
1
+ require 'adapters/restful_adapter'
2
+ require 'net/http'
3
+ require 'extlib/inflection'
4
+ require 'extlib/module'
5
+
6
+ module DataMapper
7
+ module Adapters
8
+ class RestfulAdapter < BaseAdapter
9
+
10
+ include ::Slf4r::Logger
11
+
12
+ def resource_name_from_model(model)
13
+ ::Extlib::Inflection.underscore(model.name)
14
+ end
15
+
16
+ def resource_name_from_query(query)
17
+ resource_name_from_model(query.model)
18
+ end
19
+
20
+ def keys_from_query(query)
21
+ keys = query.model.key
22
+ # work around strange missing of properties in model
23
+ # but the query has still the fields :P
24
+ if keys.size == 0
25
+ query.fields.select do |f|
26
+ f.key?
27
+ end
28
+ else
29
+ keys
30
+ end
31
+ end
32
+
33
+ def key_value_from_query(query)
34
+ keys = keys_from_query(query)
35
+ logger.debug { "keys=#{keys.inspect}" }
36
+ if keys.size == 1
37
+ key = keys[0]
38
+ # return the third element of the condition array
39
+ # which belongs to the key
40
+ query.conditions.detect do |c|
41
+ c[1] == key
42
+ end[2]
43
+ else
44
+ raise "compound keys are not supported"
45
+ end
46
+ end
47
+
48
+ def http_get(uri)
49
+ send_request do |http|
50
+ request = Net::HTTP::Get.new(uri)
51
+ request.basic_auth(@uri[:login],
52
+ @uri[:password]) unless @uri[:login].blank?
53
+ http.request(request)
54
+ end
55
+ end
56
+
57
+ def http_post(uri, data = nil)
58
+ send_request do |http|
59
+ request = Net::HTTP::Post.new(uri, {
60
+ 'content-type' => 'application/xml',
61
+ 'content-length' => data.length.to_s
62
+ })
63
+ request.basic_auth(@uri[:login],
64
+ @uri[:password]) unless @uri[:login].blank?
65
+ http.request(request, data)
66
+ end
67
+ end
68
+
69
+ def http_put(uri, data = {})
70
+ send_request do |http|
71
+ request = Net::HTTP::Put.new(uri)
72
+ request.basic_auth(@uri[:login],
73
+ @uri[:password]) unless @uri[:login].blank?
74
+ request.set_form_data(data)
75
+ http.request(request)
76
+ end
77
+ end
78
+
79
+ def http_delete(uri)
80
+ send_request do |http|
81
+ request = Net::HTTP::Delete.new(uri)
82
+ request.basic_auth(@uri[:login],
83
+ @uri[:password]) unless @uri[:login].blank?
84
+ http.request(request)
85
+ end
86
+ end
87
+
88
+ def send_request(&block)
89
+ res = nil
90
+ Net::HTTP.start(@uri[:host], @uri[:port].to_i) do |http|
91
+ res = yield(http)
92
+ end
93
+ logger.debug { "response=" + res.code }
94
+ res
95
+ end
96
+
97
+ def parse_resource(xml, model, query = nil)
98
+ elements = {}
99
+ associations = {}
100
+ many_to_many = {}
101
+ xml.elements.collect do |element|
102
+ if element.text.nil?
103
+ if element.attributes['type'] == 'array'
104
+ many_to_many[element.name.gsub('-','_').to_sym] = element
105
+ else
106
+ associations[element.name.gsub('-','_').to_sym] = element
107
+ end
108
+ else
109
+ elements[element.name.gsub('-','_').to_sym] = element.text
110
+ end
111
+ end
112
+ #puts
113
+ #puts "elements"
114
+ #p elements
115
+ resource = model.load(model.properties.collect do |f|
116
+ elements[f.name]
117
+ end, query)
118
+ resource.send("#{keys_from_query(query)[0].name}=".to_sym, elements[keys_from_query(query)[0].name] )
119
+ #p resource
120
+ associations.each do |name, association|
121
+ model =
122
+ if rel = model.relationships[name]
123
+ if rel.child_model == model
124
+ rel.parent_model
125
+ else
126
+ rel.child_model
127
+ end
128
+ # else
129
+ #::Extlib::Inflection.constantize(::Extlib::Inflection.classify(name))
130
+ # model.find_const(::Extlib::Inflection.classify(name))
131
+ end
132
+ if resource.respond_to? "#{name}=".to_sym
133
+ resource.send("#{name}=".to_sym,
134
+ parse_resource(association, model,
135
+ ::DataMapper::Query.new(query.repository, model )))
136
+ else
137
+ resource.send(("#{name.to_s.pluralize}<" + "<").to_sym,
138
+ parse_resource(association, model,
139
+ ::DataMapper::Query.new(query.repository, model )))
140
+ end
141
+ end
142
+ resource.instance_variable_set(:@new_record, false)
143
+ resource
144
+ end
145
+
146
+ # @see BaseAdapter
147
+ def create_resource(resource)
148
+ name = resource.model.name
149
+ uri = "/#{name.pluralize}.xml"
150
+ logger.debug { "post #{uri}" }
151
+ response = http_post(uri, resource.to_xml )
152
+ resource_new = parse_resource(REXML::Document::new(response.body).root,
153
+ resource.model,
154
+ ::DataMapper::Query.new(resource.repository,
155
+ resource.model ))
156
+
157
+ # copy all attributes/associations from the downloaded resource
158
+ # to the given resource
159
+ # TODO better pass the given resource into parse_resource
160
+ resource_new.attributes.each do |key, value|
161
+ resource.send(:properties)[key].set!(resource, value)
162
+ end
163
+ resource_new.send(:relationships).each do |key, value|
164
+ resource.send("#{key}=".to_sym, resource_new.send(key))
165
+ end
166
+ resource
167
+ end
168
+
169
+ # @see BaseAdapter
170
+ def read_resource(query)
171
+ if(query.conditions.empty?)
172
+ raise "not implemented"
173
+ else
174
+ key = key_value_from_query(query)
175
+ uri = "/#{resource_name_from_query(query).pluralize}/#{key}.xml"
176
+ logger.debug { "get #{uri}" }
177
+ response = http_get(uri)
178
+ if response.kind_of?(Net::HTTPSuccess)
179
+ parse_resource(REXML::Document::new(response.body).root,
180
+ query.model,
181
+ query)
182
+ else
183
+ #TODO may act on different response codes differently
184
+ end
185
+ end
186
+ end
187
+
188
+ # @see BaseAdapter
189
+ def read_resources(query)
190
+ # raise "not implemented"
191
+ [read_resource(query)]
192
+ end
193
+
194
+ # @overwrite BaseAdapter
195
+ def update(attributes, query)
196
+ name = resource_name_from_query(query)
197
+ params = {}
198
+ attributes.each do |attr, val|
199
+ params["#{name}[#{attr.name}]"]=val
200
+ end
201
+ key = key_value_from_query(query)
202
+ uri = "/#{name.pluralize}/#{key}.xml"
203
+ logger.debug { "put #{uri}" }
204
+ response = http_put(uri, params)
205
+ response.kind_of?(Net::HTTPSuccess)
206
+ end
207
+
208
+ # @see BaseAdapter
209
+ def update_resource(resource, attributes)
210
+ query = resource.to_query
211
+ if(query.conditions.empty?)
212
+ raise "not implemented"
213
+ else
214
+ name = resource.name
215
+ params = {}
216
+ attributes.each do |attr, val|
217
+ params["#{name}[#{attr.name}]"]=val
218
+ end
219
+ key = key_value_from_query(query)
220
+ logger.debug {resource.to_xml}
221
+ response = http_put("/#{resource_name_from_query(query).pluralize}/#{key}.xml", params)
222
+ response.kind_of?(Net::HTTPSuccess)
223
+ end
224
+ end
225
+
226
+ # @overwrite BaseAdapter
227
+ def delete(query)
228
+ # TODO limit == 1 is NOT sufficient ONLY necessary
229
+ if query.limit == 1
230
+ name = resource_name_from_query(query)
231
+ key = key_value_from_query(query)
232
+ uri = "/#{name.pluralize}/#{key}.xml"
233
+ logger.debug { "delete #{uri}" }
234
+ response = http_delete(uri)
235
+ response.kind_of?(Net::HTTPSuccess)
236
+ else
237
+ super
238
+ end
239
+ end
240
+
241
+ # @see BaseAdapter
242
+ def delete_resource(resource)
243
+ name = resource.name
244
+ key = key_value_from_query(resource.to_queryquery)
245
+ uri = "/#{name.pluralize}/#{key}.xml"
246
+ logger.debug { "delete #{uri}" }
247
+ response = http_delete(uri)
248
+ response.kind_of?(Net::HTTPSuccess)
249
+ end
250
+ end
251
+ end
252
+ end
@@ -0,0 +1,49 @@
1
+ require 'dm-core'
2
+
3
+ def config_file()
4
+ RAILS_ROOT + "/config/database.yml"
5
+ end
6
+
7
+ def create_connection()
8
+ conf = config.dup
9
+ repositories = conf.delete(:repositories)
10
+ ::DataMapper.setup(:default, conf) unless conf.empty?
11
+ end
12
+
13
+ def get_config_for_environment
14
+ if hash = full_config[RAILS_ENV]
15
+ symbolize_keys(hash)
16
+ elsif hash = full_config[RAILS_ENV.to_sym]
17
+ hash
18
+ else
19
+ raise ArgumentError, "missing environment '#{RAILS_ENV}' in config file #{config_file}"
20
+ end
21
+ end
22
+
23
+ def full_config
24
+ @full_config ||= YAML::load(ERB.new(IO.read(config_file)).result)
25
+ end
26
+
27
+ def config
28
+ @config ||= get_config_for_environment
29
+ end
30
+
31
+ def symbolize_keys(h)
32
+ config = {}
33
+
34
+ h.each do |k, v|
35
+ if k == 'port'
36
+ config[k.to_sym] = v.to_i
37
+ elsif k == 'adapter' && v == 'postgresql'
38
+ config[k.to_sym] = 'postgres'
39
+ elsif v.is_a?(Hash)
40
+ config[k.to_sym] = symbolize_keys(v)
41
+ else
42
+ config[k.to_sym] = v
43
+ end
44
+ end
45
+
46
+ config
47
+ end
48
+
49
+ create_connection()
@@ -0,0 +1,67 @@
1
+ require 'dm-core'
2
+
3
+ module ActionController
4
+ module Session
5
+ class DatamapperStore < AbstractStore
6
+
7
+ def initialize(app, options = {})
8
+ super
9
+ if options.delete(:cache)
10
+ @@cache = {}
11
+ end
12
+ @@session_class = ::DatamapperStore::Session
13
+ end
14
+
15
+ private
16
+ def get_session(env, sid)
17
+ sid ||= generate_sid
18
+ session =
19
+ if @@cache
20
+ @@cache[sid] || @@session_class.get(sid)
21
+ else
22
+ @@session_class.get(sid)
23
+ end
24
+ [sid, session.nil? ? {} : session.data]
25
+ end
26
+
27
+ def set_session(env, sid, session_data)
28
+ session =
29
+ if @@cache
30
+ @@cache[sid] || @@session_class.get(sid)
31
+ else
32
+ @@session_class.get(sid)
33
+ end || @@session_class.new(:session_id => sid)
34
+ session.data = session_data || {}
35
+ if session.new_record?
36
+ session.updated_at = Time.now
37
+ @@cache[sid] = session if @@cache
38
+ end
39
+ session.save
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ module DatamapperStore
46
+ class Session
47
+
48
+ include ::DataMapper::Resource
49
+
50
+ def self.name
51
+ "session"
52
+ end
53
+
54
+ property :session_id, String, :key => true
55
+
56
+ property :data, Text, :nullable => false, :default => ::Base64.encode64(Marshal.dump({}))
57
+
58
+ property :updated_at, DateTime, :nullable => true, :index => true
59
+
60
+ def data=(data)
61
+ attribute_set(:data, ::Base64.encode64(Marshal.dump(data)))
62
+ end
63
+ def data
64
+ Marshal.load(::Base64.decode64(attribute_get(:data)))
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,42 @@
1
+ module Datamapper4rails
2
+ module RestfulTransactions
3
+
4
+ class Rollback < StandardError
5
+ end
6
+
7
+ module Base
8
+ def self.included(base)
9
+ base.prepend_around_filter(TransactionFilter)
10
+ end
11
+ end
12
+
13
+ class TransactionFilter
14
+ def self.filter(controller)
15
+ case controller.request.method
16
+ when :post, :put, :delete then
17
+ begin
18
+ DataMapper::Transaction.new(DataMapper.repository(:default)) do |*block_args|
19
+ if block_given?
20
+ yield (*block_args)
21
+ # added rollback for all actions which just render
22
+ # a page with validation errors and do not redirect to new idem potent
23
+ # page (http-method get is idem potent within the
24
+ # restful paradigma
25
+ unless controller.response.redirected_to
26
+ raise Datamapper4rails::RestfulTransactions::Rollback
27
+ end
28
+ end
29
+ end
30
+ rescue Datamapper4rails::RestfulTransactions::Rollback
31
+ # ignore,
32
+ # this is just needed to trigger the rollback on the transaction
33
+ end
34
+ else
35
+ yield if block_given?
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ ::ActionController::Base.send(:include, Datamapper4rails::RestfulTransactions::Base)
@@ -0,0 +1,3 @@
1
+ class Datamapper4rails
2
+ VERSION = '0.1.0'
3
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: datamapper4rail
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - mkristian
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-21 00:00:00 +05:30
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: slf4r
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: 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: collection of datamapper related extensions. mostly needed to run within rails. the restful transactions is around filter for rails. the restful adapter can be outside of rails. datamapper store is a session store for rails which uses datamapper as persistent layer. the generators produces datamapper models for your rails application. quite a few things are "stolen" from dm-more/rails_datamapper. a lot of things do not work there and patches are still in process to be applied so until dm-more/rails_datamapper catches up, ut I hope these two project merge someday again.
36
+ email:
37
+ - m.kristian@web.de
38
+ executables: []
39
+
40
+ extensions: []
41
+
42
+ extra_rdoc_files:
43
+ - History.txt
44
+ - Manifest.txt
45
+ - README.txt
46
+ files:
47
+ - History.txt
48
+ - Manifest.txt
49
+ - README.txt
50
+ - Rakefile
51
+ - generators/dm_install/dm_install_generator.rb
52
+ - generators/dm_install/templates/datamapper.rake
53
+ - generators/dm_migration/dm_migration_generator.rb
54
+ - generators/dm_migration/templates/migration.rb
55
+ - generators/dm_model/dm_model_generator.rb
56
+ - generators/dm_model/templates/model.rb
57
+ - generators/dm_model/templates/unit_test.rb
58
+ - generators/rspec_default_values.rb
59
+ - generators/rspec_dm_model/rspec_dm_model_generator.rb
60
+ - generators/rspec_dm_model/templates/model.rb
61
+ - generators/rspec_dm_model/templates/model_spec.rb
62
+ - lib/datamapper4rails.rb
63
+ - lib/datamapper4rails/adapters/base_adapter.rb
64
+ - lib/datamapper4rails/adapters/restful_adapter.rb
65
+ - lib/datamapper4rails/database_config.rb
66
+ - lib/datamapper4rails/datamapper_store.rb
67
+ - lib/datamapper4rails/restful_transactions.rb
68
+ - lib/datamapper4rails/version.rb
69
+ has_rdoc: true
70
+ homepage: http://datamapper4rail.rubyforge.org
71
+ post_install_message:
72
+ rdoc_options:
73
+ - --main
74
+ - README.txt
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: "0"
82
+ version:
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: "0"
88
+ version:
89
+ requirements: []
90
+
91
+ rubyforge_project: datamapper4rail
92
+ rubygems_version: 1.3.1
93
+ signing_key:
94
+ specification_version: 2
95
+ summary: collection of datamapper related extensions
96
+ test_files: []
97
+