mongoid 1.1.4 → 1.2.0

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 (65) hide show
  1. data/HISTORY +69 -1
  2. data/Rakefile +1 -1
  3. data/VERSION +1 -1
  4. data/lib/mongoid.rb +39 -13
  5. data/lib/mongoid/associations.rb +1 -0
  6. data/lib/mongoid/associations/has_many.rb +19 -3
  7. data/lib/mongoid/attributes.rb +6 -1
  8. data/lib/mongoid/collection.rb +106 -0
  9. data/lib/mongoid/collections/cyclic_iterator.rb +34 -0
  10. data/lib/mongoid/collections/master.rb +28 -0
  11. data/lib/mongoid/collections/mimic.rb +46 -0
  12. data/lib/mongoid/collections/operations.rb +39 -0
  13. data/lib/mongoid/collections/slaves.rb +44 -0
  14. data/lib/mongoid/commands.rb +1 -0
  15. data/lib/mongoid/config.rb +61 -9
  16. data/lib/mongoid/contexts/enumerable.rb +24 -15
  17. data/lib/mongoid/contexts/mongo.rb +25 -31
  18. data/lib/mongoid/contexts/paging.rb +2 -2
  19. data/lib/mongoid/criteria.rb +48 -7
  20. data/lib/mongoid/criterion/exclusion.rb +2 -0
  21. data/lib/mongoid/criterion/optional.rb +2 -2
  22. data/lib/mongoid/cursor.rb +82 -0
  23. data/lib/mongoid/document.rb +12 -13
  24. data/lib/mongoid/errors.rb +35 -4
  25. data/lib/mongoid/extensions.rb +1 -0
  26. data/lib/mongoid/extensions/array/aliasing.rb +4 -0
  27. data/lib/mongoid/extensions/string/inflections.rb +44 -1
  28. data/lib/mongoid/factory.rb +17 -0
  29. data/lib/mongoid/finders.rb +7 -2
  30. data/lib/mongoid/matchers/default.rb +6 -0
  31. data/lib/mongoid/matchers/gt.rb +1 -1
  32. data/lib/mongoid/matchers/gte.rb +1 -1
  33. data/lib/mongoid/matchers/lt.rb +1 -1
  34. data/lib/mongoid/matchers/lte.rb +1 -1
  35. data/mongoid.gemspec +30 -5
  36. data/perf/benchmark.rb +5 -3
  37. data/spec/integration/mongoid/associations_spec.rb +12 -0
  38. data/spec/integration/mongoid/contexts/enumerable_spec.rb +20 -0
  39. data/spec/integration/mongoid/criteria_spec.rb +28 -0
  40. data/spec/integration/mongoid/document_spec.rb +1 -1
  41. data/spec/integration/mongoid/inheritance_spec.rb +2 -2
  42. data/spec/models/person.rb +1 -1
  43. data/spec/spec_helper.rb +9 -4
  44. data/spec/unit/mongoid/associations/has_many_spec.rb +19 -0
  45. data/spec/unit/mongoid/associations_spec.rb +9 -0
  46. data/spec/unit/mongoid/attributes_spec.rb +4 -4
  47. data/spec/unit/mongoid/collection_spec.rb +113 -0
  48. data/spec/unit/mongoid/collections/cyclic_iterator_spec.rb +75 -0
  49. data/spec/unit/mongoid/collections/master_spec.rb +41 -0
  50. data/spec/unit/mongoid/collections/mimic_spec.rb +43 -0
  51. data/spec/unit/mongoid/collections/slaves_spec.rb +81 -0
  52. data/spec/unit/mongoid/commands_spec.rb +7 -0
  53. data/spec/unit/mongoid/config_spec.rb +52 -1
  54. data/spec/unit/mongoid/contexts/enumerable_spec.rb +38 -12
  55. data/spec/unit/mongoid/contexts/mongo_spec.rb +38 -31
  56. data/spec/unit/mongoid/criteria_spec.rb +20 -12
  57. data/spec/unit/mongoid/criterion/exclusion_spec.rb +26 -0
  58. data/spec/unit/mongoid/criterion/optional_spec.rb +13 -0
  59. data/spec/unit/mongoid/cursor_spec.rb +74 -0
  60. data/spec/unit/mongoid/document_spec.rb +4 -5
  61. data/spec/unit/mongoid/errors_spec.rb +5 -9
  62. data/spec/unit/mongoid/extensions/string/inflections_spec.rb +21 -2
  63. data/spec/unit/mongoid/factory_spec.rb +16 -0
  64. data/spec/unit/mongoid_spec.rb +4 -4
  65. metadata +28 -3
