mongo_doc_rails2 0.6.1

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 (142) hide show
  1. data/.document +5 -0
  2. data/.gitignore +8 -0
  3. data/HISTORY.md +11 -0
  4. data/LICENSE +20 -0
  5. data/README.textile +185 -0
  6. data/Rakefile +188 -0
  7. data/TODO +40 -0
  8. data/VERSION +1 -0
  9. data/data/.gitignore +2 -0
  10. data/examples/simple_document.rb +46 -0
  11. data/examples/simple_object.rb +34 -0
  12. data/features/collections.feature +9 -0
  13. data/features/embed_hash.feature +16 -0
  14. data/features/finders.feature +76 -0
  15. data/features/indexes.feature +28 -0
  16. data/features/mongodb.yml +7 -0
  17. data/features/mongodoc_base.feature +128 -0
  18. data/features/new_record.feature +36 -0
  19. data/features/partial_updates.feature +95 -0
  20. data/features/removing_documents.feature +68 -0
  21. data/features/saving_an_object.feature +15 -0
  22. data/features/scopes.feature +66 -0
  23. data/features/step_definitions/collection_steps.rb +17 -0
  24. data/features/step_definitions/document_steps.rb +149 -0
  25. data/features/step_definitions/documents.rb +40 -0
  26. data/features/step_definitions/embed_hash_steps.rb +6 -0
  27. data/features/step_definitions/finder_steps.rb +15 -0
  28. data/features/step_definitions/index_steps.rb +10 -0
  29. data/features/step_definitions/json_steps.rb +9 -0
  30. data/features/step_definitions/object_steps.rb +50 -0
  31. data/features/step_definitions/objects.rb +24 -0
  32. data/features/step_definitions/partial_update_steps.rb +31 -0
  33. data/features/step_definitions/query_steps.rb +66 -0
  34. data/features/step_definitions/removing_documents_steps.rb +14 -0
  35. data/features/step_definitions/scope_steps.rb +18 -0
  36. data/features/step_definitions/string_casting_steps.rb +29 -0
  37. data/features/step_definitions/util_steps.rb +7 -0
  38. data/features/string_casting.feature +10 -0
  39. data/features/support/support.rb +10 -0
  40. data/features/using_criteria.feature +142 -0
  41. data/lib/mongo_doc.rb +12 -0
  42. data/lib/mongo_doc/associations.rb +109 -0
  43. data/lib/mongo_doc/associations/collection_proxy.rb +121 -0
  44. data/lib/mongo_doc/associations/document_proxy.rb +65 -0
  45. data/lib/mongo_doc/associations/hash_proxy.rb +102 -0
  46. data/lib/mongo_doc/associations/proxy_base.rb +48 -0
  47. data/lib/mongo_doc/attributes.rb +84 -0
  48. data/lib/mongo_doc/bson.rb +31 -0
  49. data/lib/mongo_doc/collection.rb +82 -0
  50. data/lib/mongo_doc/connection.rb +88 -0
  51. data/lib/mongo_doc/contexts.rb +31 -0
  52. data/lib/mongo_doc/contexts/ids.rb +41 -0
  53. data/lib/mongo_doc/contexts/mongo.rb +272 -0
  54. data/lib/mongo_doc/criteria.rb +70 -0
  55. data/lib/mongo_doc/cursor.rb +32 -0
  56. data/lib/mongo_doc/document.rb +205 -0
  57. data/lib/mongo_doc/ext.rb +16 -0
  58. data/lib/mongo_doc/ext/array.rb +5 -0
  59. data/lib/mongo_doc/ext/binary.rb +7 -0
  60. data/lib/mongo_doc/ext/boolean_class.rb +17 -0
  61. data/lib/mongo_doc/ext/date.rb +19 -0
  62. data/lib/mongo_doc/ext/date_time.rb +17 -0
  63. data/lib/mongo_doc/ext/dbref.rb +7 -0
  64. data/lib/mongo_doc/ext/hash.rb +7 -0
  65. data/lib/mongo_doc/ext/min_max_keys.rb +13 -0
  66. data/lib/mongo_doc/ext/nil_class.rb +5 -0
  67. data/lib/mongo_doc/ext/numeric.rb +17 -0
  68. data/lib/mongo_doc/ext/object.rb +19 -0
  69. data/lib/mongo_doc/ext/object_id.rb +7 -0
  70. data/lib/mongo_doc/ext/regexp.rb +5 -0
  71. data/lib/mongo_doc/ext/string.rb +5 -0
  72. data/lib/mongo_doc/ext/symbol.rb +5 -0
  73. data/lib/mongo_doc/ext/time.rb +9 -0
  74. data/lib/mongo_doc/finders.rb +38 -0
  75. data/lib/mongo_doc/index.rb +46 -0
  76. data/lib/mongo_doc/matchers.rb +35 -0
  77. data/lib/mongo_doc/root.rb +26 -0
  78. data/lib/mongo_doc/scope.rb +64 -0
  79. data/lib/mongo_doc/validations.rb +12 -0
  80. data/lib/mongo_doc/validations/macros.rb +11 -0
  81. data/lib/mongo_doc/validations/validates_embedded.rb +13 -0
  82. data/lib/mongoid/contexts/enumerable.rb +151 -0
  83. data/lib/mongoid/contexts/paging.rb +42 -0
  84. data/lib/mongoid/criteria.rb +239 -0
  85. data/lib/mongoid/criterion/complex.rb +21 -0
  86. data/lib/mongoid/criterion/exclusion.rb +65 -0
  87. data/lib/mongoid/criterion/inclusion.rb +93 -0
  88. data/lib/mongoid/criterion/optional.rb +136 -0
  89. data/lib/mongoid/extensions/hash/criteria_helpers.rb +20 -0
  90. data/lib/mongoid/extensions/symbol/inflections.rb +36 -0
  91. data/lib/mongoid/matchers/all.rb +11 -0
  92. data/lib/mongoid/matchers/default.rb +26 -0
  93. data/lib/mongoid/matchers/exists.rb +13 -0
  94. data/lib/mongoid/matchers/gt.rb +11 -0
  95. data/lib/mongoid/matchers/gte.rb +11 -0
  96. data/lib/mongoid/matchers/in.rb +11 -0
  97. data/lib/mongoid/matchers/lt.rb +11 -0
  98. data/lib/mongoid/matchers/lte.rb +11 -0
  99. data/lib/mongoid/matchers/ne.rb +11 -0
  100. data/lib/mongoid/matchers/nin.rb +11 -0
  101. data/lib/mongoid/matchers/size.rb +11 -0
  102. data/mongo_doc_rails2.gemspec +237 -0
  103. data/mongod.example.yml +2 -0
  104. data/mongodb.example.yml +14 -0
  105. data/perf/mongo_doc_object.rb +83 -0
  106. data/perf/mongo_document.rb +84 -0
  107. data/perf/ruby_driver.rb +49 -0
  108. data/script/console +8 -0
  109. data/spec/array_including_argument_matcher.rb +62 -0
  110. data/spec/associations/collection_proxy_spec.rb +233 -0
  111. data/spec/associations/document_proxy_spec.rb +45 -0
  112. data/spec/associations/hash_proxy_spec.rb +181 -0
  113. data/spec/associations/proxy_base_spec.rb +92 -0
  114. data/spec/associations_spec.rb +218 -0
  115. data/spec/attributes_accessor_spec.rb +33 -0
  116. data/spec/attributes_spec.rb +145 -0
  117. data/spec/bson_matchers.rb +54 -0
  118. data/spec/bson_spec.rb +196 -0
  119. data/spec/collection_spec.rb +169 -0
  120. data/spec/connection_spec.rb +147 -0
  121. data/spec/contexts/ids_spec.rb +49 -0
  122. data/spec/contexts/mongo_spec.rb +235 -0
  123. data/spec/contexts_spec.rb +56 -0
  124. data/spec/criteria_spec.rb +69 -0
  125. data/spec/cursor_spec.rb +91 -0
  126. data/spec/document_ext.rb +9 -0
  127. data/spec/document_spec.rb +553 -0
  128. data/spec/embedded_save_spec.rb +73 -0
  129. data/spec/ext_spec.rb +89 -0
  130. data/spec/finders_spec.rb +61 -0
  131. data/spec/hash_matchers.rb +27 -0
  132. data/spec/index_spec.rb +79 -0
  133. data/spec/matchers_spec.rb +342 -0
  134. data/spec/mongodb.yml +6 -0
  135. data/spec/mongodb_pairs.yml +8 -0
  136. data/spec/new_record_spec.rb +128 -0
  137. data/spec/root_spec.rb +41 -0
  138. data/spec/scope_spec.rb +79 -0
  139. data/spec/spec.opts +2 -0
  140. data/spec/spec_helper.rb +14 -0
  141. data/spec/validations_spec.rb +30 -0
  142. metadata +346 -0
