ardm-rails 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gitignore +35 -0
  4. data/.travis.yml +11 -0
  5. data/Gemfile +47 -0
  6. data/LICENSE +20 -0
  7. data/README.rdoc +595 -0
  8. data/Rakefile +4 -0
  9. data/VERSION +1 -0
  10. data/ardm-rails.gemspec +27 -0
  11. data/lib/ardm-rails.rb +1 -0
  12. data/lib/dm-rails/configuration.rb +76 -0
  13. data/lib/dm-rails/mass_assignment_security.rb +89 -0
  14. data/lib/dm-rails/middleware/identity_map.rb +20 -0
  15. data/lib/dm-rails/multiparameter_attributes.rb +167 -0
  16. data/lib/dm-rails/railtie.rb +100 -0
  17. data/lib/dm-rails/railties/controller_runtime.rb +45 -0
  18. data/lib/dm-rails/railties/database.rake +106 -0
  19. data/lib/dm-rails/railties/i18n_support.rb +12 -0
  20. data/lib/dm-rails/railties/log_listener.rb +39 -0
  21. data/lib/dm-rails/railties/log_subscriber.rb +54 -0
  22. data/lib/dm-rails/session_store.rb +70 -0
  23. data/lib/dm-rails/setup.rb +84 -0
  24. data/lib/dm-rails/storage.rb +209 -0
  25. data/lib/dm-rails/version.rb +5 -0
  26. data/lib/dm-rails.rb +1 -0
  27. data/lib/generators/data_mapper/migration/migration_generator.rb +30 -0
  28. data/lib/generators/data_mapper/migration/templates/migration.rb +23 -0
  29. data/lib/generators/data_mapper/model/model_generator.rb +23 -0
  30. data/lib/generators/data_mapper/model/templates/model.rb +11 -0
  31. data/lib/generators/data_mapper/observer/observer_generator.rb +19 -0
  32. data/lib/generators/data_mapper/observer/templates/observer.rb +7 -0
  33. data/lib/generators/data_mapper.rb +82 -0
  34. data/spec/models/fake.rb +31 -0
  35. data/spec/models/topic.rb +11 -0
  36. data/spec/spec.opts +3 -0
  37. data/spec/spec_helper.rb +19 -0
  38. data/spec/unit/mass_assignment_security_spec.rb +43 -0
  39. data/spec/unit/multiparameter_attributes_spec.rb +168 -0
  40. data/tasks/clean.rake +6 -0
  41. data/tasks/spec.rake +17 -0
  42. data/tasks/yard.rake +9 -0
  43. data/tasks/yardstick.rake +20 -0
  44. metadata +188 -0
