mrkurt-mongo_mapper 0.6.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/.gitignore +10 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +38 -0
  4. data/Rakefile +55 -0
  5. data/VERSION +1 -0
  6. data/bin/mmconsole +60 -0
  7. data/lib/mongo_mapper.rb +139 -0
  8. data/lib/mongo_mapper/associations.rb +72 -0
  9. data/lib/mongo_mapper/associations/base.rb +113 -0
  10. data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +26 -0
  11. data/lib/mongo_mapper/associations/belongs_to_proxy.rb +21 -0
  12. data/lib/mongo_mapper/associations/collection.rb +19 -0
  13. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +26 -0
  14. data/lib/mongo_mapper/associations/many_documents_proxy.rb +115 -0
  15. data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +31 -0
  16. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +54 -0
  17. data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +11 -0
  18. data/lib/mongo_mapper/associations/one_proxy.rb +61 -0
  19. data/lib/mongo_mapper/associations/proxy.rb +111 -0
  20. data/lib/mongo_mapper/callbacks.rb +61 -0
  21. data/lib/mongo_mapper/dirty.rb +117 -0
  22. data/lib/mongo_mapper/document.rb +496 -0
  23. data/lib/mongo_mapper/dynamic_finder.rb +74 -0
  24. data/lib/mongo_mapper/embedded_document.rb +380 -0
  25. data/lib/mongo_mapper/finder_options.rb +145 -0
  26. data/lib/mongo_mapper/key.rb +36 -0
  27. data/lib/mongo_mapper/mongo_mapper.rb +125 -0
  28. data/lib/mongo_mapper/pagination.rb +66 -0
  29. data/lib/mongo_mapper/rails_compatibility/document.rb +15 -0
  30. data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +28 -0
  31. data/lib/mongo_mapper/serialization.rb +54 -0
  32. data/lib/mongo_mapper/serializers/json_serializer.rb +48 -0
  33. data/lib/mongo_mapper/support.rb +192 -0
  34. data/lib/mongo_mapper/validations.rb +39 -0
  35. data/mongo_mapper.gemspec +173 -0
  36. data/specs.watchr +30 -0
  37. data/test/NOTE_ON_TESTING +1 -0
  38. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +55 -0
  39. data/test/functional/associations/test_belongs_to_proxy.rb +91 -0
  40. data/test/functional/associations/test_many_documents_as_proxy.rb +246 -0
  41. data/test/functional/associations/test_many_documents_proxy.rb +477 -0
  42. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +156 -0
  43. data/test/functional/associations/test_many_embedded_proxy.rb +192 -0
  44. data/test/functional/associations/test_many_polymorphic_proxy.rb +339 -0
  45. data/test/functional/associations/test_one_proxy.rb +131 -0
  46. data/test/functional/test_associations.rb +44 -0
  47. data/test/functional/test_binary.rb +33 -0
  48. data/test/functional/test_callbacks.rb +85 -0
  49. data/test/functional/test_dirty.rb +159 -0
  50. data/test/functional/test_document.rb +1198 -0
  51. data/test/functional/test_embedded_document.rb +135 -0
  52. data/test/functional/test_logger.rb +20 -0
  53. data/test/functional/test_modifiers.rb +242 -0
  54. data/test/functional/test_pagination.rb +95 -0
  55. data/test/functional/test_rails_compatibility.rb +25 -0
  56. data/test/functional/test_string_id_compatibility.rb +72 -0
  57. data/test/functional/test_validations.rb +361 -0
  58. data/test/models.rb +271 -0
  59. data/test/support/custom_matchers.rb +55 -0
  60. data/test/support/timing.rb +16 -0
  61. data/test/test_helper.rb +27 -0
  62. data/test/unit/associations/test_base.rb +182 -0
  63. data/test/unit/associations/test_proxy.rb +91 -0
  64. data/test/unit/serializers/test_json_serializer.rb +189 -0
  65. data/test/unit/test_document.rb +236 -0
  66. data/test/unit/test_dynamic_finder.rb +125 -0
  67. data/test/unit/test_embedded_document.rb +709 -0
  68. data/test/unit/test_finder_options.rb +325 -0
  69. data/test/unit/test_key.rb +172 -0
  70. data/test/unit/test_mongo_mapper.rb +65 -0
  71. data/test/unit/test_pagination.rb +119 -0
  72. data/test/unit/test_rails_compatibility.rb +52 -0
  73. data/test/unit/test_serializations.rb +52 -0
  74. data/test/unit/test_support.rb +346 -0
  75. data/test/unit/test_time_zones.rb +40 -0
  76. data/test/unit/test_validations.rb +503 -0
  77. metadata +239 -0
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ *~
7
+ *.gem
8
+ tmp
9
+ .yardoc
10
+ doc/*
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 John Nunemaker
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,38 @@
1
+ = MongoMapper
2
+
3
+ Awesome gem for modeling your domain and storing it in mongo.
4
+
5
+ Releases are tagged on github and released on gemcutter. Master is pushed to whenever I add a patch or a new feature, but I do not release a new gem version each time I push.
6
+
7
+ == Note on Patches/Pull Requests
8
+
9
+ * Fork the project.
10
+ * Make your feature addition or bug fix.
11
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
12
+ * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself in another branch so I can ignore when I pull)
13
+ * Send me a pull request. Bonus points for topic branches.
14
+
15
+ == Install
16
+
17
+ $ gem install mongo_mapper
18
+
19
+ == Dependencies
20
+
21
+ * ActiveSupport (typically the latest version)
22
+ * Mongo Ruby Driver (mongo)
23
+ * My fork of the validatable gem (jnunemaker-validatable)
24
+
25
+ == Documentation
26
+
27
+ Documentation is lacking right now because if you can't look through the code right now and feel comfortable, this is probably too young for you to use. Wait til it stabilizes a bit more.
28
+
29
+ http://groups.google.com/group/mongomapper
30
+
31
+ == More Info
32
+
33
+ * http://www.mongodb.org/
34
+ * http://github.com/mongodb/mongo-ruby-driver/
35
+
36
+ == Copyright
37
+
38
+ Copyright (c) 2009 John Nunemaker. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'jeweler'
4
+ require 'yard'
5
+ require 'yard/rake/yardoc_task'
6
+
7
+ Jeweler::Tasks.new do |gem|
8
+ gem.name = "mrkurt-mongo_mapper"
9
+ gem.summary = %Q{Awesome gem for modeling your domain and storing it in mongo}
10
+ gem.email = "mrkurt@gmail.com"
11
+ gem.homepage = "http://github.com/mrkurt/mongomapper"
12
+ gem.authors = ["John Nunemaker", "Kurt Mackey"]
13
+
14
+ gem.add_dependency('activesupport', '>= 2.3')
15
+ gem.add_dependency('mongo', '0.18.2')
16
+ gem.add_dependency('jnunemaker-validatable', '1.8.1')
17
+
18
+ gem.add_development_dependency('jnunemaker-matchy', '0.4.0')
19
+ gem.add_development_dependency('shoulda', '2.10.2')
20
+ gem.add_development_dependency('timecop', '0.3.1')
21
+ gem.add_development_dependency('mocha', '0.9.8')
22
+ end
23
+
24
+ Jeweler::GemcutterTasks.new
25
+
26
+ require 'rake/testtask'
27
+ Rake::TestTask.new(:test) do |test|
28
+ test.libs << 'test'
29
+ test.ruby_opts << '-rubygems'
30
+ test.pattern = 'test/**/test_*.rb'
31
+ test.verbose = true
32
+ end
33
+
34
+ namespace :test do
35
+ Rake::TestTask.new(:units) do |test|
36
+ test.libs << 'test'
37
+ test.ruby_opts << '-rubygems'
38
+ test.pattern = 'test/unit/**/test_*.rb'
39
+ test.verbose = true
40
+ end
41
+
42
+ Rake::TestTask.new(:functionals) do |test|
43
+ test.libs << 'test'
44
+ test.ruby_opts << '-rubygems'
45
+ test.pattern = 'test/functional/**/test_*.rb'
46
+ test.verbose = true
47
+ end
48
+ end
49
+
50
+ task :default => :test
51
+ task :test => :check_dependencies
52
+
53
+ YARD::Rake::YardocTask.new(:doc) do |t|
54
+ t.options = ["--legacy"] if RUBY_VERSION < "1.9.0"
55
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.6.8
data/bin/mmconsole ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
3
+
4
+ begin
5
+ require 'mongo_mapper'
6
+ require 'irb'
7
+ rescue LoadError
8
+ require 'rubygems'
9
+ retry
10
+ end
11
+
12
+ IRB.setup(nil)
13
+ irb = IRB::Irb.new
14
+
15
+ IRB.conf[:MAIN_CONTEXT] = irb.context
16
+
17
+ irb.context.evaluate("require 'irb/completion'", 0)
18
+ irb.context.evaluate(%@
19
+ include MongoMapper
20
+
21
+ MongoMapper.database = "mmtest"
22
+ $db = MongoMapper.database
23
+
24
+ @, 0)
25
+
26
+ puts %@
27
+ Welcome to the MongoMapper Console!
28
+
29
+ Example 1:
30
+ things = $db.collection("things")
31
+ things.insert("name" => "Raw Thing")
32
+ things.insert("name" => "Another Thing", "date" => Time.now)
33
+
34
+ cursor = things.find("name" => "Raw Thing")
35
+ puts cursor.next_object.inspect
36
+
37
+ Example 2:
38
+ class Thing
39
+ include MongoMapper::Document
40
+ key :name, String, :required => true
41
+ key :date, Time
42
+ end
43
+
44
+ thing = Thing.new
45
+ thing.name = "My thing"
46
+ thing.date = Time.now
47
+ thing.save
48
+
49
+ all_things = Thing.all
50
+ puts all_things.map { |object| object.name }.inspect
51
+ @
52
+
53
+ trap("SIGINT") do
54
+ irb.signal_handle
55
+ end
56
+
57
+ catch(:IRB_EXIT) do
58
+ irb.eval_input
59
+ end
60
+
@@ -0,0 +1,139 @@
1
+ # if Gem is defined i'll assume you are using rubygems and lock specific versions
2
+ # call me crazy but a plain old require will just get the latest version you have installed
3
+ # so i want to make sure that if you are using gems you do in fact have the correct versions
4
+ # if there is a better way to do this, please enlighten me!
5
+ if self.class.const_defined?(:Gem)
6
+ gem 'activesupport', '>= 2.3'
7
+ gem 'mongo', '0.18.2'
8
+ gem 'jnunemaker-validatable', '1.8.1'
9
+ end
10
+
11
+ require 'active_support'
12
+ require 'mongo'
13
+ require 'validatable'
14
+
15
+ module MongoMapper
16
+ # generic MM error
17
+ class MongoMapperError < StandardError; end
18
+
19
+ # raised when key expected to exist but not found
20
+ class KeyNotFound < MongoMapperError; end
21
+
22
+ # raised when document expected but not found
23
+ class DocumentNotFound < MongoMapperError; end
24
+
25
+ # raised when document not valid and using !
26
+ class DocumentNotValid < MongoMapperError
27
+ def initialize(document)
28
+ super("Validation failed: #{document.errors.full_messages.join(", ")}")
29
+ end
30
+ end
31
+
32
+ #raised when locking is enabled, and a document is saved with
33
+ #an incorrect _rev
34
+ class StaleDocumentError < MongoMapperError; end
35
+
36
+ # @api public
37
+ def self.connection
38
+ @@connection ||= Mongo::Connection.new
39
+ end
40
+
41
+ # @api public
42
+ def self.connection=(new_connection)
43
+ @@connection = new_connection
44
+ end
45
+
46
+ # @api public
47
+ def self.logger
48
+ connection.logger
49
+ end
50
+
51
+ # @api public
52
+ def self.database=(name)
53
+ @@database = nil
54
+ @@database_name = name
55
+ end
56
+
57
+ # @api public
58
+ def self.database
59
+ if @@database_name.blank?
60
+ raise 'You forgot to set the default database name: MongoMapper.database = "foobar"'
61
+ end
62
+
63
+ @@database ||= MongoMapper.connection.db(@@database_name)
64
+ end
65
+
66
+ # @api public
67
+ def self.safe_mode
68
+ @@safe_mode ||= false
69
+ end
70
+
71
+ # @api public
72
+ def self.safe_mode=(value)
73
+ @@safe_mode = value
74
+ end
75
+
76
+ # @api private
77
+ def self.ensured_indexes
78
+ @@ensured_indexes ||= []
79
+ end
80
+
81
+ # @api private
82
+ def self.ensured_indexes=(value)
83
+ @@ensured_indexes = value
84
+ end
85
+
86
+ # @api private
87
+ def self.ensure_index(klass, keys, options={})
88
+ ensured_indexes << {:klass => klass, :keys => keys, :options => options}
89
+ end
90
+
91
+ # @api public
92
+ def self.ensure_indexes!
93
+ ensured_indexes.each do |index|
94
+ unique = index[:options].delete(:unique)
95
+ index[:klass].collection.create_index(index[:keys], unique)
96
+ end
97
+ end
98
+
99
+ # @api private
100
+ def self.use_time_zone?
101
+ Time.respond_to?(:zone) && Time.zone ? true : false
102
+ end
103
+
104
+ # @api private
105
+ def self.time_class
106
+ use_time_zone? ? Time.zone : Time
107
+ end
108
+
109
+ # @api private
110
+ def self.normalize_object_id(value)
111
+ value.is_a?(String) ? Mongo::ObjectID.from_string(value) : value
112
+ end
113
+ end
114
+
115
+ require 'mongo_mapper/support'
116
+ require 'mongo_mapper/callbacks'
117
+ require 'mongo_mapper/finder_options'
118
+ require 'mongo_mapper/dirty'
119
+ require 'mongo_mapper/dynamic_finder'
120
+ require 'mongo_mapper/key'
121
+ require 'mongo_mapper/pagination'
122
+ require 'mongo_mapper/serialization'
123
+ require 'mongo_mapper/validations'
124
+ require 'mongo_mapper/rails_compatibility/document'
125
+ require 'mongo_mapper/rails_compatibility/embedded_document'
126
+ require 'mongo_mapper/embedded_document'
127
+ require 'mongo_mapper/document'
128
+ require 'mongo_mapper/associations'
129
+ require 'mongo_mapper/associations/base'
130
+ require 'mongo_mapper/associations/proxy'
131
+ require 'mongo_mapper/associations/collection'
132
+ require 'mongo_mapper/associations/many_documents_proxy'
133
+ require 'mongo_mapper/associations/belongs_to_proxy'
134
+ require 'mongo_mapper/associations/belongs_to_polymorphic_proxy'
135
+ require 'mongo_mapper/associations/many_polymorphic_proxy'
136
+ require 'mongo_mapper/associations/many_embedded_proxy'
137
+ require 'mongo_mapper/associations/many_embedded_polymorphic_proxy'
138
+ require 'mongo_mapper/associations/many_documents_as_proxy'
139
+ require 'mongo_mapper/associations/one_proxy'
@@ -0,0 +1,72 @@
1
+ module MongoMapper
2
+ module Associations
3
+ module ClassMethods
4
+ def belongs_to(association_id, options={}, &extension)
5
+ create_association(:belongs_to, association_id, options, &extension)
6
+ end
7
+
8
+ def many(association_id, options={}, &extension)
9
+ create_association(:many, association_id, options, &extension)
10
+ end
11
+
12
+ def one(association_id, options={}, &extension)
13
+ create_association(:one, association_id, options, &extension)
14
+ end
15
+
16
+ def associations
17
+ @associations ||= HashWithIndifferentAccess.new
18
+ end
19
+
20
+ def associations=(hash)
21
+ @associations = hash
22
+ end
23
+
24
+ def inherited(subclass)
25
+ subclass.associations = associations.dup
26
+ super
27
+ end
28
+
29
+ private
30
+ def create_association(type, name, options, &extension)
31
+ association = Associations::Base.new(type, name, options, &extension)
32
+ associations[association.name] = association
33
+
34
+ define_method(association.name) do
35
+ get_proxy(association)
36
+ end
37
+
38
+ define_method("#{association.name}=") do |value|
39
+ get_proxy(association).replace(value)
40
+ value
41
+ end
42
+
43
+ if association.options[:dependent] && association.many? && !association.embeddable?
44
+ after_destroy do |doc|
45
+ case association.options[:dependent]
46
+ when :destroy
47
+ doc.get_proxy(association).destroy_all
48
+ when :delete_all
49
+ doc.get_proxy(association).delete_all
50
+ when :nullify
51
+ doc.get_proxy(association).nullify
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ module InstanceMethods
59
+ def associations
60
+ self.class.associations
61
+ end
62
+
63
+ def get_proxy(association)
64
+ unless proxy = self.instance_variable_get(association.ivar)
65
+ proxy = association.proxy_class.new(self, association)
66
+ self.instance_variable_set(association.ivar, proxy)
67
+ end
68
+ proxy
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,113 @@
1
+ module MongoMapper
2
+ module Associations
3
+ class Base
4
+ attr_reader :type, :name, :options, :finder_options
5
+
6
+ # Options that should not be considered MongoDB query options/criteria
7
+ AssociationOptions = [:as, :class, :class_name, :dependent, :extend, :foreign_key, :polymorphic]
8
+
9
+ def initialize(type, name, options={}, &extension)
10
+ @type, @name, @options, @finder_options = type, name, {}, {}
11
+ options.symbolize_keys!
12
+
13
+ options[:extend] = modulized_extensions(extension, options[:extend])
14
+
15
+ options.each_pair do |key, value|
16
+ if AssociationOptions.include?(key)
17
+ @options[key] = value
18
+ else
19
+ @finder_options[key] = value
20
+ end
21
+ end
22
+ end
23
+
24
+ def class_name
25
+ @class_name ||= begin
26
+ if cn = options[:class_name]
27
+ cn
28
+ elsif many?
29
+ name.to_s.singularize.camelize
30
+ else
31
+ name.to_s.camelize
32
+ end
33
+ end
34
+ end
35
+
36
+ def klass
37
+ @klass ||= options[:class] || class_name.constantize
38
+ end
39
+
40
+ def many?
41
+ @many_type ||= @type == :many
42
+ end
43
+
44
+ def belongs_to?
45
+ @belongs_to_type ||= @type == :belongs_to
46
+ end
47
+
48
+ def one?
49
+ @one_type ||= @type == :one
50
+ end
51
+
52
+ def polymorphic?
53
+ !!@options[:polymorphic]
54
+ end
55
+
56
+ def as?
57
+ !!@options[:as]
58
+ end
59
+
60
+ def type_key_name
61
+ @type_key_name ||= many? ? '_type' : "#{as}_type"
62
+ end
63
+
64
+ def as
65
+ @options[:as] || self.name
66
+ end
67
+
68
+ def foreign_key
69
+ @options[:foreign_key] || "#{name}_id"
70
+ end
71
+
72
+ def ivar
73
+ @ivar ||= "@_#{name}"
74
+ end
75
+
76
+ def embeddable?
77
+ many? && klass.embeddable?
78
+ end
79
+
80
+ def proxy_class
81
+ @proxy_class ||= begin
82
+ if many?
83
+ if self.klass.embeddable?
84
+ polymorphic? ? ManyEmbeddedPolymorphicProxy : ManyEmbeddedProxy
85
+ else
86
+ if polymorphic?
87
+ ManyPolymorphicProxy
88
+ elsif as?
89
+ ManyDocumentsAsProxy
90
+ else
91
+ ManyDocumentsProxy
92
+ end
93
+ end
94
+ elsif one?
95
+ OneProxy
96
+ else
97
+ polymorphic? ? BelongsToPolymorphicProxy : BelongsToProxy
98
+ end
99
+ end # end begin
100
+ end # end proxy_class
101
+
102
+ private
103
+
104
+ # @param [Array<Module, Proc>] extensions a collection of Modules or
105
+ # Procs that extend the behaviour of this association.
106
+ def modulized_extensions(*extensions)
107
+ extensions.flatten.compact.map do |extension|
108
+ Proc === extension ? Module.new(&extension) : extension
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end