thorsson-mongo_mapper 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +27 -0
  3. data/bin/mmconsole +60 -0
  4. data/examples/keys.rb +40 -0
  5. data/examples/modifiers/set.rb +25 -0
  6. data/examples/plugins.rb +41 -0
  7. data/examples/querying.rb +35 -0
  8. data/examples/scopes.rb +52 -0
  9. data/lib/mongo_mapper.rb +79 -0
  10. data/lib/mongo_mapper/connection.rb +83 -0
  11. data/lib/mongo_mapper/document.rb +41 -0
  12. data/lib/mongo_mapper/embedded_document.rb +31 -0
  13. data/lib/mongo_mapper/exceptions.rb +27 -0
  14. data/lib/mongo_mapper/extensions/array.rb +19 -0
  15. data/lib/mongo_mapper/extensions/binary.rb +22 -0
  16. data/lib/mongo_mapper/extensions/boolean.rb +44 -0
  17. data/lib/mongo_mapper/extensions/date.rb +25 -0
  18. data/lib/mongo_mapper/extensions/float.rb +14 -0
  19. data/lib/mongo_mapper/extensions/hash.rb +14 -0
  20. data/lib/mongo_mapper/extensions/integer.rb +19 -0
  21. data/lib/mongo_mapper/extensions/kernel.rb +9 -0
  22. data/lib/mongo_mapper/extensions/nil_class.rb +18 -0
  23. data/lib/mongo_mapper/extensions/object.rb +27 -0
  24. data/lib/mongo_mapper/extensions/object_id.rb +30 -0
  25. data/lib/mongo_mapper/extensions/set.rb +20 -0
  26. data/lib/mongo_mapper/extensions/string.rb +18 -0
  27. data/lib/mongo_mapper/extensions/time.rb +29 -0
  28. data/lib/mongo_mapper/plugins.rb +16 -0
  29. data/lib/mongo_mapper/plugins/accessible.rb +44 -0
  30. data/lib/mongo_mapper/plugins/associations.rb +97 -0
  31. data/lib/mongo_mapper/plugins/associations/base.rb +124 -0
  32. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +29 -0
  33. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +24 -0
  34. data/lib/mongo_mapper/plugins/associations/collection.rb +22 -0
  35. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +40 -0
  36. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +127 -0
  37. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  38. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +110 -0
  39. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +32 -0
  40. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +24 -0
  41. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +14 -0
  42. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +40 -0
  43. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +68 -0
  44. data/lib/mongo_mapper/plugins/associations/proxy.rb +126 -0
  45. data/lib/mongo_mapper/plugins/caching.rb +21 -0
  46. data/lib/mongo_mapper/plugins/callbacks.rb +241 -0
  47. data/lib/mongo_mapper/plugins/clone.rb +19 -0
  48. data/lib/mongo_mapper/plugins/descendants.rb +17 -0
  49. data/lib/mongo_mapper/plugins/dirty.rb +120 -0
  50. data/lib/mongo_mapper/plugins/document.rb +41 -0
  51. data/lib/mongo_mapper/plugins/dynamic_querying.rb +43 -0
  52. data/lib/mongo_mapper/plugins/dynamic_querying/dynamic_finder.rb +44 -0
  53. data/lib/mongo_mapper/plugins/embedded_document.rb +49 -0
  54. data/lib/mongo_mapper/plugins/equality.rb +17 -0
  55. data/lib/mongo_mapper/plugins/identity_map.rb +128 -0
  56. data/lib/mongo_mapper/plugins/indexes.rb +12 -0
  57. data/lib/mongo_mapper/plugins/inspect.rb +15 -0
  58. data/lib/mongo_mapper/plugins/keys.rb +309 -0
  59. data/lib/mongo_mapper/plugins/keys/key.rb +55 -0
  60. data/lib/mongo_mapper/plugins/logger.rb +18 -0
  61. data/lib/mongo_mapper/plugins/modifiers.rb +112 -0
  62. data/lib/mongo_mapper/plugins/pagination.rb +14 -0
  63. data/lib/mongo_mapper/plugins/persistence.rb +69 -0
  64. data/lib/mongo_mapper/plugins/protected.rb +53 -0
  65. data/lib/mongo_mapper/plugins/querying.rb +176 -0
  66. data/lib/mongo_mapper/plugins/querying/decorator.rb +46 -0
  67. data/lib/mongo_mapper/plugins/querying/plucky_methods.rb +15 -0
  68. data/lib/mongo_mapper/plugins/rails.rb +58 -0
  69. data/lib/mongo_mapper/plugins/safe.rb +28 -0
  70. data/lib/mongo_mapper/plugins/sci.rb +32 -0
  71. data/lib/mongo_mapper/plugins/scopes.rb +21 -0
  72. data/lib/mongo_mapper/plugins/serialization.rb +76 -0
  73. data/lib/mongo_mapper/plugins/timestamps.rb +22 -0
  74. data/lib/mongo_mapper/plugins/userstamps.rb +15 -0
  75. data/lib/mongo_mapper/plugins/validations.rb +50 -0
  76. data/lib/mongo_mapper/support/descendant_appends.rb +45 -0
  77. data/lib/mongo_mapper/version.rb +4 -0
  78. data/test/_NOTE_ON_TESTING +1 -0
  79. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +63 -0
  80. data/test/functional/associations/test_belongs_to_proxy.rb +93 -0
  81. data/test/functional/associations/test_in_array_proxy.rb +319 -0
  82. data/test/functional/associations/test_many_documents_as_proxy.rb +229 -0
  83. data/test/functional/associations/test_many_documents_proxy.rb +575 -0
  84. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +176 -0
  85. data/test/functional/associations/test_many_embedded_proxy.rb +256 -0
  86. data/test/functional/associations/test_many_polymorphic_proxy.rb +302 -0
  87. data/test/functional/associations/test_one_embedded_proxy.rb +67 -0
  88. data/test/functional/associations/test_one_proxy.rb +182 -0
  89. data/test/functional/test_accessible.rb +168 -0
  90. data/test/functional/test_associations.rb +44 -0
  91. data/test/functional/test_binary.rb +27 -0
  92. data/test/functional/test_caching.rb +76 -0
  93. data/test/functional/test_callbacks.rb +151 -0
  94. data/test/functional/test_dirty.rb +163 -0
  95. data/test/functional/test_document.rb +253 -0
  96. data/test/functional/test_dynamic_querying.rb +75 -0
  97. data/test/functional/test_embedded_document.rb +210 -0
  98. data/test/functional/test_identity_map.rb +506 -0
  99. data/test/functional/test_indexes.rb +42 -0
  100. data/test/functional/test_logger.rb +20 -0
  101. data/test/functional/test_modifiers.rb +416 -0
  102. data/test/functional/test_pagination.rb +91 -0
  103. data/test/functional/test_protected.rb +175 -0
  104. data/test/functional/test_querying.rb +873 -0
  105. data/test/functional/test_safe.rb +76 -0
  106. data/test/functional/test_sci.rb +230 -0
  107. data/test/functional/test_scopes.rb +171 -0
  108. data/test/functional/test_string_id_compatibility.rb +67 -0
  109. data/test/functional/test_timestamps.rb +62 -0
  110. data/test/functional/test_userstamps.rb +27 -0
  111. data/test/functional/test_validations.rb +342 -0
  112. data/test/models.rb +233 -0
  113. data/test/test_active_model_lint.rb +13 -0
  114. data/test/test_helper.rb +104 -0
  115. data/test/unit/associations/test_base.rb +212 -0
  116. data/test/unit/associations/test_proxy.rb +105 -0
  117. data/test/unit/serializers/test_json_serializer.rb +202 -0
  118. data/test/unit/test_clone.rb +69 -0
  119. data/test/unit/test_descendant_appends.rb +71 -0
  120. data/test/unit/test_document.rb +213 -0
  121. data/test/unit/test_dynamic_finder.rb +125 -0
  122. data/test/unit/test_embedded_document.rb +644 -0
  123. data/test/unit/test_extensions.rb +380 -0
  124. data/test/unit/test_key.rb +185 -0
  125. data/test/unit/test_keys.rb +55 -0
  126. data/test/unit/test_mongo_mapper.rb +110 -0
  127. data/test/unit/test_pagination.rb +11 -0
  128. data/test/unit/test_plugins.rb +50 -0
  129. data/test/unit/test_rails.rb +181 -0
  130. data/test/unit/test_rails_compatibility.rb +52 -0
  131. data/test/unit/test_serialization.rb +51 -0
  132. data/test/unit/test_time_zones.rb +39 -0
  133. data/test/unit/test_validations.rb +544 -0
  134. metadata +316 -0
