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,76 @@
1
+ require 'active_support/core_ext/hash/except'
2
+ require 'active_support/core_ext/class/attribute_accessors'
3
+
4
+ module Rails
5
+ module DataMapper
6
+
7
+ mattr_accessor :configuration
8
+
9
+ class Configuration
10
+
11
+ attr_accessor :raw
12
+ attr_accessor :root
13
+
14
+ def self.create
15
+ Rails::DataMapper.configuration ||= new
16
+ end
17
+
18
+ def environments
19
+ raw.keys
20
+ end
21
+
22
+ def repositories
23
+ @repositories ||= raw.reject { |k,v| k =~ /defaults/ }.inject({}) do |repositories, pair|
24
+ environment, config = pair.first, pair.last
25
+ repositories[environment] = begin
26
+ c = config['repositories'] || {}
27
+ c['default'] = config.except('repositories') if config.except('repositories')
28
+ normalize_repository_config(c)
29
+ end
30
+ repositories
31
+ end
32
+ end
33
+
34
+ def resource_naming_convention
35
+ @resource_naming_convention ||= {}
36
+ end
37
+
38
+ def field_naming_convention
39
+ @field_naming_convention ||= {}
40
+ end
41
+
42
+ private
43
+
44
+ def normalize_repository_config(hash)
45
+ config = {}
46
+ hash.each do |key, value|
47
+
48
+ config[key] = if value.kind_of?(Hash)
49
+ normalize_repository_config(value)
50
+ elsif key == 'port'
51
+ value.to_i
52
+ elsif key == 'adapter' && value == 'postgresql'
53
+ 'postgres'
54
+ elsif (key == 'database' || key == 'path') && hash['adapter'] =~ /sqlite/
55
+ value == ':memory:' ? value : File.expand_path(hash[key], root)
56
+ else
57
+ value
58
+ end
59
+
60
+ # FIXME Rely on a new dm-sqlite-adapter to do the right thing
61
+ # For now, we need to make sure that both 'path' and 'database'
62
+ # point to the same thing, since dm-sqlite-adapter always passes
63
+ # both to the underlying do_sqlite3 adapter and there's no
64
+ # guarantee which one will be used
65
+
66
+ config['path'] = config[key] if key == 'database'
67
+ config['database'] = config[key] if key == 'path'
68
+
69
+ end
70
+ config
71
+ end
72
+
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,89 @@
1
+ require 'dm-core'
2
+ require 'active_support/core_ext/class/attribute'
3
+ require 'active_support/concern'
4
+ require 'active_model'
5
+
6
+ module ActiveModel
7
+ module MassAssignmentSecurity
8
+ # Provides a patched version of the Sanitizer used in Rails to handle property
9
+ # and relationship objects as keys. There is no way to inject a custom sanitizer
10
+ # without reimplementing the permission sets.
11
+ Sanitizer.send(Sanitizer.is_a?(Module) ? :module_eval : :class_eval) do
12
+ # Returns all attributes not denied by the authorizer.
13
+ #
14
+ # @param [Hash{Symbol,String,::DataMapper::Property,::DataMapper::Relationship=>Object}] attributes
15
+ # Names and values of attributes to sanitize.
16
+ # @return [Hash]
17
+ # Sanitized hash of attributes.
18
+ def sanitize(attributes, authorizer = nil)
19
+ sanitized_attributes = attributes.reject do |key, value|
20
+ key_name = key.name rescue key
21
+ authorizer ? authorizer.deny?(key_name) : deny?(key_name)
22
+ end
23
+ debug_protected_attribute_removal(attributes, sanitized_attributes)
24
+ sanitized_attributes
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ module DataMapper
31
+ # Include this module into a DataMapper model to enable ActiveModel's mass
32
+ # assignment security.
33
+ #
34
+ # To use second parameter of {#attributes=} make sure to include this module
35
+ # last.
36
+ module MassAssignmentSecurity
37
+ extend ::ActiveSupport::Concern
38
+ include ::ActiveModel::MassAssignmentSecurity
39
+
40
+ module ClassMethods
41
+ extend ::ActiveModel::MassAssignmentSecurity::ClassMethods
42
+
43
+ def logger
44
+ @logger ||= ::DataMapper.logger
45
+ end
46
+ end
47
+
48
+ # Sanitizes the specified +attributes+ according to the defined mass-assignment
49
+ # security rules and calls +super+ with the result.
50
+ #
51
+ # Use either +attr_accessible+ to specify which attributes are allowed to be
52
+ # assigned via {#attributes=}, or +attr_protected+ to specify which attributes
53
+ # are *not* allowed to be assigned via {#attributes=}.
54
+ #
55
+ # +attr_accessible+ and +attr_protected+ are mutually exclusive.
56
+ #
57
+ # @param [Hash{Symbol,String,::DataMapper::Property,::DataMapper::Relationship=>Object}] attributes
58
+ # Names and values of attributes to sanitize.
59
+ # @param [Boolean] guard_protected_attributes
60
+ # Determines whether mass-security rules are applied (when +true+) or not.
61
+ # @return [Hash]
62
+ # Sanitized hash of attributes.
63
+ # @api public
64
+ #
65
+ # @example [Usage]
66
+ # class User
67
+ # include DataMapper::Resource
68
+ # include DataMapper::MassAssignmentSecurity
69
+ #
70
+ # property :name, String
71
+ # property :is_admin, Boolean
72
+ #
73
+ # # Only allow name to be set via #attributes=
74
+ # attr_accessible :name
75
+ # end
76
+ #
77
+ # user = User.new
78
+ # user.attributes = { :username => 'Phusion', :is_admin => true }
79
+ # user.username # => "Phusion"
80
+ # user.is_admin # => false
81
+ #
82
+ # user.send(:attributes=, { :username => 'Phusion', :is_admin => true }, false)
83
+ # user.is_admin # => true
84
+ def attributes=(attributes, guard_protected_attributes = true)
85
+ attributes = sanitize_for_mass_assignment(attributes) if guard_protected_attributes
86
+ super(attributes)
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,20 @@
1
+ module Rails
2
+ module DataMapper
3
+ module Middleware
4
+
5
+ class IdentityMap
6
+ def initialize(app, name = :default)
7
+ @app = app
8
+ @name = name.to_sym
9
+ end
10
+
11
+ def call(env)
12
+ ::DataMapper.repository(@name) do
13
+ @app.call(env)
14
+ end
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,167 @@
1
+ require 'active_support/core_ext/module/aliasing'
2
+
3
+ module Rails
4
+ module DataMapper
5
+ # Encapsulates an error of a multiparameter assignment.
6
+ class MultiparameterAssignmentError < StandardError
7
+ # Gets the target attribute of the assignment.
8
+ # @return [String]
9
+ attr_reader :attribute
10
+
11
+ # Gets the assigned values.
12
+ # @return [Array]
13
+ attr_reader :values
14
+
15
+ # Gets the exception raised on the assignment.
16
+ # @return [Exception]
17
+ attr_reader :exception
18
+
19
+ # Initializes a new instance of the {MultiparameterAssignmentError} class.
20
+ #
21
+ # @param [String] attribute The target attribute of the assignment.
22
+ # @param [Array] values The assigned values.
23
+ # @param [Exception] exception The exception raised on the assignment.
24
+ def initialize(attribute, values, exception)
25
+ super("Could not assign #{values.inspect} to #{attribute} (#{exception.message}).")
26
+ @attribute = attribute
27
+ @values = values
28
+ @exception = exception
29
+ end
30
+ end
31
+
32
+ # Raised by {MultiparameterAttributes#attributes=} when there are errors when
33
+ # merging multiparameter attributes. Use {#errors} to get the array of errors
34
+ # occured.
35
+ class MultiparameterAssignmentErrors < StandardError
36
+ # Gets the array of assignment errors.
37
+ # @return [Array<MultiparameterAssignmentError>]
38
+ attr_reader :errors
39
+
40
+ # Initializes a new instance of the {MultiparameterAssignmentErrors} class.
41
+ #
42
+ # @param [Array<MultiparameterAssignmentError>] errors
43
+ # The array of assignment errors.
44
+ # @param [String] message Optional error message.
45
+ def initialize(errors, message = nil)
46
+ super(message || "#{errors.size} error#{errors.size == 1 ? '' : 's'} on assignment of multiparameter attributes.")
47
+ @errors = errors
48
+ end
49
+ end
50
+
51
+ # Include this module into a DataMapper model to enable multiparameter
52
+ # attributes.
53
+ #
54
+ # A multiparameter attribute has +attr(Xc)+ as name where +attr+ specifies
55
+ # the attribute, +X+ the position of the value, and +c+ an optional typecast
56
+ # identifier. All values that share an +attr+ are sorted by their position,
57
+ # optionally cast using +#to_c+ (where +c+ is the typecast identifier) and
58
+ # then used to instantiate the proper attribute value.
59
+ #
60
+ # @example
61
+ # # Assigning a hash with multiparameter values for a +Date+ property called
62
+ # # +written_on+:
63
+ # resource.attributes = {
64
+ # 'written_on(1i)' => '2004',
65
+ # 'written_on(2i)' => '6',
66
+ # 'written_on(3i)' => '24' }
67
+ #
68
+ # # +Date+ will be initialized with each string cast to a number using
69
+ # # #to_i.
70
+ # resource.written_on == Date.new(2004, 6, 24)
71
+ module MultiparameterAttributes
72
+ # Merges multiparameter attributes and calls +super+ with the merged
73
+ # attributes.
74
+ #
75
+ # @param [Hash{String,Symbol=>Object}] attributes
76
+ # Names and values of attributes to assign.
77
+ # @return [Hash]
78
+ # Names and values of attributes assigned.
79
+ # @raise [MultiparameterAssignmentErrors]
80
+ # One or more multiparameters could not be assigned.
81
+ # @api public
82
+ def attributes=(attributes)
83
+ attribs = attributes.dup
84
+ multi_parameter_attributes = []
85
+ attribs.each do |k, v|
86
+ if k.to_s.include?("(")
87
+ multi_parameter_attributes << [ k, v ]
88
+ attribs.delete(k)
89
+ end
90
+ end
91
+
92
+ attribs.merge!(merge_multiparameter_attributes(multi_parameter_attributes))
93
+ super(attribs)
94
+ end
95
+
96
+ protected
97
+ def merge_multiparameter_attributes(pairs)
98
+ pairs = extract_multiparameter_attributes(pairs)
99
+
100
+ errors = []
101
+ attributes = {}
102
+ pairs.each do |name, values_with_empty_parameters|
103
+ # ActiveRecord keeps the empty values to set dates without a year.
104
+ # Removing all nils (instead of removing only trailing nils) seems
105
+ # like a weird behavior though.
106
+ values = values_with_empty_parameters.compact
107
+
108
+ if values.empty?
109
+ attributes[name] = nil
110
+ next
111
+ end
112
+
113
+ klass = properties[name].primitive
114
+ begin
115
+ attributes[name] =
116
+ if klass == Time
117
+ Time.local(*values)
118
+ elsif klass == Date
119
+ # Date does not replace nil values with defaults.
120
+ Date.new(*values_with_empty_parameters.map { |v| v.nil? ? 1 : v })
121
+ else
122
+ klass.new(*values)
123
+ end
124
+ rescue => ex
125
+ errors << MultiparameterAssignmentError.new(name, values, ex)
126
+ end
127
+ end
128
+
129
+ unless errors.empty?
130
+ raise MultiparameterAssignmentErrors.new(errors)
131
+ end
132
+
133
+ attributes
134
+ end
135
+
136
+ def extract_multiparameter_attributes(pairs)
137
+ attributes = {}
138
+
139
+ for multiparameter_name, value in pairs
140
+ unless multiparameter_name =~ /\A ([^\)]+) \( ([0-9]+) ([a-z])? \) \z/x
141
+ raise "Invalid multiparameter name #{multiparameter_name.inspect}."
142
+ end
143
+
144
+ name, position, typecast = $1, $2, $3
145
+ attributes[name] ||= []
146
+
147
+ parameter_value =
148
+ if value.empty?
149
+ nil
150
+ elsif typecast
151
+ value.send('to_' + typecast)
152
+ else
153
+ value
154
+ end
155
+
156
+ attributes[name] << [ position, parameter_value ]
157
+ end
158
+
159
+ # Order each parameter array according to the position, then discard the
160
+ # position.
161
+ attributes.each { |name, values|
162
+ attributes[name] = values.sort_by{ |v| v.first }.collect { |v| v.last }
163
+ }
164
+ end
165
+ end # MultiparameterAttributes
166
+ end
167
+ end
@@ -0,0 +1,100 @@
1
+ require 'dm-core'
2
+ require 'dm-active_model'
3
+
4
+ require 'rails'
5
+ require 'active_model/railtie'
6
+
7
+ # Comment taken from active_record/railtie.rb
8
+ #
9
+ # For now, action_controller must always be present with
10
+ # rails, so let's make sure that it gets required before
11
+ # here. This is needed for correctly setting up the middleware.
12
+ # In the future, this might become an optional require.
13
+ require 'action_controller/railtie'
14
+
15
+ require 'dm-rails/setup'
16
+ require "dm-rails/railties/log_subscriber"
17
+ require "dm-rails/railties/i18n_support"
18
+
19
+ # The module provided in there is made available
20
+ # but users will still need to include it into the
21
+ # models they want it to use it in.
22
+ require 'dm-rails/mass_assignment_security'
23
+ require 'dm-rails/multiparameter_attributes'
24
+
25
+ module Rails
26
+ module DataMapper
27
+
28
+ class Railtie < Rails::Railtie
29
+
30
+ config.data_mapper = Rails::DataMapper::Configuration.create
31
+
32
+ config.app_generators.orm :data_mapper, :migration => true
33
+
34
+
35
+ # Support overwriting crucial steps in subclasses
36
+
37
+ def configure_data_mapper(app)
38
+ app.config.data_mapper.root = Rails.root
39
+ app.config.data_mapper.raw = app.config.database_configuration
40
+ end
41
+
42
+ def setup_i18n_support(app)
43
+ ::DataMapper::Model.append_extensions(::ActiveModel::Translation)
44
+ ::DataMapper::Model.append_extensions(Rails::DataMapper::I18nSupport)
45
+ end
46
+
47
+ def setup_controller_runtime(app)
48
+ require "dm-rails/railties/controller_runtime"
49
+ ActiveSupport.on_load(:action_controller) do
50
+ include Rails::DataMapper::Railties::ControllerRuntime
51
+ end
52
+ end
53
+
54
+ def setup_logger(app, logger)
55
+ Rails::DataMapper.setup_logger(logger)
56
+ end
57
+
58
+
59
+ initializer 'data_mapper.configuration' do |app|
60
+ configure_data_mapper(app)
61
+ ::DataMapper::Model.append_inclusions(Rails::DataMapper::MultiparameterAttributes)
62
+ end
63
+
64
+ initializer 'data_mapper.logger' do |app|
65
+ setup_logger(app, Rails.logger)
66
+ end
67
+
68
+ initializer 'data_mapper.i18n_support' do |app|
69
+ setup_i18n_support(app)
70
+ end
71
+
72
+ # Expose database runtime to controller for logging.
73
+ initializer "data_mapper.log_runtime" do |app|
74
+ setup_controller_runtime(app)
75
+ end
76
+
77
+ # Preload all models once in production mode,
78
+ # and before every request in development mode
79
+ initializer "datamapper.add_to_prepare" do |app|
80
+ config.to_prepare { Rails::DataMapper.preload_models(app) }
81
+ end
82
+
83
+ # Run setup code once in after_initialize to make sure all initializers
84
+ # are in effect once we setup the connection. Also, this will make sure
85
+ # that the connection gets set up after all models have been loaded,
86
+ # because #after_initialize is guaranteed to run after #to_prepare.
87
+ # Both production and development environment will execute the setup
88
+ # code only once.
89
+ config.after_initialize do |app|
90
+ Rails::DataMapper.setup(Rails.env)
91
+ end
92
+
93
+ rake_tasks do
94
+ load 'dm-rails/railties/database.rake'
95
+ end
96
+
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,45 @@
1
+ require 'active_support/core_ext/module/attr_internal'
2
+
3
+ module Rails
4
+ module DataMapper
5
+ module Railties
6
+
7
+ module ControllerRuntime
8
+
9
+ extend ActiveSupport::Concern
10
+
11
+ protected
12
+
13
+ attr_internal :db_runtime
14
+
15
+ def cleanup_view_runtime
16
+ # TODO add checks if DataMapper is connected to a repository.
17
+ # If it is, do this, if it isn't, just delegate to super
18
+ db_rt_before_render = ::DataMapper::Railties::LogSubscriber.reset_runtime
19
+ runtime = super
20
+ db_rt_after_render = ::DataMapper::Railties::LogSubscriber.reset_runtime
21
+ self.db_runtime = db_rt_before_render + db_rt_after_render
22
+ runtime - db_rt_after_render
23
+ end
24
+
25
+ def append_info_to_payload(payload)
26
+ super
27
+ payload[:db_runtime] = db_runtime
28
+ end
29
+
30
+
31
+ module ClassMethods
32
+
33
+ def log_process_action(payload)
34
+ messages, db_runtime = super, payload[:db_runtime]
35
+ messages << ("Models: %.3fms" % db_runtime.to_f) if db_runtime
36
+ messages
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,106 @@
1
+ require 'dm-rails/setup'
2
+ require 'dm-rails/storage'
3
+
4
+ namespace :db do
5
+
6
+ desc 'Create the database, load the schema, and initialize with the seed data'
7
+ task :setup => [ 'db:create', 'db:automigrate', 'db:seed' ]
8
+
9
+ namespace :create do
10
+ desc 'Create all the local databases defined in config/database.yml'
11
+ task :all => :environment do
12
+ Rails::DataMapper.storage.create_all
13
+ end
14
+ end
15
+
16
+ desc "Create all local databases defined for the current Rails.env"
17
+ task :create => :environment do
18
+ Rails::DataMapper.storage.create_environment(Rails::DataMapper.configuration.repositories[Rails.env])
19
+ end
20
+
21
+ namespace :drop do
22
+ desc 'Drop all the local databases defined in config/database.yml'
23
+ task :all => :environment do
24
+ Rails::DataMapper.storage.drop_all
25
+ end
26
+ end
27
+
28
+ desc "Drop all local databases defined for the current Rails.env"
29
+ task :drop => :environment do
30
+ Rails::DataMapper.storage.drop_environment(Rails::DataMapper.configuration.repositories[Rails.env])
31
+ end
32
+
33
+
34
+ desc 'Perform destructive automigration of all repositories in the current Rails.env'
35
+ task :automigrate => :environment do
36
+ require 'dm-migrations'
37
+ Rails::DataMapper.configuration.repositories[Rails.env].each do |repository, config|
38
+ ::DataMapper.auto_migrate!(repository.to_sym)
39
+ puts "[datamapper] Finished auto_migrate! for :#{repository} repository '#{config['database']}'"
40
+ end
41
+ end
42
+
43
+ desc 'Perform non destructive automigration of all repositories in the current Rails.env'
44
+ task :autoupgrade => :environment do
45
+ require 'dm-migrations'
46
+ Rails::DataMapper.configuration.repositories[Rails.env].each do |repository, config|
47
+ ::DataMapper.auto_upgrade!(repository.to_sym)
48
+ puts "[datamapper] Finished auto_upgrade! for :#{repository} repository '#{config['database']}'"
49
+ end
50
+ end
51
+
52
+ desc 'Load the seed data from db/seeds.rb'
53
+ task :seed => :environment do
54
+ seed_file = File.join(Rails.root, 'db', 'seeds.rb')
55
+ load(seed_file) if File.exist?(seed_file)
56
+ end
57
+
58
+ namespace :migrate do
59
+ task :load => :environment do
60
+ require 'dm-migrations/migration_runner'
61
+ FileList['db/migrate/*.rb'].each do |migration|
62
+ load migration
63
+ end
64
+ end
65
+
66
+ desc 'Migrate up using migrations'
67
+ task :up, [:version] => [:load] do |t, args|
68
+ ::DataMapper::MigrationRunner.migrate_up!(args[:version])
69
+ end
70
+
71
+ desc 'Migrate down using migrations'
72
+ task :down, [:version] => [:load] do |t, args|
73
+ ::DataMapper::MigrationRunner.migrate_down!(args[:version])
74
+ end
75
+ end
76
+
77
+ desc 'Migrate the database to the latest version'
78
+ task :migrate do
79
+ migrate_task = if Dir['db/migrate/*.rb'].empty?
80
+ 'db:autoupgrade'
81
+ else
82
+ 'db:migrate:up'
83
+ end
84
+
85
+ Rake::Task[migrate_task].invoke
86
+ end
87
+
88
+ namespace :sessions do
89
+ desc "Creates the sessions table for DataMapperStore"
90
+ task :create => :environment do
91
+ require 'dm-rails/session_store'
92
+ Rails::DataMapper::SessionStore::Session.auto_migrate!
93
+ database = Rails::DataMapper.configuration.repositories[Rails.env]['database']
94
+ puts "Created '#{database}.sessions'"
95
+ end
96
+
97
+ desc "Clear the sessions table for DataMapperStore"
98
+ task :clear => :environment do
99
+ require 'dm-rails/session_store'
100
+ Rails::DataMapper::SessionStore::Session.destroy!
101
+ database = Rails::DataMapper.configuration.repositories[Rails.env]['database']
102
+ puts "Deleted entries from '#{database}.sessions'"
103
+ end
104
+ end
105
+
106
+ end
@@ -0,0 +1,12 @@
1
+ module Rails
2
+ module DataMapper
3
+
4
+ module I18nSupport
5
+ # Set the i18n scope to overwrite ActiveModel.
6
+ def i18n_scope #:nodoc:
7
+ :data_mapper
8
+ end
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,39 @@
1
+ require 'active_support/notifications'
2
+
3
+ # TODO This needs to be fixed upstream in active_support/notifications/instrumenter.rb
4
+ #
5
+ # We need to monkeypatch this for now, because the original implementation hardcodes the
6
+ # duration to the time elapsed between start and end of the event. The current upstream
7
+ # implementation is included here for reference:
8
+ #
9
+ # def duration
10
+ # @duration ||= 1000.0 * (@end - @time)
11
+ # end
12
+ #
13
+ # It should be safe to assume that explicitly provided duration information should be at
14
+ # least as precise as the current generic solution, if not more (as in our specific case).
15
+ #
16
+ module ActiveSupport
17
+ module Notifications
18
+ class Event
19
+ def duration
20
+ payload[:duration] ? (payload[:duration] / 1000.0) : 1000.0 * (self.end - self.time)
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ module LogListener
27
+ def log(message)
28
+ ActiveSupport::Notifications.instrument('sql.data_mapper',
29
+ :name => 'SQL',
30
+ :sql => message.query, # TODO think about changing the key to :query
31
+ :start => message.start,
32
+ :duration => message.duration,
33
+ :connection_id => self.object_id
34
+ )
35
+ super
36
+ rescue Exception => e
37
+ ::DataMapper.logger.error "[datamapper] #{e.class.name}: #{e.message}: #{message.inspect}}"
38
+ end
39
+ end