data/HISTORY CHANGED
@@ -1,4 +1,72 @@
1
- === 1.0.7
1
+ === 1.2.0
2
+ - Fixed composite key generation not to replace all
3
+ special chars with dashes.
4
+
5
+ - Memory optimizations, now wrapping the mongo cursor.
6
+
7
+ - Fixed memoization on has_many_related assocations.
8
+
9
+ - Fixed pagination on embedded associations
10
+
11
+ - Fixed after_update callback not getting fired.
12
+
13
+ - When a connection failure occurs, Mongoid tried to
14
+ retry the operation up to a configurable time.
15
+
16
+ - Mongoid now supports a configuration with a master
17
+ and multiple read slaves. It will direct all writes
18
+ to the master and all reads to the slaves. In the case
19
+ of a write, subsequent reads will be directed to the
20
+ master up to configurable number to try and counter
21
+ the 2 second slave sync delay.
22
+
23
+ - Fixed issue with criteria exclusion queries with ids.
24
+
25
+ - Mongoid only executes a count when explicitly paginating.
26
+
27
+ - Fixed Criteria offset to be a getter or setter
28
+
29
+ - Added indexes to foreign keys on belongs_to_related
30
+ associations.
31
+
32
+ - General code refactorings/cleanup
33
+
34
+ === 1.1.4
35
+ - Refactorings in preparation for next feature push.
36
+
37
+ - Associations may now have anonymous extensions.
38
+
39
+ - Ruby 1.9 compatibility updates. (brainopia)
40
+
41
+ - Document.to_json accepts options. (jsmestad)
42
+
43
+ === 1.1.3
44
+ - Nil can be passed into methods that set attributes.
45
+
46
+ - Mongoid.config can now take a block.
47
+
48
+ - Allow named_scopes and criteria class methods on
49
+ has_many_related if related is a Mongoid document.
50
+
51
+ - Document.find can now take an array of ids.
52
+
53
+ === 1.1.2
54
+ - Fixing issues with updates to parents.
55
+
56
+ === 1.1.1
57
+ - Document.create raises validations error, not no
58
+ method error on self.errors (Rick Frankel)
59
+
60
+ - Support default values that respond_to?(:call)
61
+ (procs/lambda) (ahoward)
62
+
63
+ - Added Mongoid.persist_in_safe_mode global config
64
+ option.
65
+
66
+ - Minor optimization: don't evaluate default procs
67
+ if it's not needed (brainopia)
68
+
69
+ === 1.1.0
2
70
  - Nil attributes no longer persist nil values to the
3
71
  database - the field will just not exist.
4
72
 
data/Rakefile CHANGED
@@ -15,7 +15,7 @@ begin
15
15
  gem.add_dependency("activesupport", "<= 2.3.5")
16
16
  gem.add_dependency("mongo", ">= 0.18.2")
17
17
  gem.add_dependency("durran-validatable", ">= 2.0.1")
18
- gem.add_dependency("leshill-will_paginate", ">= 2.3.11")
18
+ gem.add_dependency("will_paginate", ">= 2.3.11")
19
19
 
20
20
  gem.add_development_dependency("rspec", ">= 1.2.9")
21
21
  gem.add_development_dependency("mocha", ">= 0.9.8")
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.4
1
+ 1.2.0
@@ -24,7 +24,7 @@ require "rubygems"
24
24
  gem "activesupport", ">= 2.2.2", "<3.0.pre"