@@ -0,0 +1,121 @@
1
+ # Thanks Sandro!
2
+ # http://github.com/sandro
3
+ module MongoDoc
4
+ module Associations
5
+ class CollectionProxy
6
+ include ProxyBase
7
+
8
+ # List of array methods (that are not in +Object+) that need to be
9
+ # delegated to +collection+.
10
+ ARRAY_METHODS = (Array.instance_methods - Object.instance_methods).map { |n| n.to_s }
11
+
12
+ # List of additional methods that must be delegated to +collection+.
13
+ MUST_DEFINE = %w[to_a to_ary inspect to_bson ==]
14
+
15
+ DO_NOT_DEFINE = %w[concat insert replace]
16
+
17
+ (ARRAY_METHODS + MUST_DEFINE - DO_NOT_DEFINE).uniq.each do |method|
18
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
19
+ def #{method}(*args, &block) # def each(*args, &block)
20
+ collection.send(:#{method}, *args, &block) # collection.send(:each, *args, &block)
21
+ end # end
22
+ RUBY
23
+ end
24
+
25
+ attr_reader :collection
26
+
27
+ def _modifier_path=(path)
28
+ super
29
+ collection.each do |item|
30
+ item._modifier_path = _modifier_path + '.$' if ProxyBase.is_document?(item)
31
+ end
32
+ end
33
+
34
+ def _root=(value)
35
+ @_root = value
36
+ collection.each do |item|
37
+ item._root = value if ProxyBase.is_document?(item)
38
+ end
39
+ end
40
+
41
+ %w(_root _selector_path).each do |setter|
42
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
43
+ def #{setter}=(val)
44
+ super
45
+ collection.each do |item|
46
+ item.#{setter} = #{setter} if ProxyBase.is_document?(item)
47
+ end
48
+ end
49
+ RUBY
50
+ end
51
+
52
+ def initialize(options)
53
+ @collection = []
54
+ super
55
+ end
56
+
57
+ alias _append <<
58
+ def <<(item)
59
+ attach(item)
60
+ _append item
61
+ self
62
+ end
63
+ alias push <<
64
+
65
+ alias add []=
66
+ def []=(index, item)
67
+ attach(item)
68
+ add(index, item)
69
+ end
70
+ alias insert []=
71
+
72
+ def build(attrs)
73
+ item = _assoc_class.new(attrs)
74
+ push(item)
75
+ end
76
+
77
+ def concat(array)
78
+ array.each do |item|
79
+ push(item)
80
+ end
81
+ end
82
+
83
+ # Lie about our class. Borrowed from Rake::FileList
84
+ # Note: Does not work for case equality (<tt>===</tt>)
85
+ def is_a?(klass)
86
+ klass == Array || super(klass)
87
+ end
88
+ alias kind_of? is_a?
89
+
90
+ def replace(other)
91
+ clear
92
+ concat(other)
93
+ end
94
+
95
+ alias _unshift unshift
96
+ def unshift(item)
97
+ attach(item)
98
+ _unshift(item)
99
+ end
100
+
101
+ def valid?
102
+ all? do |child|
103
+ if ProxyBase.is_document?(child)
104
+ child.valid?
105
+ else
106
+ true
107
+ end
108
+ end
109
+ end
110
+
111
+ protected
112
+
113
+ def attach_document(doc)
114
+ doc._modifier_path = _modifier_path + '.$'
115
+ doc._selector_path = _selector_path
116
+ doc._root = _root
117
+ _root.send(:register_save_observer, doc)
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,65 @@
1
+ module MongoDoc
2
+ module Associations
3
+ class DocumentProxy
4
+ include ProxyBase
5
+
6
+ attr_reader :document
7
+
8
+ delegate :to_bson, :id, :to => :document
9
+
10
+ %w(_modifier_path _selector_path).each do |setter|
11
+ class_eval(<<-RUBY, __FILE__, __LINE__)
12
+ def #{setter}=(path)
13
+ super
14
+ document.#{setter} = #{setter} if ProxyBase.is_document?(document)
15
+ end
16
+ RUBY
17
+ end
18
+
19
+ def _root=(value)
20
+ @_root = value
21
+ document._root = value if ProxyBase.is_document?(document)
22
+ end
23
+
24
+ def ==(other)
25
+ if self.class === other
26
+ document == other.document
27
+ else
28
+ document == other
29
+ end
30
+ end
31
+
32
+ def build(attrs)
33
+ item = _assoc_class.new(attrs)
34
+ self.document = item
35
+ end
36
+
37
+ def document=(doc)
38
+ attach(doc)
39
+ @document = doc
40
+ end
41
+
42
+ def valid?
43
+ if ProxyBase.is_document?(document)
44
+ document.valid?
45
+ else
46
+ true
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def method_missing(method, *args)
53
+ unless document.respond_to?(method)
54
+ raise NoMethodError, "undefined method `#{method.to_s}' for proxied \"#{document}\":#{document.class.to_s}"
55
+ end
56
+
57
+ if block_given?
58
+ document.send(method, *args) { |*block_args| yield(*block_args) }
59
+ else
60
+ document.send(method, *args)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,102 @@
1
+ module MongoDoc
2
+ class InvalidEmbeddedHashKey < RuntimeError; end
3
+
4
+ module Associations
5
+ class HashProxy
6
+ include ProxyBase
7
+
8
+ HASH_METHODS = (Hash.instance_methods - Object.instance_methods).map { |n| n.to_s }
9
+
10
+ MUST_DEFINE = %w[to_a inspect to_bson ==]
11
+
12
+ DO_NOT_DEFINE = %w[merge! replace store update]
13
+
14
+ (HASH_METHODS + MUST_DEFINE - DO_NOT_DEFINE).uniq.each do |method|
15
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
16
+ def #{method}(*args, &block) # def each(*args, &block)
17
+ hash.send(:#{method}, *args, &block) # hash.send(:each, *args, &block)
18
+ end # end
19
+ RUBY
20
+ end
21
+
22
+ attr_reader :hash
23
+
24
+ %w(_modifier_path _selector_path).each do |setter|
25
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
26
+ def #{setter}=(path)
27
+ super
28
+ hash.each do |key, doc|
29
+ doc.#{setter} = #{setter} + '.' + key.to_s if ProxyBase.is_document?(doc)
30
+ end
31
+ end
32
+ RUBY
33
+ end
34
+
35
+ def initialize(options)
36
+ @hash = {}
37
+ super
38
+ end
39
+
40
+ alias put []=
41
+ def []=(key, value)
42
+ raise InvalidEmbeddedHashKey.new("Key name [#{key}] must be a valid element name, see http://www.mongodb.org/display/DOCS/BSON#BSON-noteonelementname") unless valid_key?(key)
43
+ put(key, attach(key, value))
44
+ end
45
+ alias store []=
46
+
47
+ def build(key, attrs)
48
+ item = _assoc_class.new(attrs)
49
+ store(key, item)
50
+ end
51
+
52
+ # Lie about our class. Borrowed from Rake::FileList
53
+ # Note: Does not work for case equality (<tt>===</tt>)
54
+ def is_a?(klass)
55
+ klass == Hash || super(klass)
56
+ end
57
+ alias kind_of? is_a?
58
+
59
+ def merge!(other)
60
+ other.each_pair do |key, value|
61
+ self[key] = if block_given?
62
+ yield key, [key], value
63
+ else
64
+ value
65
+ end
66
+ end
67
+ end
68
+ alias update merge!
69
+
70
+ def replace(other)
71
+ clear
72
+ merge!(other)
73
+ end
74
+
75
+ def valid?
76
+ values.all? do |child|
77
+ if ProxyBase.is_document?(child)
78
+ child.valid?
79
+ else
80
+ true
81
+ end
82
+ end
83
+ end
84
+
85
+ protected
86
+
87
+ def attach(key, value)
88
+ if ProxyBase.is_document?(value)
89
+ proxy = DocumentProxy.new(:path => _selector_path, :assoc_name => key, :root => _root, :parent => self)
90
+ proxy.document = value
91
+ proxy
92
+ else
93
+ value
94
+ end
95
+ end
96
+
97
+ def valid_key?(key)
98
+ (String === key or Symbol === key) and key.to_s !~ /(_id|query|\$.*|.*\..*)/
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,48 @@
1
+ module MongoDoc
2
+ module Associations
3
+ module ProxyBase
4
+ def self.included(klass)
5
+ klass.class_eval do
6
+ attr_reader :_assoc_class, :_assoc_name, :_modifier_path, :_root, :_selector_path
7
+ end
8
+ end
9
+
10
+ %w(_modifier_path _selector_path).each do |setter|
11
+ module_eval(<<-RUBY, __FILE__, __LINE__)
12
+ def #{setter}=(path)
13
+ @#{setter} = (path.blank? ? '' : path + '.') + _assoc_name.to_s
14
+ end
15
+ RUBY
16
+ end
17
+
18
+ def _root=(root)
19
+ @_root = root
20
+ end
21
+
22
+ def initialize(options)
23
+ @_assoc_name = options[:assoc_name]
24
+ @_assoc_class = options[:assoc_class]
25
+ self._root = options[:root]
26
+ self._selector_path = self._modifier_path = options[:path]
27
+ end
28
+
29
+ def self.is_document?(object)
30
+ object.respond_to?(:_root)
31
+ end
32
+
33
+ protected
34
+
35
+ def attach(obj)
36
+ attach_document(obj) if ProxyBase.is_document?(obj)
37
+ obj
38
+ end
39
+
40
+ def attach_document(doc)
41
+ doc._modifier_path = _modifier_path
42
+ doc._selector_path = _selector_path
43
+ doc._root = _root
44
+ _root.send(:register_save_observer, doc)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,84 @@
1
+ module MongoDoc
2
+ module Attributes
3
+ def self.included(klass)
4
+ klass.class_eval do
5
+ class_inheritable_array :_keys
6
+ self._keys = []
7
+ class_inheritable_array :_associations
8
+ self._associations = []
9
+
10
+ attr_accessor :_id
11
+
12
+ extend ClassMethods
13
+ end
14
+ end
15
+
16
+ def attributes
17
+ hash = {}
18
+ self.class._attributes.each do |attr|
19
+ hash[attr] = send(attr)
20
+ end
21
+ hash
22
+ end
23
+
24
+ def attributes=(attrs)
25
+ attrs.each do |key, value|
26
+ send("#{key}=", value)
27
+ end
28
+ end
29
+
30
+ module ClassMethods
31
+
32
+ def self.extended(klass)
33
+ klass.class_eval do
34
+ singleton_class.alias_method_chain :attr_accessor, :mongo
35
+ end
36
+ end
37
+
38
+ def _attributes
39
+ _keys + _associations
40
+ end
41
+
42
+ def attr_accessor_with_mongo(*args)
43
+ opts = args.extract_options!
44
+ default = opts.delete(:default)
45
+ type = opts.delete(:type)
46
+ args.each do |name|
47
+ _keys << name unless _keys.include?(name)
48
+ attr_writer name
49
+
50
+ if default
51
+ define_method("_default_#{name}", default.kind_of?(Proc) ? default : proc { default })
52
+ private "_default_#{name}"
53
+
54
+ module_eval(<<-RUBY, __FILE__, __LINE__)
55
+ def #{name} # def birth_date
56
+ unless defined? @#{name} # unless defined? @birth_date
57
+ @#{name} = _default_#{name} # @birth_date = _default_birth_date
58
+ end # end
59
+ class << self; attr_reader :#{name} end # class << self; attr_reader :birth_date end
60
+ @#{name} # @birth_date
61
+ end # end
62
+ RUBY
63
+ else
64
+ attr_reader name
65
+ end
66
+
67
+ if type and type.respond_to?(:cast_from_string)
68
+ module_eval(<<-RUBY, __FILE__, __LINE__)
69
+ def #{name}_with_type=(value) # def birth_date_with_type=(value)
70
+ if value.kind_of?(String) # if value.kind_of?(String)
71
+ value = #{type}.cast_from_string(value) # value = Date.cast_from_string(value)
72
+ end # end
73
+ self.#{name}_without_type = value # self.birth_date_without_type = value
74
+ end # end
75
+ RUBY
76
+ alias_method_chain "#{name}=", :type
77
+ end
78
+ end
79
+ end
80
+ alias key attr_accessor_with_mongo
81
+
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,31 @@
1
+ require 'mongo_doc/ext'
2
+
3
+ module MongoDoc
4
+ module BSON
5
+ CLASS_KEY = "json_class"
6
+
7
+ def self.decode(bson, options = {})
8
+ return bson if options[:raw_json]
9
+ case bson
10
+ when Hash
11
+ bson_create(bson, options)
12
+ when Array
13
+ array_create(bson, options)
14
+ else
15
+ bson
16
+ end
17
+ end
18
+
19
+ def self.bson_create(bson_hash, options = {})
20
+ return bson_hash if options[:raw_json]
21
+ klass = bson_hash.delete(CLASS_KEY)
22
+ return bson_hash.each_pair {|key, value| bson_hash[key] = decode(value, options)} unless klass
23
+ klass.constantize.bson_create(bson_hash, options)
24
+ end
25
+
26
+ def self.array_create(bson_array, options = {})
27
+ return bson_array if options[:raw_json]
28
+ bson_array.map {|item| decode(item, options)}
29
+ end
30
+ end
31
+ end