@@ -0,0 +1,54 @@
1
+ module DataMapper
2
+ module Railties
3
+
4
+ class LogSubscriber < ActiveSupport::LogSubscriber
5
+
6
+ def self.runtime=(value)
7
+ Thread.current["data_mapper_sql_runtime"] = value
8
+ end
9
+
10
+ def self.runtime
11
+ Thread.current["data_mapper_sql_runtime"] ||= 0
12
+ end
13
+
14
+ def self.reset_runtime
15
+ rt, self.runtime = runtime, 0
16
+ rt
17
+ end
18
+
19
+ def initialize
20
+ super
21
+ @odd_or_even = false
22
+ end
23
+
24
+ def sql(event)
25
+ self.class.runtime += event.duration
26
+ return unless logger.debug?
27
+
28
+ name = '%s (%.3fms)' % [event.payload[:name], event.duration]
29
+ sql = event.payload[:sql].squeeze(' ')
30
+
31
+ if odd?
32
+ name = color(name, CYAN, true)
33
+ sql = color(sql, nil, true)
34
+ else
35
+ name = color(name, MAGENTA, true)
36
+ end
37
+
38
+ debug " #{name} #{sql}"
39
+ end
40
+
41
+ def odd?
42
+ @odd_or_even = !@odd_or_even
43
+ end
44
+
45
+ def logger
46
+ ::DataMapper.logger
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+ end
53
+
54
+ DataMapper::Railties::LogSubscriber.attach_to :data_mapper
@@ -0,0 +1,70 @@
1
+ require 'dm-core'
2
+ require 'active_support/core_ext/class/attribute'
3
+
4
+ # Implements DataMapper-specific session store.
5
+
6
+ module Rails
7
+ module DataMapper
8
+
9
+ class SessionStore < ActionDispatch::Session::AbstractStore
10
+
11
+ class Session
12
+
13
+ include ::DataMapper::Resource
14
+
15
+ property :id, Serial
16
+ property :session_id, String, :required => true, :unique => true
17
+ property :data, Object, :required => true
18
+ property :updated_at, DateTime, :index => true
19
+
20
+ def self.name
21
+ 'session'
22
+ end
23
+
24
+ def data
25
+ attribute_get(:data) || {}
26
+ end
27
+
28
+ end
29
+
30
+ SESSION_RECORD_KEY = 'rack.session.record'.freeze
31
+
32
+ class_attribute :session_class
33
+ self.session_class = Session
34
+
35
+ private
36
+
37
+ def get_session(env, sid)
38
+ sid ||= generate_sid
39
+ session = find_session(sid)
40
+ env[SESSION_RECORD_KEY] = session
41
+ [ sid, session.data ]
42
+ end
43
+
44
+ def set_session(env, sid, session_data)
45
+ session = get_session_resource(env, sid)
46
+ session.data = session_data
47
+ session.updated_at = DateTime.now if session.dirty?
48
+ session.save ? sid : false
49
+ end
50
+
51
+ def get_session_resource(env, sid)
52
+ if env[ENV_SESSION_OPTIONS_KEY][:id].nil?
53
+ env[SESSION_RECORD_KEY] = find_session(sid)
54
+ else
55
+ env[SESSION_RECORD_KEY] ||= find_session(sid)
56
+ end
57
+ end
58
+
59
+ def find_session(sid)
60
+ self.class.session_class.first_or_new(:session_id => sid)
61
+ end
62
+
63
+ def destroy(env)
64
+ find_session(current_session_id(env)).destroy
65
+ end
66
+
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,84 @@
1
+ require 'active_support/core_ext/hash/except'
2
+
3
+ require 'dm-rails/configuration'
4
+ require 'dm-rails/railties/log_listener'
5
+
6
+ module Rails
7
+ module DataMapper
8
+
9
+ def self.setup(environment)
10
+ ::DataMapper.logger.info "[datamapper] Setting up the #{environment.inspect} environment:"
11
+ configuration.repositories[environment].each do |name, config|
12
+ setup_with_instrumentation(name.to_sym, config)
13
+ end
14
+ finalize
15
+ end
16
+
17
+ def self.setup_with_instrumentation(name, options)
18
+
19
+ adapter = if options['uri']
20
+ database_uri = ::Addressable::URI.parse(options['uri'])
21
+ ::DataMapper.logger.info "[datamapper] Setting up #{name.inspect} repository: '#{database_uri.path}' on #{database_uri.scheme}"
22
+ ::DataMapper.setup(name, database_uri)
23
+ else
24
+ ::DataMapper.logger.info "[datamapper] Setting up #{name.inspect} repository: '#{options['database']}' on #{options['adapter']}"
25
+ ::DataMapper.setup(name, options)
26
+ end
27
+
28
+ if convention = configuration.resource_naming_convention[name]
29
+ adapter.resource_naming_convention = convention
30
+ end
31
+ if convention = configuration.field_naming_convention[name]
32
+ adapter.field_naming_convention = convention
33
+ end
34
+ setup_log_listener(adapter.options['adapter'])
35
+ end
36
+
37
+ def self.setup_logger(logger)
38
+ ::DataMapper.logger = logger
39
+ end
40
+
41
+ def self.setup_log_listener(adapter_name)
42
+ adapter_name = 'sqlite3' if adapter_name == 'sqlite'
43
+ driver_name = ActiveSupport::Inflector.camelize(adapter_name)
44
+
45
+ setup_do_driver(driver_name) do |driver|
46
+ DataObjects::Connection.send(:include, LogListener)
47
+ # FIXME Setting DataMapper::Logger.new($stdout, :off) alone won't work because the #log
48
+ # method is currently only available in DO and needs an explicit DO Logger instantiated.
49
+ # We turn the logger :off because ActiveSupport::Notifications handles displaying log messages
50
+ driver.logger = DataObjects::Logger.new($stdout, :off)
51
+ end
52
+ end
53
+
54
+ def self.finalize
55
+ ::DataMapper.finalize
56
+ end
57
+
58
+ def self.preload_models(app)
59
+ app.config.paths['app/models'].each do |path|
60
+ Dir.glob("#{path}/**/*.rb").sort.each { |file| require_dependency file[path.length..-1] }
61
+ end
62
+ finalize
63
+ end
64
+
65
+ class << self
66
+ private
67
+
68
+ if RUBY_VERSION < '1.9'
69
+ def setup_do_driver(driver_name)
70
+ if Object.const_defined?('DataObjects') && DataObjects.const_defined?(driver_name)
71
+ yield DataObjects.const_get(driver_name)
72
+ end
73
+ end
74
+ else
75
+ def setup_do_driver(driver_name)
76
+ if Object.const_defined?('DataObjects', false) && DataObjects.const_defined?(driver_name, false)
77
+ yield DataObjects.const_get(driver_name, false)
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,209 @@
1
+ module Rails
2
+ module DataMapper
3
+
4
+ def self.storage
5
+ Storage
6
+ end
7
+
8
+ class Storage
9
+ attr_reader :name, :config
10
+
11
+ def self.create_all
12
+ with_local_repositories { |config| create_environment(config) }
13
+ end
14
+
15
+ def self.drop_all
16
+ with_local_repositories { |config| drop_environment(config) }
17
+ end
18
+
19
+ def self.create_environment(config)
20
+ config.each { |repo_name, repo_config| new(repo_name, repo_config).create }
21
+ end
22
+
23
+ def self.drop_environment(config)
24
+ config.each { |repo_name, repo_config| new(repo_name, repo_config).drop }
25
+ end
26
+
27
+ def self.new(name, config)
28
+ klass = lookup_class(config['adapter'])
29
+ if klass.equal?(self)
30
+ super(name, config)
31
+ else
32
+ klass.new(name, config)
33
+ end
34
+ end
35
+
36
+ class << self
37
+ private
38
+
39
+ def with_local_repositories
40
+ Rails::DataMapper.configuration.repositories.each_value do |config|
41
+ if config['host'].blank? || %w[ 127.0.0.1 localhost ].include?(config['host'])
42
+ yield(config)
43
+ else
44
+ puts "This task only modifies local databases. #{config['database']} is on a remote host."
45
+ end
46
+ end
47
+ end
48
+
49
+ def lookup_class(adapter)
50
+ klass_name = normalized_adapter_name(adapter).camelize.to_sym
51
+
52
+ unless Storage.const_defined?(klass_name)
53
+ raise "Adapter #{adapter} not supported (#{klass_name.inspect})"
54
+ end
55
+
56
+ const_get(klass_name)
57
+ end
58
+
59
+ def normalized_adapter_name(adapter_name)
60
+ adapter_name.to_s == 'sqlite3' ? 'sqlite' : adapter_name
61
+ end
62
+
63
+ end
64
+
65
+ def initialize(name, config)
66
+ @name, @config = name.to_sym, config
67
+ end
68
+
69
+ def create
70
+ puts create_message if _create
71
+ end
72
+
73
+ def drop
74
+ puts drop_message if _drop
75
+ end
76
+
77
+ def database
78
+ @database ||= config['database'] || config['path']
79
+ end
80
+
81
+ def username
82
+ @username ||= config['username'] || ''
83
+ end
84
+
85
+ def password
86
+ @password ||= config['password'] || ''
87
+ end
88
+
89
+ def charset
90
+ @charset ||= config['charset'] || ENV['CHARSET'] || 'utf8'
91
+ end
92
+
93
+ def create_message
94
+ "[datamapper] Created database '#{database}'"
95
+ end
96
+
97
+ def drop_message
98
+ "[datamapper] Dropped database '#{database}'"
99
+ end
100
+
101
+ # Create the configured database
102
+ #
103
+ # This is a noop so that calling this method
104
+ # won't explode on people who use adapters that
105
+ # don't support creating a storage recepticle
106
+ def _create
107
+ true
108
+ end
109
+
110
+ # Drop the configured database
111
+ #
112
+ # This is a noop so that calling this method
113
+ # won't explode on people who use adapters that
114
+ # don't support dropping a storage recepticle
115
+ def _drop
116
+ true
117
+ end
118
+
119
+ class Sqlite < Storage
120
+
121
+ # This is a noop for sqlite
122
+ #
123
+ # Overwritten solely for documentation purposes
124
+ #
125
+ # Both auto_migrate!/auto_upgrade! will create the actual database
126
+ # if the connection has been setup properly and there actually
127
+ # are statements to execute (i.e. at least one model is declared)
128
+ #
129
+ # DataMapper.setup alone won't create the actual database so there
130
+ # really is no API to simply create an empty database for sqlite3.
131
+ #
132
+ # we return true to indicate success nevertheless
133
+ def _create
134
+ true
135
+ end
136
+
137
+ def _drop
138
+ return if in_memory?
139
+ path.unlink if path.file?
140
+ end
141
+
142
+ def create_message
143
+ "[datamapper] db:create is a noop for sqlite3, use db:automigrate instead (#{database})"
144
+ end
145
+
146
+ private
147
+
148
+ def in_memory?
149
+ database == ':memory:'
150
+ end
151
+
152
+ def path
153
+ @path ||= Pathname(File.expand_path(database, Rails.root))
154
+ end
155
+
156
+ end
157
+
158
+ class Mysql < Storage
159
+ def _create
160
+ execute("CREATE DATABASE `#{database}` DEFAULT CHARACTER SET #{charset} DEFAULT COLLATE #{collation}")
161
+ end
162
+
163
+ def _drop
164
+ execute("DROP DATABASE IF EXISTS `#{database}`")
165
+ end
166
+
167
+ private
168
+
169
+ def execute(statement)
170
+ system(
171
+ 'mysql',
172
+ (username.blank? ? '' : "--user=#{username}"),
173
+ (password.blank? ? '' : "--password=#{password}"),
174
+ '-e',
175
+ statement
176
+ )
177
+ end
178
+
179
+ def collation
180
+ @collation ||= config['collation'] || ENV['COLLATION'] || 'utf8_unicode_ci'
181
+ end
182
+
183
+ end
184
+
185
+ class Postgres < Storage
186
+ def _create
187
+ system(
188
+ 'createdb',
189
+ '-E',
190
+ charset,
191
+ '-U',
192
+ username,
193
+ database
194
+ )
195
+ end
196
+
197
+ def _drop
198
+ system(
199
+ 'dropdb',
200
+ '-U',
201
+ username,
202
+ database
203
+ )
204
+ end
205
+
206
+ end
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,5 @@
1
+ module DataMapper
2
+ module Rails
3
+ VERSION = '1.2.1'
4
+ end
5
+ end
data/lib/dm-rails.rb ADDED
@@ -0,0 +1 @@
1
+ require 'dm-rails/railtie'
@@ -0,0 +1,30 @@
1
+ require 'generators/data_mapper'
2
+
3
+ module DataMapper
4
+ module Generators
5
+
6
+ class MigrationGenerator < Base
7
+
8
+ argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
9
+ class_option :id, :type => :numeric, :desc => "The id to be used in this migration"
10
+
11
+ def create_migration_file
12
+ set_local_assigns!
13
+ migration_template "migration.rb", "db/migrate/#{file_name}.rb"
14
+ end
15
+
16
+ protected
17
+
18
+ attr_reader :migration_action
19
+
20
+ def set_local_assigns!
21
+ if file_name =~ /^(add|remove|drop)_.*_(?:to|from)_(.*)/
22
+ @migration_action = $1 == 'add' ? 'add' : 'drop'
23
+ @table_name = $2.pluralize
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,23 @@
1
+ migration <%= migration_number.to_i %>, :<%= migration_file_name %> do
2
+
3
+ up do
4
+ <% unless attributes.empty? -%>
5
+ modify_table :<%= table_name %> do
6
+ <% attributes.each do |attribute| -%>
7
+ <%= migration_action %>_column :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type_class %><% end -%>
8
+ <% end -%>
9
+ end
10
+ <% end -%>
11
+ end
12
+
13
+ down do
14
+ <% unless attributes.empty? -%>
15
+ modify_table :<%= table_name %> do
16
+ <% attributes.reverse.each do |attribute| -%>
17
+ <%= migration_action == 'add' ? 'drop' : 'add' %>_column :<%= attribute.name %><% if migration_action == 'drop' %>, :<%= attribute.type_class %><% end -%>
18
+ <% end -%>
19
+ end
20
+ <% end -%>
21
+ end
22
+
23
+ end
@@ -0,0 +1,23 @@
1
+ require 'generators/data_mapper'
2
+
3
+ module DataMapper
4
+ module Generators
5
+
6
+ class ModelGenerator < Base
7
+ argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
8
+
9
+ check_class_collision
10
+
11
+ class_option :timestamps, :type => :boolean
12
+ class_option :parent, :type => :string, :desc => "The parent class for the generated model"
13
+
14
+ def create_model_file
15
+ template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb")
16
+ end
17
+
18
+ hook_for :test_framework
19
+
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,11 @@
1
+ class <%= class_name %><%= "< #{options[:parent].classify}" if options[:parent] %>
2
+
3
+ <% unless options[:parent] -%>
4
+ include DataMapper::Resource
5
+
6
+ property :id, Serial
7
+ <% end %>
8
+ <% attributes.each do |attribute| -%>
9
+ property :<%= attribute.name -%>, <%= attribute.type_class %>
10
+ <% end %>
11
+ end
@@ -0,0 +1,19 @@
1
+ require 'generators/data_mapper'
2
+
3
+ module DataMapper
4
+ module Generators
5
+
6
+ class ObserverGenerator < Base
7
+
8
+ check_class_collision :suffix => "Observer"
9
+
10
+ def create_observer_file
11
+ template 'observer.rb', File.join('app/models', class_path, "#{file_name}_observer.rb")
12
+ end
13
+
14
+ hook_for :test_framework
15
+
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ class <%= class_name %>Observer
2
+
3
+ include DataMapper::Observer
4
+
5
+ observe <%= class_name %>
6
+
7
+ end
@@ -0,0 +1,82 @@
1
+ require 'rails/generators/named_base'
2
+ require 'rails/generators/migration'
3
+ require 'rails/generators/active_model'
4
+
5
+ module DataMapper
6
+ module Generators
7
+
8
+ class Base < ::Rails::Generators::NamedBase #:nodoc:
9
+
10
+ include ::Rails::Generators::Migration
11
+
12
+ def self.source_root
13
+ @_datamapper_source_root ||=
14
+ File.expand_path("../#{base_name}/#{generator_name}/templates", __FILE__)
15
+ end
16
+
17
+ protected
18
+
19
+ # Datamapper does not care if migrations have the same name as long as
20
+ # they have different ids.
21
+ #
22
+ def migration_exists?(dirname, file_name) #:nodoc:
23
+ false
24
+ end
25
+
26
+ # Implement the required interface for Rails::Generators::Migration.
27
+ #
28
+ def self.next_migration_number(dirname) #:nodoc:
29
+ "%.3d" % (current_migration_number(dirname) + 1)
30
+ end
31
+
32
+ end
33
+
34
+ class ActiveModel < ::Rails::Generators::ActiveModel #:nodoc:
35
+ def self.all(klass)
36
+ "#{klass}.all"
37
+ end
38
+
39
+ def self.find(klass, params=nil)
40
+ "#{klass}.get(#{params})"
41
+ end
42
+
43
+ def self.build(klass, params=nil)
44
+ if params
45
+ "#{klass}.new(#{params})"
46
+ else
47
+ "#{klass}.new"
48
+ end
49
+ end
50
+
51
+ def save
52
+ "#{name}.save"
53
+ end
54
+
55
+ def update_attributes(params=nil)
56
+ "#{name}.update(#{params})"
57
+ end
58
+
59
+ def errors
60
+ "#{name}.errors"
61
+ end
62
+
63
+ def destroy
64
+ "#{name}.destroy"
65
+ end
66
+ end
67
+
68
+ end
69
+ end
70
+
71
+ module Rails
72
+
73
+ module Generators
74
+ class GeneratedAttribute #:nodoc:
75
+ def type_class
76
+ return 'DateTime' if type.to_s == 'datetime'
77
+ return type.to_s.camelcase
78
+ end
79
+ end
80
+ end
81
+
82
+ end
@@ -0,0 +1,31 @@
1
+ module Rails; module DataMapper; module Models
2
+ class Composite
3
+ attr_accessor :args
4
+
5
+ def initialize(*args)
6
+ @args = args
7
+ end
8
+
9
+ def ==(other)
10
+ eql?(other)
11
+ end
12
+
13
+ def eql?(other)
14
+ !other.nil? && other.args == args
15
+ end
16
+ end
17
+
18
+ class Fake
19
+ super_module = Module.new do
20
+ def _super_attributes=(*args)
21
+ end
22
+
23
+ def attributes=(*args)
24
+ self.send(:_super_attributes=, *args)
25
+ end
26
+ end
27
+ include super_module
28
+
29
+ include ::Rails::DataMapper::MultiparameterAttributes
30
+ end
31
+ end; end; end
@@ -0,0 +1,11 @@
1
+ module Rails; module DataMapper; module Models
2
+ class Topic
3
+ include ::DataMapper::Resource
4
+ include ::Rails::DataMapper::MultiparameterAttributes
5
+
6
+ property :id, Serial
7
+ property :last_read, Date
8
+ property :written_on, Time
9
+ property :updated_at, DateTime
10
+ end
11
+ end; end; end
data/spec/spec.opts ADDED
@@ -0,0 +1,3 @@
1
+ --format progress
2
+ --color
3
+ --backtrace
@@ -0,0 +1,19 @@
1
+ $:.unshift File.expand_path File.dirname(__FILE__) + '../lib'
2
+ require 'dm-core/spec/setup'
3
+ require 'dm-core/spec/lib/adapter_helpers'
4
+ require 'dm-core/spec/lib/spec_helper'
5
+ require 'dm-core/spec/lib/pending_helpers'
6
+
7
+ DataMapper::Spec.setup
8
+ DataMapper.finalize
9
+
10
+ Spec::Runner.configure do |config|
11
+
12
+ config.extend(DataMapper::Spec::Adapters::Helpers)
13
+ config.include(DataMapper::Spec::PendingHelpers)
14
+
15
+ config.after :all do
16
+ DataMapper::Spec.cleanup_models
17
+ end
18
+
19
+ end