25
25
  gem "mongo", ">= 0.18.2"
26
26
  gem "durran-validatable", ">= 2.0.1"
27
- gem "leshill-will_paginate", ">= 2.3.11"
27
+ gem "will_paginate", ">= 2.3.11"
28
28
 
29
29
  require "delegate"
30
30
  require "observer"
@@ -41,12 +41,15 @@ require "mongoid/associations"
41
41
  require "mongoid/associations/options"
42
42
  require "mongoid/attributes"
43
43
  require "mongoid/callbacks"
44
+ require "mongoid/collection"
44
45
  require "mongoid/commands"
45
46
  require "mongoid/config"
46
47
  require "mongoid/contexts"
47
48
  require "mongoid/criteria"
49
+ require "mongoid/cursor"
48
50
  require "mongoid/extensions"
49
51
  require "mongoid/errors"
52
+ require "mongoid/factory"
50
53
  require "mongoid/field"
51
54
  require "mongoid/fields"
52
55
  require "mongoid/finders"
@@ -65,20 +68,43 @@ module Mongoid #:nodoc
65
68
 
66
69
  class << self
67
70
 
68
- delegate \
69
- :allow_dynamic_fields,
70
- :allow_dynamic_fields=,
71
- :database,
72
- :database=,
73
- :persist_in_safe_mode,
74
- :persist_in_safe_mode=,
75
- :raise_not_found_error,
76
- :raise_not_found_error=, :to => :config
77
-
78
- def config
79
- yield Config.instance if block_given?; Config.instance
71
+ # Sets the Mongoid configuration options. Best used by passing a block.
72
+ #
73
+ # Example:
74
+ #
75
+ # Mongoid.configure do |config|
76
+ # name = "mongoid_test"
77
+ # host = "localhost"
78
+ # config.allow_dynamic_fields = false
79
+ # config.master = Mongo::Connection.new.db(name)
80
+ # config.slaves = [
81
+ # Mongo::Connection.new(host, 27018, :slave_ok => true).db(name),
82
+ # Mongo::Connection.new(host, 27019, :slave_ok => true).db(name)
83
+ # ]
84
+ # end
85
+ #
86
+ # Returns:
87
+ #
88
+ # The Mongoid +Config+ singleton instance.
89
+ def configure
90
+ config = Config.instance
91
+ block_given? ? yield(config) : config
80
92
  end
81
93
 
94
+ alias :config :configure
82
95
  end
83
96
 
97
+ # Take all the public instance methods from the Config singleton and allow
98
+ # them to be accessed through the Mongoid module directly.
99
+ #
100
+ # Example:
101
+ #
102
+ # <tt>Mongoid.database = Mongo::Connection.new.db("test")</tt>
103
+ Config.public_instance_methods(false).each do |name|
104
+ (class << self; self; end).class_eval <<-EOT
105
+ def #{name}(*args)
106
+ configure.send("#{name}", *args)
107
+ end
108
+ EOT
109
+ end
84
110
  end
@@ -88,6 +88,7 @@ module Mongoid # :nodoc:
88
88
  #
89
89
  def belongs_to_related(name, options = {}, &block)
90
90
  field "#{name.to_s}_id"