@@ -0,0 +1,46 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Querying
5
+ module Decorator
6
+ include DynamicQuerying::ClassMethods
7
+
8
+ def model(model=nil)
9
+ return @model if model.nil?
10
+ @model = model
11
+ self
12
+ end
13
+
14
+ def find!(*ids)
15
+ raise DocumentNotFound, "Couldn't find without an ID" if ids.size == 0
16
+
17
+ find(*ids).tap do |result|
18
+ if result.nil? || ids.size != Array(result).size
19
+ raise DocumentNotFound, "Couldn't find all of the ids (#{ids.join(',')}). Found #{Array(result).size}, but was expecting #{ids.size}"
20
+ end
21
+ end
22
+ end
23
+
24
+ def all(opts={})
25
+ super.map { |doc| model.load(doc) }
26
+ end
27
+
28
+ def first(opts={})
29
+ model.load(super)
30
+ end
31
+
32
+ def last(opts={})
33
+ model.load(super)
34
+ end
35
+
36
+ private
37
+ def method_missing(method, *args, &block)
38
+ return super unless model.respond_to?(method)
39
+ result = model.send(method, *args, &block)
40
+ return super unless result.is_a?(Plucky::Query)
41
+ merge(result)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: UTF-8
2
+ require 'forwardable'
3
+
4
+ module MongoMapper
5
+ module Plugins
6
+ module Querying
7
+ module PluckyMethods
8
+ extend Forwardable
9
+ def_delegators :query, :where, :fields, :limit, :skip, :sort,
10
+ :count, :last, :first, :all, :paginate,
11
+ :find, :find!, :exists?, :exist?, :find_each
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Rails
5
+ def self.configure(model)
6
+ model.extend ActiveModel::Naming if defined?(ActiveModel)
7
+ end
8
+
9
+ module InstanceMethods
10
+ def to_param
11
+ id.to_s if persisted?
12
+ end
13
+
14
+ def to_model
15
+ self
16
+ end
17
+
18
+ def to_key
19
+ [id] if persisted?
20
+ end
21
+
22
+ def new_record?
23
+ new?
24
+ end
25
+
26
+ def read_attribute(name)
27
+ self[name]
28
+ end
29
+
30
+ def read_attribute_before_typecast(name)
31
+ read_key_before_typecast(name)
32
+ end
33
+
34
+ def write_attribute(name, value)
35
+ self[name] = value
36
+ end
37
+ end
38
+
39
+ module ClassMethods
40
+ def has_one(*args)
41
+ one(*args)
42
+ end
43
+
44
+ def has_many(*args)
45
+ many(*args)
46
+ end
47
+
48
+ def column_names
49
+ keys.keys
50
+ end
51
+
52
+ def human_name
53
+ self.name.demodulize.titleize
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Safe
5
+ module ClassMethods
6
+ def inherited(subclass)
7
+ super
8
+ subclass.safe if safe?
9
+ end
10
+
11
+ def safe
12
+ @safe = true
13
+ end
14
+
15
+ def safe?
16
+ @safe == true
17
+ end
18
+ end
19
+
20
+ module InstanceMethods
21
+ def save_to_collection(options={})
22
+ options[:safe] = self.class.safe? unless options.key?(:safe)
23
+ super
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,32 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Sci
5
+ module ClassMethods
6
+ def inherited(subclass)
7
+ key :_type, String unless key?(:_type)
8
+ subclass.instance_variable_set("@single_collection_inherited", true)
9
+ subclass.set_collection_name(collection_name) unless subclass.embeddable?
10
+ super
11
+ end
12
+
13
+ def single_collection_inherited?
14
+ @single_collection_inherited == true
15
+ end
16
+
17
+ def query(options={})
18
+ super.tap do |query|
19
+ query[:_type] = name if single_collection_inherited?
20
+ end
21
+ end
22
+ end
23
+
24
+ module InstanceMethods
25
+ def initialize(*args)
26
+ super
27
+ write_key :_type, self.class.name if self.class.key?(:_type)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Scopes
5
+ module ClassMethods
6
+ def scope(name, scope_options={})
7
+ scopes[name] = lambda do |*args|
8
+ result = scope_options.is_a?(Proc) ? scope_options.call(*args) : scope_options
9
+ result = self.query(result) if result.is_a?(Hash)
10
+ self.query.merge(result)
11
+ end
12
+ singleton_class.send :define_method, name, &scopes[name]
13
+ end
14
+
15
+ def scopes
16
+ read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,76 @@
1
+ # encoding: UTF-8
2
+ require 'active_support/json'
3
+
4
+ module MongoMapper
5
+ module Plugins
6
+ module Serialization
7
+ def self.configure(model)
8
+ model.class_eval { cattr_accessor :include_root_in_json, :instance_writer => true }
9
+ end
10
+
11
+ module InstanceMethods
12
+ def as_json options={}
13
+ options ||= {}
14
+ unless options[:only]
15
+ methods = [options.delete(:methods)].flatten.compact
16
+ methods << :id
17
+ options[:methods] = methods.uniq
18
+ end
19
+
20
+ except = [options.delete(:except)].flatten.compact
21
+ except << :_id
22
+ options[:except] = except
23
+
24
+ # Direct rip from Rails 3 ActiveModel Serialization (#serializable_hash)
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 }
28
+
29
+ attribute_names = attributes.keys.sort
30
+ if options[:only].any?
31
+ attribute_names &= options[:only]
32
+ elsif options[:except].any?
33
+ attribute_names -= options[:except]
34
+ end
35
+
36
+ method_names = Array.wrap(options[:methods]).inject([]) do |methods, name|
37
+ methods << name if respond_to?(name.to_s)
38
+ methods
39
+ end
40
+
41
+ (attribute_names + method_names).inject({}) { |hash, name|
42
+ hash[name] = send(name)
43
+ hash
44
+ }
45
+ end
46
+ # End rip
47
+
48
+ options.delete(:only) if options[:only].nil? or options[:only].empty?
49
+
50
+ hash.each do |key, value|
51
+ if value.is_a?(Array)
52
+ hash[key] = value.map do |item|
53
+ item.respond_to?(:as_json) ? item.as_json(options) : item
54
+ end
55
+ elsif value.is_a? BSON::ObjectID
56
+ hash[key] = value.to_s
57
+ elsif value.respond_to?(:as_json)
58
+ hash[key] = value.as_json(options)
59
+ end
60
+ end
61
+
62
+ # Replicate Rails 3 naming - and also bin anytihng after : for use in our dynamic classes from unit tests
63
+ hash = { ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self)).gsub(/:.*/,'') => hash } if include_root_in_json
64
+ hash
65
+ end
66
+ end
67
+
68
+ module ClassMethods
69
+ def from_json(json)
70
+ self.new(ActiveSupport::JSON.decode(json))
71
+ end
72
+ end
73
+
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Timestamps
5
+ module ClassMethods
6
+ def timestamps!
7
+ key :created_at, Time
8
+ key :updated_at, Time
9
+ class_eval { before_save :update_timestamps }
10
+ end
11
+ end
12
+
13
+ module InstanceMethods
14
+ def update_timestamps
15
+ now = Time.now.utc
16
+ self[:created_at] = now if new? && !created_at?
17
+ self[:updated_at] = now
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Userstamps
5
+ module ClassMethods
6
+ def userstamps!
7
+ key :creator_id, ObjectId
8
+ key :updater_id, ObjectId
9
+ belongs_to :creator, :class_name => 'User'
10
+ belongs_to :updater, :class_name => 'User'
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,50 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Validations
5
+ def self.configure(model)
6
+ model.class_eval do
7
+ include Validatable
8
+ extend Validations::DocumentMacros
9
+ end
10
+ end
11
+
12
+ module DocumentMacros
13
+ def validates_uniqueness_of(*args)
14
+ add_validations(args, Validations::ValidatesUniquenessOf)
15
+ end
16
+ end
17
+
18
+ class ValidatesUniquenessOf < Validatable::ValidationBase
19
+ option :scope, :case_sensitive
20
+ default :case_sensitive => true
21
+
22
+ def valid?(instance)
23
+ value = instance[attribute]
24
+ return true if allow_blank && value.blank?
25
+ return true if allow_nil && value.nil?
26
+ base_conditions = case_sensitive ? {self.attribute => value} : {}
27
+ doc = instance.class.first(base_conditions.merge(scope_conditions(instance)).merge(where_conditions(instance)))
28
+ doc.nil? || instance._id == doc._id
29
+ end
30
+
31
+ def message(instance)
32
+ super || "has already been taken"
33
+ end
34
+
35
+ def scope_conditions(instance)
36
+ return {} unless scope
37
+ Array(scope).inject({}) do |conditions, key|
38
+ conditions.merge(key => instance[key])
39
+ end
40
+ end
41
+
42
+ def where_conditions(instance)
43
+ conditions = {}
44
+ conditions[attribute] = /^#{Regexp.escape(instance[attribute].to_s)}$/i unless case_sensitive
45
+ conditions
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,45 @@
1
+ # encoding: UTF-8
2
+ require 'set'
3
+
4
+ module MongoMapper
5
+ module Support
6
+ module DescendantAppends
7
+ def included(model)
8
+ extra_extensions.each { |extension| model.extend(extension) }
9
+ extra_inclusions.each { |inclusion| model.send(:include, inclusion) }
10
+ descendants << model
11
+ end
12
+
13
+ # @api public
14
+ def descendants
15
+ @descendants ||= Set.new
16
+ end
17
+
18
+ # @api public
19
+ def append_extensions(*extensions)
20
+ extra_extensions.concat(extensions)
21
+ descendants.each do |model|
22
+ extensions.each { |extension| model.extend(extension) }
23
+ end
24
+ end
25
+
26
+ # @api public
27
+ def append_inclusions(*inclusions)
28
+ extra_inclusions.concat(inclusions)
29
+ descendants.each do |model|
30
+ inclusions.each { |inclusion| model.send(:include, inclusion) }
31
+ end
32
+ end
33
+
34
+ # @api private
35
+ def extra_extensions
36
+ @extra_extensions ||= []
37
+ end
38
+
39
+ # @api private
40
+ def extra_inclusions
41
+ @extra_inclusions ||= []
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,4 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ Version = '0.8.2'
4
+ end
@@ -0,0 +1 @@
1
+ I am doing my best to keep unit and functional tests separate. As I see them, functional tests hit the database and should never care about internals. Unit tests do not hit the database.
@@ -0,0 +1,63 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class BelongsToPolymorphicProxyTest < Test::Unit::TestCase
5
+ def setup
6
+ Status.collection.remove
7
+ Project.collection.remove
8
+ end
9
+
10
+ should "default to nil" do
11
+ status = Status.new
12
+ status.target.nil?.should be_true
13
+ status.target.inspect.should == "nil"
14
+ end
15
+
16
+ should "have boolean presence method" do
17
+ status = Status.new
18
+ status.target?.should be_false
19
+
20
+ status.target = Project.new(:name => 'mongomapper')
21
+ status.target?.should be_true
22
+ end
23
+
24
+ should "be able to replace the association" do
25
+ status = Status.new(:name => 'Foo!')
26
+ project = Project.new(:name => "mongomapper")
27
+ status.target = project
28
+ status.save.should be_true
29
+
30
+ status = status.reload
31
+ status.target.nil?.should be_false
32
+ status.target_id.should == project._id
33
+ status.target_type.should == "Project"
34
+ status.target.name.should == "mongomapper"
35
+ end
36
+
37
+ should "unset the association" do
38
+ status = Status.new(:name => 'Foo!')
39
+ project = Project.new(:name => "mongomapper")
40
+ status.target = project
41
+ status.save.should be_true
42
+
43
+ status = status.reload
44
+ status.target = nil
45
+ status.target_type.nil?.should be_true
46
+ status.target_id.nil?.should be_true
47
+ status.target.nil?.should be_true
48
+ end
49
+
50
+ context "association id set but document not found" do
51
+ setup do
52
+ @status = Status.new(:name => 'Foo!')
53
+ project = Project.new(:name => "mongomapper")
54
+ @status.target = project
55
+ @status.save.should be_true
56
+ project.destroy
57
+ end
58
+
59
+ should "return nil instead of raising error" do
60
+ @status.target.nil?.should be_true
61
+ end
62
+ end
63
+ end