mongo_mapper 0.8.6 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/UPGRADES +10 -0
- data/bin/mmconsole +0 -1
- data/examples/identity_map/automatic.rb +1 -7
- data/examples/plugins.rb +9 -9
- data/examples/safe.rb +43 -0
- data/lib/mongo_mapper.rb +46 -33
- data/lib/mongo_mapper/document.rb +33 -32
- data/lib/mongo_mapper/embedded_document.rb +22 -22
- data/lib/mongo_mapper/locale/en.yml +5 -0
- data/lib/mongo_mapper/middleware/identity_map.rb +16 -0
- data/lib/mongo_mapper/plugins.rb +16 -3
- data/lib/mongo_mapper/plugins/accessible.rb +2 -0
- data/lib/mongo_mapper/plugins/active_model.rb +18 -0
- data/lib/mongo_mapper/plugins/associations.rb +37 -42
- data/lib/mongo_mapper/plugins/associations/base.rb +14 -50
- data/lib/mongo_mapper/plugins/associations/belongs_to_association.rb +58 -0
- data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +6 -1
- data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +30 -2
- data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +4 -0
- data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +12 -6
- data/lib/mongo_mapper/plugins/associations/many_association.rb +67 -0
- data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +5 -5
- data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +1 -1
- data/lib/mongo_mapper/plugins/associations/one_association.rb +20 -0
- data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +5 -0
- data/lib/mongo_mapper/plugins/associations/one_proxy.rb +7 -7
- data/lib/mongo_mapper/plugins/associations/proxy.rb +2 -2
- data/lib/mongo_mapper/plugins/caching.rb +3 -1
- data/lib/mongo_mapper/plugins/callbacks.rb +12 -221
- data/lib/mongo_mapper/plugins/clone.rb +3 -1
- data/lib/mongo_mapper/plugins/dirty.rb +38 -91
- data/lib/mongo_mapper/plugins/document.rb +4 -2
- data/lib/mongo_mapper/plugins/dynamic_querying.rb +2 -0
- data/lib/mongo_mapper/plugins/embedded_callbacks.rb +43 -0
- data/lib/mongo_mapper/plugins/embedded_document.rb +16 -9
- data/lib/mongo_mapper/plugins/equality.rb +2 -0
- data/lib/mongo_mapper/plugins/identity_map.rb +4 -2
- data/lib/mongo_mapper/plugins/indexes.rb +2 -0
- data/lib/mongo_mapper/plugins/inspect.rb +3 -1
- data/lib/mongo_mapper/plugins/keys.rb +28 -22
- data/lib/mongo_mapper/plugins/keys/key.rb +12 -6
- data/lib/mongo_mapper/plugins/logger.rb +2 -0
- data/lib/mongo_mapper/plugins/modifiers.rb +3 -1
- data/lib/mongo_mapper/plugins/pagination.rb +2 -0
- data/lib/mongo_mapper/plugins/persistence.rb +2 -0
- data/lib/mongo_mapper/plugins/protected.rb +2 -0
- data/lib/mongo_mapper/plugins/querying.rb +5 -4
- data/lib/mongo_mapper/plugins/rails.rb +3 -5
- data/lib/mongo_mapper/plugins/safe.rb +2 -0
- data/lib/mongo_mapper/plugins/sci.rb +2 -0
- data/lib/mongo_mapper/plugins/scopes.rb +2 -0
- data/lib/mongo_mapper/plugins/serialization.rb +67 -46
- data/lib/mongo_mapper/plugins/timestamps.rb +3 -1
- data/lib/mongo_mapper/plugins/userstamps.rb +2 -0
- data/lib/mongo_mapper/plugins/validations.rb +40 -24
- data/lib/mongo_mapper/railtie.rb +49 -0
- data/lib/mongo_mapper/railtie/database.rake +60 -0
- data/lib/mongo_mapper/support/descendant_appends.rb +11 -11
- data/lib/mongo_mapper/translation.rb +10 -0
- data/lib/mongo_mapper/version.rb +1 -1
- data/lib/rails/generators/mongo_mapper/config/config_generator.rb +24 -0
- data/lib/rails/generators/mongo_mapper/config/templates/mongo.yml +18 -0
- data/lib/rails/generators/mongo_mapper/model/model_generator.rb +23 -0
- data/lib/rails/generators/mongo_mapper/model/templates/model.rb +11 -0
- data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +1 -0
- data/test/functional/associations/test_belongs_to_proxy.rb +131 -1
- data/test/functional/associations/test_in_array_proxy.rb +30 -0
- data/test/functional/associations/test_many_documents_proxy.rb +30 -2
- data/test/functional/associations/test_many_embedded_proxy.rb +33 -0
- data/test/functional/associations/test_many_polymorphic_proxy.rb +1 -0
- data/test/functional/associations/test_one_embedded_proxy.rb +21 -2
- data/test/functional/associations/test_one_proxy.rb +49 -9
- data/test/functional/test_associations.rb +2 -0
- data/test/functional/test_caching.rb +3 -2
- data/test/functional/test_callbacks.rb +25 -18
- data/test/functional/test_dirty.rb +123 -1
- data/test/functional/test_document.rb +26 -2
- data/test/functional/test_embedded_document.rb +68 -2
- data/test/functional/test_identity_map.rb +3 -4
- data/test/functional/test_querying.rb +11 -0
- data/test/functional/test_userstamps.rb +2 -2
- data/test/functional/test_validations.rb +31 -29
- data/test/models.rb +10 -0
- data/test/test_active_model_lint.rb +1 -1
- data/test/test_helper.rb +9 -10
- data/test/unit/associations/test_base.rb +24 -100
- data/test/unit/associations/test_belongs_to_association.rb +29 -0
- data/test/unit/associations/test_many_association.rb +63 -0
- data/test/unit/associations/test_one_association.rb +18 -0
- data/test/unit/serializers/test_json_serializer.rb +0 -1
- data/test/unit/test_descendant_appends.rb +8 -16
- data/test/unit/test_document.rb +4 -9
- data/test/unit/test_dynamic_finder.rb +1 -1
- data/test/unit/test_embedded_document.rb +51 -18
- data/test/unit/test_identity_map_middleware.rb +34 -0
- data/test/unit/test_inspect.rb +22 -0
- data/test/unit/test_key.rb +21 -1
- data/test/unit/test_keys.rb +0 -2
- data/test/unit/test_plugins.rb +106 -20
- data/test/unit/test_rails.rb +8 -8
- data/test/unit/test_serialization.rb +116 -1
- data/test/unit/test_translation.rb +27 -0
- data/test/unit/test_validations.rb +66 -81
- metadata +103 -43
- data/examples/identity_map/middleware.rb +0 -14
- data/lib/mongo_mapper/plugins/descendants.rb +0 -17
- data/rails/init.rb +0 -19
@@ -2,9 +2,7 @@
|
|
2
2
|
module MongoMapper
|
3
3
|
module Plugins
|
4
4
|
module Rails
|
5
|
-
|
6
|
-
model.extend ActiveModel::Naming if defined?(ActiveModel)
|
7
|
-
end
|
5
|
+
extend ActiveSupport::Concern
|
8
6
|
|
9
7
|
module InstanceMethods
|
10
8
|
def to_param
|
@@ -27,8 +25,8 @@ module MongoMapper
|
|
27
25
|
self[name]
|
28
26
|
end
|
29
27
|
|
30
|
-
def
|
31
|
-
|
28
|
+
def read_attribute_before_type_cast(name)
|
29
|
+
read_key_before_type_cast(name)
|
32
30
|
end
|
33
31
|
|
34
32
|
def write_attribute(name, value)
|
@@ -1,73 +1,94 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
require '
|
2
|
+
require 'active_model/serializers/json'
|
3
|
+
require 'active_model/serializers/xml'
|
3
4
|
|
4
5
|
module MongoMapper
|
5
6
|
module Plugins
|
6
7
|
module Serialization
|
7
|
-
|
8
|
-
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
include ::ActiveModel::Serializers::JSON
|
12
|
+
include ::ActiveModel::Serializers::Xml
|
13
|
+
self.include_root_in_json = false
|
9
14
|
end
|
10
15
|
|
11
16
|
module InstanceMethods
|
12
|
-
def
|
17
|
+
def serializable_attributes
|
18
|
+
attributes.keys.map { |k| k.to_s } + ['id'] - ['_id']
|
19
|
+
end
|
20
|
+
|
21
|
+
def serializable_hash(options = nil)
|
13
22
|
options ||= {}
|
14
|
-
unless options[:only]
|
15
|
-
methods = [options.delete(:methods)].flatten.compact
|
16
|
-
methods << :id
|
17
|
-
options[:methods] = methods.uniq
|
18
|
-
end
|
19
23
|
|
20
|
-
|
21
|
-
except
|
22
|
-
options[:except] = except
|
24
|
+
options[:only] = Array.wrap(options[:only]).map { |k| k.to_s }
|
25
|
+
options[:except] = Array.wrap(options[:except]).map { |k| k.to_s }
|
23
26
|
|
24
|
-
|
25
|
-
hash = begin
|
26
|
-
options[:only] = Array.wrap(options[:only]).map { |n| n.to_s }
|
27
|
-
options[:except] = Array.wrap(options[:except]).map { |n| n.to_s }
|
27
|
+
attribute_names = serializable_attributes
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
29
|
+
if options[:only].any?
|
30
|
+
attribute_names &= options[:only]
|
31
|
+
elsif options[:except].any?
|
32
|
+
attribute_names -= options[:except]
|
33
|
+
end
|
35
34
|
|
36
|
-
|
37
|
-
|
38
|
-
|
35
|
+
attribute_names += Array.wrap(options[:methods]).map { |m| m.to_s }.select do |method|
|
36
|
+
respond_to?(method)
|
37
|
+
end
|
38
|
+
|
39
|
+
hash = attribute_names.sort.inject({}) do |hash, name|
|
40
|
+
value = send(name)
|
41
|
+
hash[name] = if value.is_a?(Array)
|
42
|
+
value.map {|v| v.respond_to?(:serializable_hash) ? v.serializable_hash : v }
|
43
|
+
elsif value.respond_to?(:serializable_hash)
|
44
|
+
value.serializable_hash
|
45
|
+
else
|
46
|
+
value
|
39
47
|
end
|
48
|
+
hash
|
49
|
+
end
|
40
50
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
51
|
+
serializable_add_includes(options) do |association, records, opts|
|
52
|
+
hash[association.to_s] = records.is_a?(Array) ?
|
53
|
+
records.map { |r| r.serializable_hash(opts) } :
|
54
|
+
records.serializable_hash(opts)
|
45
55
|
end
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
56
|
+
|
57
|
+
hash
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def serializable_add_includes(options = {})
|
63
|
+
return unless include_associations = options.delete(:include)
|
64
|
+
|
65
|
+
base_only_or_except = { :except => options[:except],
|
66
|
+
:only => options[:only] }
|
67
|
+
|
68
|
+
include_has_options = include_associations.is_a?(Hash)
|
69
|
+
associations = include_has_options ? include_associations.keys : Array.wrap(include_associations)
|
70
|
+
|
71
|
+
associations.each do |association|
|
72
|
+
records = get_proxy(self.class.associations[association])
|
73
|
+
unless records.nil?
|
74
|
+
association_options = include_has_options ? include_associations[association] : base_only_or_except
|
75
|
+
opts = options.merge(association_options)
|
76
|
+
yield(association, records, opts)
|
59
77
|
end
|
60
78
|
end
|
61
79
|
|
62
|
-
|
63
|
-
hash = { ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self.class.name)).gsub(/:.*/,'') => hash } if include_root_in_json
|
64
|
-
hash
|
80
|
+
options[:include] = include_associations
|
65
81
|
end
|
82
|
+
|
66
83
|
end
|
67
84
|
|
68
85
|
module ClassMethods
|
69
86
|
def from_json(json)
|
70
|
-
self.new
|
87
|
+
self.new.from_json(json)
|
88
|
+
end
|
89
|
+
|
90
|
+
def from_xml(xml)
|
91
|
+
self.new.from_xml(xml)
|
71
92
|
end
|
72
93
|
end
|
73
94
|
|
@@ -2,6 +2,8 @@
|
|
2
2
|
module MongoMapper
|
3
3
|
module Plugins
|
4
4
|
module Timestamps
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
5
7
|
module ClassMethods
|
6
8
|
def timestamps!
|
7
9
|
key :created_at, Time
|
@@ -13,7 +15,7 @@ module MongoMapper
|
|
13
15
|
module InstanceMethods
|
14
16
|
def update_timestamps
|
15
17
|
now = Time.now.utc
|
16
|
-
self[:created_at] = now if
|
18
|
+
self[:created_at] = now if !persisted? && !created_at?
|
17
19
|
self[:updated_at] = now
|
18
20
|
end
|
19
21
|
end
|
@@ -2,30 +2,44 @@
|
|
2
2
|
module MongoMapper
|
3
3
|
module Plugins
|
4
4
|
module Validations
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
include ::ActiveModel::Validations
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def validates_uniqueness_of(*attr_names)
|
11
|
+
validates_with UniquenessValidator, _merge_attributes(attr_names)
|
9
12
|
end
|
10
|
-
end
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
add_validations(args, Validations::ValidatesUniquenessOf)
|
14
|
+
def validates_associated(*attr_names)
|
15
|
+
validates_with AssociatedValidator, _merge_attributes(attr_names)
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
18
|
-
class
|
19
|
-
|
20
|
-
|
19
|
+
class UniquenessValidator < ::ActiveModel::EachValidator
|
20
|
+
def initialize(options)
|
21
|
+
super(options.reverse_merge(:case_sensitive => true))
|
22
|
+
end
|
23
|
+
|
24
|
+
def setup(klass)
|
25
|
+
@klass = klass
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate_each(record, attribute, value)
|
29
|
+
conditions = scope_conditions(record)
|
21
30
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
31
|
+
if options[:case_sensitive]
|
32
|
+
conditions[attribute] = value
|
33
|
+
else
|
34
|
+
conditions[attribute] = /^#{Regexp.escape(value.to_s)}$/i
|
35
|
+
end
|
36
|
+
|
37
|
+
# Make sure we're not including the current document in the query
|
38
|
+
conditions[:_id.ne] = record._id if record._id
|
39
|
+
|
40
|
+
if @klass.exists?(conditions)
|
41
|
+
record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
|
42
|
+
end
|
29
43
|
end
|
30
44
|
|
31
45
|
def message(instance)
|
@@ -33,18 +47,20 @@ module MongoMapper
|
|
33
47
|
end
|
34
48
|
|
35
49
|
def scope_conditions(instance)
|
36
|
-
|
37
|
-
Array(scope).inject({}) do |conditions, key|
|
50
|
+
Array(options[:scope] || []).inject({}) do |conditions, key|
|
38
51
|
conditions.merge(key => instance[key])
|
39
52
|
end
|
40
53
|
end
|
54
|
+
end
|
41
55
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
56
|
+
class AssociatedValidator < ::ActiveModel::EachValidator
|
57
|
+
def validate_each(record, attribute, value)
|
58
|
+
if !Array.wrap(value).all? { |c| c.nil? || c.valid? }
|
59
|
+
record.errors.add(attribute, :invalid, :default => options[:message], :value => value)
|
60
|
+
end
|
46
61
|
end
|
47
62
|
end
|
63
|
+
|
48
64
|
end
|
49
65
|
end
|
50
66
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "mongo_mapper"
|
2
|
+
require "rails"
|
3
|
+
require "active_model/railtie"
|
4
|
+
|
5
|
+
module MongoMapper
|
6
|
+
# = MongoMapper Railtie
|
7
|
+
class Railtie < Rails::Railtie
|
8
|
+
|
9
|
+
config.mongo_mapper = ActiveSupport::OrderedOptions.new
|
10
|
+
|
11
|
+
rake_tasks do
|
12
|
+
load "mongo_mapper/railtie/database.rake"
|
13
|
+
end
|
14
|
+
|
15
|
+
initializer "mongo_mapper.set_configs" do |app|
|
16
|
+
ActiveSupport.on_load(:mongo_mapper) do
|
17
|
+
app.config.mongo_mapper.each do |k,v|
|
18
|
+
send "#{k}=", v
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# This sets the database configuration and establishes the connection.
|
24
|
+
initializer "mongo_mapper.initialize_database" do |app|
|
25
|
+
config_file = Rails.root.join('config/mongo.yml')
|
26
|
+
if config_file.file?
|
27
|
+
config = YAML.load(ERB.new(config_file.read).result)
|
28
|
+
MongoMapper.setup(config, Rails.env, :logger => Rails.logger)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Clear the identity map after each request
|
33
|
+
initializer "mongo_mapper.clear_identity_map" do |app|
|
34
|
+
app.config.middleware.use 'MongoMapper::Middleware::IdentityMap'
|
35
|
+
end
|
36
|
+
|
37
|
+
initializer "mongo_mapper.prepare_dispatcher" do |app|
|
38
|
+
# See http://groups.google.com/group/mongomapper/browse_thread/thread/68f62e8eda43b43a/4841dba76938290c
|
39
|
+
# to_prepare is called before each request in development mode and the first request in production.
|
40
|
+
ActionDispatch::Callbacks.to_prepare do
|
41
|
+
unless app.config.cache_classes
|
42
|
+
# Rails reloading was making descendants fill up and leak memory, these make sure they get cleared
|
43
|
+
ActiveSupport::DescendantsTracker.clear
|
44
|
+
MongoMapper::Plugins::IdentityMap.models.clear
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
namespace :db do
|
2
|
+
|
3
|
+
if not Rake::Task.task_defined?("db:drop")
|
4
|
+
desc 'Drops all the collections for the database for the current Rails.env'
|
5
|
+
task :drop => :environment do
|
6
|
+
MongoMapper.database.collections.select {|c| c.name !~ /system/ }.each(&:drop)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
if not Rake::Task.task_defined?("db:seed")
|
11
|
+
# if another ORM has defined db:seed, don't run it twice.
|
12
|
+
desc 'Load the seed data from db/seeds.rb'
|
13
|
+
task :seed => :environment do
|
14
|
+
seed_file = File.join(Rails.root, 'db', 'seeds.rb')
|
15
|
+
load(seed_file) if File.exist?(seed_file)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
if not Rake::Task.task_defined?("db:setup")
|
20
|
+
desc 'Create the database, and initialize with the seed data'
|
21
|
+
task :setup => [ 'db:create', 'db:seed' ]
|
22
|
+
end
|
23
|
+
|
24
|
+
if not Rake::Task.task_defined?("db:reseed")
|
25
|
+
desc 'Delete data and seed'
|
26
|
+
task :reseed => [ 'db:drop', 'db:seed' ]
|
27
|
+
end
|
28
|
+
|
29
|
+
if not Rake::Task.task_defined?("db:create")
|
30
|
+
task :create => :environment do
|
31
|
+
# noop
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
if not Rake::Task.task_defined?("db:migrate")
|
36
|
+
task :migrate => :environment do
|
37
|
+
# noop
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
if not Rake::Task.task_defined?("db:schema:load")
|
42
|
+
namespace :schema do
|
43
|
+
task :load do
|
44
|
+
# noop
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
if not Rake::Task.task_defined?("db:test:prepare")
|
50
|
+
namespace :test do
|
51
|
+
task :prepare => :environment do
|
52
|
+
MongoMapper.connect('test')
|
53
|
+
MongoMapper.database.collections.select {|c| c.name !~ /system/ }.each(&:drop)
|
54
|
+
MongoMapper.connect(Rails.env)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
task 'test:prepare' => 'db:test:prepare'
|
@@ -4,29 +4,29 @@ require 'set'
|
|
4
4
|
module MongoMapper
|
5
5
|
module Support
|
6
6
|
module DescendantAppends
|
7
|
-
def included(model)
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
def descendants
|
15
|
-
@descendants ||= Set.new
|
7
|
+
def included(model = nil, &block)
|
8
|
+
if model
|
9
|
+
extra_extensions.each { |extension| model.extend(extension) }
|
10
|
+
extra_inclusions.each { |inclusion| model.send(:include, inclusion) }
|
11
|
+
direct_descendants << model
|
12
|
+
end
|
13
|
+
super
|
16
14
|
end
|
17
15
|
|
18
16
|
# @api public
|
19
17
|
def append_extensions(*extensions)
|
18
|
+
warn "[DEPRECATED] append_extensions is deprecated. Use #plugin with a module that extends ActiveSupport::Concern."
|
20
19
|
extra_extensions.concat(extensions)
|
21
|
-
|
20
|
+
direct_descendants.each do |model|
|
22
21
|
extensions.each { |extension| model.extend(extension) }
|
23
22
|
end
|
24
23
|
end
|
25
24
|
|
26
25
|
# @api public
|
27
26
|
def append_inclusions(*inclusions)
|
27
|
+
warn "[DEPRECATED] append_inclusions is deprecated. Use #plugin with a module that extends ActiveSupport::Concern."
|
28
28
|
extra_inclusions.concat(inclusions)
|
29
|
-
|
29
|
+
direct_descendants.each do |model|
|
30
30
|
inclusions.each { |inclusion| model.send(:include, inclusion) }
|
31
31
|
end
|
32
32
|
end
|