91
+ index "#{name.to_s}_id" unless self.embedded
91
92
  add_association(
92
93
  Associations::BelongsToRelated,
93
94
  Associations::Options.new(
@@ -97,9 +97,9 @@ module Mongoid #:nodoc:
97
97
  # If the method exists on the array, use the default proxy behavior.
98
98
  def method_missing(name, *args, &block)
99
99
  unless @target.respond_to?(name)
100
- criteria = @klass.send(name, *args)
101
- criteria.documents = @target
102
- return criteria
100
+ object = @klass.send(name, *args)
101
+ object.documents = @target
102
+ return object
103
103
  end
104
104
  super
105
105
  end
@@ -120,6 +120,22 @@ module Mongoid #:nodoc:
120
120
  end
121
121
  end
122
122
 
123
+ # Paginate the association. Will create a new criteria, set the documents
124
+ # on it and execute in an enumerable context.
125
+ #
126
+ # Options:
127
+ #
128
+ # options: A +Hash+ of pagination options.
129
+ #
130
+ # Returns:
131
+ #
132
+ # A +WillPaginate::Collection+.
133
+ def paginate(options)
134
+ criteria = Mongoid::Criteria.translate(@klass, options)
135
+ criteria.documents = @target
136
+ criteria.paginate
137
+ end
138
+
123
139
  protected
124
140
  # Initializes each of the attributes in the hash.
125
141
  def initialize_each(attributes)
@@ -40,7 +40,7 @@ module Mongoid #:nodoc:
40
40
  # put into the document's attributes.
41
41
  def process(attrs = nil)
42
42
  (attrs || {}).each_pair do |key, value|
43
- if Mongoid.allow_dynamic_fields && !respond_to?("#{key}=")
43
+ if set_allowed?(key)
44
44
  @attributes[key.to_s] = value
45
45
  else
46
46
  send("#{key}=", value) if value
@@ -131,6 +131,11 @@ module Mongoid #:nodoc:
131
131
  end
132
132
 
133
133
  protected
134
+ # Return true is dynamic field setting is enabled.
135
+ def set_allowed?(key)
136
+ Mongoid.allow_dynamic_fields && !respond_to?("#{key}=")
137
+ end
138
+
134
139
  # Used when supplying a :reject_if block as an option to
135
140
  # accepts_nested_attributes_for
136
141
  def reject(attributes, options)
@@ -0,0 +1,106 @@
1
+ # encoding: utf-8
2
+ require "mongoid/collections/operations"
3
+ require "mongoid/collections/cyclic_iterator"
4
+ require "mongoid/collections/mimic"
5
+ require "mongoid/collections/master"
6
+ require "mongoid/collections/slaves"
7
+
8
+ module Mongoid #:nodoc
9
+ class Collection
10
+ include Collections::Mimic
11
+ attr_reader :counter, :name
12
+
13
+ # All write operations should delegate to the master connection. These
14
+ # operations mimic the methods on a Mongo:Collection.
15
+ #
16
+ # Example:
17
+ #
18
+ # <tt>collection.save({ :name => "Al" })</tt>
19
+ proxy(:master, Collections::Operations::WRITE)
20
+
21
+ # All read operations should be intelligently directed to either the master
22
+ # or the slave, depending on where the read counter is and what it's
23
+ # maximum was configured at.
24
+ #
25
+ # Example:
26
+ #
27
+ # <tt>collection.find({ :name => "Al" })</tt>
28
+ proxy(:directed, (Collections::Operations::READ - [:find]))
29
+
30
+ # Determines where to send the next read query. If the slaves are not
31
+ # defined then send to master. If the read counter is under the configured
32
+ # maximum then return the master. In any other case return the slaves.
33
+ #
34
+ # Example:
35
+ #
36
+ # <tt>collection.directed</tt>
37
+ #
38
+ # Return:
39
+ #
40
+ # Either a +Master+ or +Slaves+ collection.
41
+ def directed
42
+ if under_max_counter? || slaves.empty?
43
+ @counter = @counter + 1
44
+ master
45
+ else
46
+ @counter = 0
47
+ slaves
48
+ end
49
+ end
50
+
51
+ # Find documents from the database given a selector and options.
52
+ #
53
+ # Options:
54
+ #
55
+ # selector: A +Hash+ selector that is the query.
56
+ # options: The options to pass to the db.
57
+ #
58
+ # Example:
59
+ #
60
+ # <tt>collection.find({ :test => "value" })</tt>
61
+ def find(selector = {}, options = {})
62
+ cursor = Mongoid::Cursor.new(self, directed.find(selector, options))
63
+ if block_given?
64
+ yield cursor; cursor.close
65
+ else
66
+ cursor
67
+ end
68
+ end
69
+
70
+ # Initialize a new Mongoid::Collection, setting up the master, slave, and
71
+ # name attributes. Masters will be used for writes, slaves for reads.
72
+ #
73
+ # Example:
74
+ #
75
+ # <tt>Mongoid::Collection.new(masters, slaves, "test")</tt>
76
+ def initialize(name)
77
+ @name, @counter = name, 0
78
+ end
79
+
80
+ # Return the object responsible for reading documents from the database.
81
+ # This is usually the slave databases, but in their absence the master will
82
+ # handle the task.
83
+ #
84
+ # Example:
85
+ #
86
+ # <tt>collection.reader</tt>
87
+ def slaves
88
+ @slaves ||= Collections::Slaves.new(Mongoid.slaves, @name)
89
+ end
90
+
91
+ # Return the object responsible for writes to the database. This will
92
+ # always return a collection associated with the Master DB.
93
+ #
94
+ # Example:
95
+ #
96
+ # <tt>collection.writer</tt>
97
+ def master
98
+ @master ||= Collections::Master.new(Mongoid.master, @name)
99
+ end
100
+
101
+ protected
102
+ def under_max_counter?
103
+ @counter < Mongoid.max_successive_reads
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Collections #:nodoc:
4
+ class CyclicIterator
5
+
6
+ attr_reader :counter
7
+
8
+ # Performs iteration over an array, if the array gets to the end then loop
9
+ # back to the first.
10
+ #
11
+ # Example:
12
+ #
13
+ # <tt>CyclicIterator.new([ first, second ])</tt>
14
+ def initialize(array)
15
+ @array, @counter = array, -1
16
+ end
17
+
18
+ # Get the next element in the array. If the element is the last in the
19
+ # array then return the first.
20
+ #
21
+ # Example:
22
+ #
23
+ # <tt>iterator.next</tt>
24
+ #
25
+ # Returns:
26
+ #
27
+ # The next element in the array.
28
+ def next
29
+ (@counter == @array.size - 1) ? @counter = 0 : @counter = @counter + 1
30
+ @array[@counter]
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Collections #:nodoc:
4
+ class Master
5
+ include Mimic
6
+
7
+ attr_reader :collection
8
+
9
+ # All read and write operations should delegate to the master connection.
10
+ # These operations mimic the methods on a Mongo:Collection.
11
+ #
12
+ # Example:
13
+ #
14
+ # <tt>collection.save({ :name => "Al" })</tt>
15
+ proxy(:collection, Operations::ALL)
16
+
17
+ # Create the new database writer. Will create a collection from the
18
+ # master database.
19
+ #
20
+ # Example:
21
+ #
22
+ # <tt>Master.new(master, "mongoid_people")</tt>
23
+ def initialize(master, name)
24
+ @collection = master.collection(name)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,46 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Collections #:nodoc:
4
+ module Mimic #:nodoc:
5
+ def self.included(base)
6
+ base.class_eval do
7
+ include InstanceMethods
8
+ extend ClassMethods
9
+ end
10
+ end
11
+
12
+ module InstanceMethods #:nodoc:
13
+ # Retry the supplied operation until the reconnect time has expired,
14
+ # defined in the mongoid Config module.
15
+ #
16
+ # Example:
17
+ #
18
+ # <tt>master.attempt(operation)</tt>
19
+ def attempt(operation, start)
20
+ begin
21
+ elapsed = (Time.now - start)
22
+ operation.call
23
+ rescue Mongo::ConnectionFailure => error
24
+ (elapsed < Mongoid.reconnect_time) ? retry : (raise error)
25
+ end
26
+ end
27
+ end
28
+
29
+ module ClassMethods #:nodoc:
30
+ # Proxy all the supplied operations to the internal collection or target.
31
+ #
32
+ # Example:
33
+ #
34
+ # <tt>proxy Operations::ALL, :collection</tt>
35
+ def proxy(target, operations)
36
+ operations.each do |name|
37
+ define_method(name) do |*args|
38
+ operation = lambda { send(target).send(name, *args) }
39
+ attempt(operation, Time.now)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end