humanoid 1.2.7
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.
- data/.gitignore +6 -0
- data/.watchr +29 -0
- data/HISTORY +342 -0
- data/MIT_LICENSE +20 -0
- data/README.rdoc +56 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/caliper.yml +4 -0
- data/humanoid.gemspec +374 -0
- data/lib/humanoid.rb +111 -0
- data/lib/humanoid/associations.rb +258 -0
- data/lib/humanoid/associations/belongs_to.rb +64 -0
- data/lib/humanoid/associations/belongs_to_related.rb +62 -0
- data/lib/humanoid/associations/has_many.rb +180 -0
- data/lib/humanoid/associations/has_many_related.rb +109 -0
- data/lib/humanoid/associations/has_one.rb +95 -0
- data/lib/humanoid/associations/has_one_related.rb +81 -0
- data/lib/humanoid/associations/options.rb +57 -0
- data/lib/humanoid/associations/proxy.rb +31 -0
- data/lib/humanoid/attributes.rb +184 -0
- data/lib/humanoid/callbacks.rb +23 -0
- data/lib/humanoid/collection.rb +118 -0
- data/lib/humanoid/collections/cyclic_iterator.rb +34 -0
- data/lib/humanoid/collections/master.rb +28 -0
- data/lib/humanoid/collections/mimic.rb +46 -0
- data/lib/humanoid/collections/operations.rb +41 -0
- data/lib/humanoid/collections/slaves.rb +44 -0
- data/lib/humanoid/commands.rb +182 -0
- data/lib/humanoid/commands/create.rb +21 -0
- data/lib/humanoid/commands/delete.rb +16 -0
- data/lib/humanoid/commands/delete_all.rb +23 -0
- data/lib/humanoid/commands/deletion.rb +18 -0
- data/lib/humanoid/commands/destroy.rb +19 -0
- data/lib/humanoid/commands/destroy_all.rb +23 -0
- data/lib/humanoid/commands/save.rb +27 -0
- data/lib/humanoid/components.rb +24 -0
- data/lib/humanoid/config.rb +84 -0
- data/lib/humanoid/contexts.rb +25 -0
- data/lib/humanoid/contexts/enumerable.rb +117 -0
- data/lib/humanoid/contexts/ids.rb +25 -0
- data/lib/humanoid/contexts/mongo.rb +224 -0
- data/lib/humanoid/contexts/paging.rb +42 -0
- data/lib/humanoid/criteria.rb +259 -0
- data/lib/humanoid/criterion/complex.rb +21 -0
- data/lib/humanoid/criterion/exclusion.rb +65 -0
- data/lib/humanoid/criterion/inclusion.rb +91 -0
- data/lib/humanoid/criterion/optional.rb +128 -0
- data/lib/humanoid/cursor.rb +82 -0
- data/lib/humanoid/document.rb +300 -0
- data/lib/humanoid/enslavement.rb +38 -0
- data/lib/humanoid/errors.rb +77 -0
- data/lib/humanoid/extensions.rb +84 -0
- data/lib/humanoid/extensions/array/accessors.rb +17 -0
- data/lib/humanoid/extensions/array/aliasing.rb +4 -0
- data/lib/humanoid/extensions/array/assimilation.rb +26 -0
- data/lib/humanoid/extensions/array/conversions.rb +29 -0
- data/lib/humanoid/extensions/array/parentization.rb +13 -0
- data/lib/humanoid/extensions/boolean/conversions.rb +16 -0
- data/lib/humanoid/extensions/date/conversions.rb +15 -0
- data/lib/humanoid/extensions/datetime/conversions.rb +17 -0
- data/lib/humanoid/extensions/float/conversions.rb +16 -0
- data/lib/humanoid/extensions/hash/accessors.rb +38 -0
- data/lib/humanoid/extensions/hash/assimilation.rb +30 -0
- data/lib/humanoid/extensions/hash/conversions.rb +15 -0
- data/lib/humanoid/extensions/hash/criteria_helpers.rb +20 -0
- data/lib/humanoid/extensions/hash/scoping.rb +12 -0
- data/lib/humanoid/extensions/integer/conversions.rb +16 -0
- data/lib/humanoid/extensions/nil/assimilation.rb +13 -0
- data/lib/humanoid/extensions/object/conversions.rb +33 -0
- data/lib/humanoid/extensions/proc/scoping.rb +12 -0
- data/lib/humanoid/extensions/string/conversions.rb +15 -0
- data/lib/humanoid/extensions/string/inflections.rb +97 -0
- data/lib/humanoid/extensions/symbol/inflections.rb +36 -0
- data/lib/humanoid/extensions/time/conversions.rb +18 -0
- data/lib/humanoid/factory.rb +19 -0
- data/lib/humanoid/field.rb +39 -0
- data/lib/humanoid/fields.rb +62 -0
- data/lib/humanoid/finders.rb +224 -0
- data/lib/humanoid/identity.rb +39 -0
- data/lib/humanoid/indexes.rb +30 -0
- data/lib/humanoid/matchers.rb +36 -0
- data/lib/humanoid/matchers/all.rb +11 -0
- data/lib/humanoid/matchers/default.rb +26 -0
- data/lib/humanoid/matchers/exists.rb +13 -0
- data/lib/humanoid/matchers/gt.rb +11 -0
- data/lib/humanoid/matchers/gte.rb +11 -0
- data/lib/humanoid/matchers/in.rb +11 -0
- data/lib/humanoid/matchers/lt.rb +11 -0
- data/lib/humanoid/matchers/lte.rb +11 -0
- data/lib/humanoid/matchers/ne.rb +11 -0
- data/lib/humanoid/matchers/nin.rb +11 -0
- data/lib/humanoid/matchers/size.rb +11 -0
- data/lib/humanoid/memoization.rb +27 -0
- data/lib/humanoid/named_scope.rb +40 -0
- data/lib/humanoid/scope.rb +75 -0
- data/lib/humanoid/timestamps.rb +30 -0
- data/lib/humanoid/versioning.rb +28 -0
- data/perf/benchmark.rb +77 -0
- data/spec/integration/humanoid/associations_spec.rb +301 -0
- data/spec/integration/humanoid/attributes_spec.rb +22 -0
- data/spec/integration/humanoid/commands_spec.rb +216 -0
- data/spec/integration/humanoid/contexts/enumerable_spec.rb +33 -0
- data/spec/integration/humanoid/criteria_spec.rb +224 -0
- data/spec/integration/humanoid/document_spec.rb +587 -0
- data/spec/integration/humanoid/extensions_spec.rb +26 -0
- data/spec/integration/humanoid/finders_spec.rb +119 -0
- data/spec/integration/humanoid/inheritance_spec.rb +137 -0
- data/spec/integration/humanoid/named_scope_spec.rb +46 -0
- data/spec/models/address.rb +39 -0
- data/spec/models/animal.rb +6 -0
- data/spec/models/comment.rb +8 -0
- data/spec/models/country_code.rb +6 -0
- data/spec/models/employer.rb +5 -0
- data/spec/models/game.rb +6 -0
- data/spec/models/inheritance.rb +56 -0
- data/spec/models/location.rb +5 -0
- data/spec/models/mixed_drink.rb +4 -0
- data/spec/models/name.rb +13 -0
- data/spec/models/namespacing.rb +11 -0
- data/spec/models/patient.rb +4 -0
- data/spec/models/person.rb +98 -0
- data/spec/models/pet.rb +7 -0
- data/spec/models/pet_owner.rb +6 -0
- data/spec/models/phone.rb +7 -0
- data/spec/models/post.rb +15 -0
- data/spec/models/translation.rb +5 -0
- data/spec/models/vet_visit.rb +5 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/unit/mongoid/associations/belongs_to_related_spec.rb +141 -0
- data/spec/unit/mongoid/associations/belongs_to_spec.rb +193 -0
- data/spec/unit/mongoid/associations/has_many_related_spec.rb +387 -0
- data/spec/unit/mongoid/associations/has_many_spec.rb +471 -0
- data/spec/unit/mongoid/associations/has_one_related_spec.rb +179 -0
- data/spec/unit/mongoid/associations/has_one_spec.rb +282 -0
- data/spec/unit/mongoid/associations/options_spec.rb +191 -0
- data/spec/unit/mongoid/associations_spec.rb +545 -0
- data/spec/unit/mongoid/attributes_spec.rb +484 -0
- data/spec/unit/mongoid/callbacks_spec.rb +55 -0
- data/spec/unit/mongoid/collection_spec.rb +171 -0
- data/spec/unit/mongoid/collections/cyclic_iterator_spec.rb +75 -0
- data/spec/unit/mongoid/collections/master_spec.rb +41 -0
- data/spec/unit/mongoid/collections/mimic_spec.rb +43 -0
- data/spec/unit/mongoid/collections/slaves_spec.rb +81 -0
- data/spec/unit/mongoid/commands/create_spec.rb +30 -0
- data/spec/unit/mongoid/commands/delete_all_spec.rb +58 -0
- data/spec/unit/mongoid/commands/delete_spec.rb +35 -0
- data/spec/unit/mongoid/commands/destroy_all_spec.rb +23 -0
- data/spec/unit/mongoid/commands/destroy_spec.rb +44 -0
- data/spec/unit/mongoid/commands/save_spec.rb +105 -0
- data/spec/unit/mongoid/commands_spec.rb +282 -0
- data/spec/unit/mongoid/config_spec.rb +165 -0
- data/spec/unit/mongoid/contexts/enumerable_spec.rb +374 -0
- data/spec/unit/mongoid/contexts/mongo_spec.rb +505 -0
- data/spec/unit/mongoid/contexts_spec.rb +25 -0
- data/spec/unit/mongoid/criteria_spec.rb +769 -0
- data/spec/unit/mongoid/criterion/complex_spec.rb +19 -0
- data/spec/unit/mongoid/criterion/exclusion_spec.rb +91 -0
- data/spec/unit/mongoid/criterion/inclusion_spec.rb +211 -0
- data/spec/unit/mongoid/criterion/optional_spec.rb +329 -0
- data/spec/unit/mongoid/cursor_spec.rb +74 -0
- data/spec/unit/mongoid/document_spec.rb +986 -0
- data/spec/unit/mongoid/enslavement_spec.rb +63 -0
- data/spec/unit/mongoid/errors_spec.rb +103 -0
- data/spec/unit/mongoid/extensions/array/accessors_spec.rb +50 -0
- data/spec/unit/mongoid/extensions/array/assimilation_spec.rb +24 -0
- data/spec/unit/mongoid/extensions/array/conversions_spec.rb +35 -0
- data/spec/unit/mongoid/extensions/array/parentization_spec.rb +20 -0
- data/spec/unit/mongoid/extensions/boolean/conversions_spec.rb +49 -0
- data/spec/unit/mongoid/extensions/date/conversions_spec.rb +102 -0
- data/spec/unit/mongoid/extensions/datetime/conversions_spec.rb +70 -0
- data/spec/unit/mongoid/extensions/float/conversions_spec.rb +61 -0
- data/spec/unit/mongoid/extensions/hash/accessors_spec.rb +184 -0
- data/spec/unit/mongoid/extensions/hash/assimilation_spec.rb +46 -0
- data/spec/unit/mongoid/extensions/hash/conversions_spec.rb +21 -0
- data/spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb +17 -0
- data/spec/unit/mongoid/extensions/hash/scoping_spec.rb +14 -0
- data/spec/unit/mongoid/extensions/integer/conversions_spec.rb +61 -0
- data/spec/unit/mongoid/extensions/nil/assimilation_spec.rb +24 -0
- data/spec/unit/mongoid/extensions/object/conversions_spec.rb +43 -0
- data/spec/unit/mongoid/extensions/proc/scoping_spec.rb +34 -0
- data/spec/unit/mongoid/extensions/string/conversions_spec.rb +17 -0
- data/spec/unit/mongoid/extensions/string/inflections_spec.rb +208 -0
- data/spec/unit/mongoid/extensions/symbol/inflections_spec.rb +91 -0
- data/spec/unit/mongoid/extensions/time/conversions_spec.rb +70 -0
- data/spec/unit/mongoid/factory_spec.rb +31 -0
- data/spec/unit/mongoid/field_spec.rb +81 -0
- data/spec/unit/mongoid/fields_spec.rb +158 -0
- data/spec/unit/mongoid/finders_spec.rb +368 -0
- data/spec/unit/mongoid/identity_spec.rb +88 -0
- data/spec/unit/mongoid/indexes_spec.rb +93 -0
- data/spec/unit/mongoid/matchers/all_spec.rb +27 -0
- data/spec/unit/mongoid/matchers/default_spec.rb +27 -0
- data/spec/unit/mongoid/matchers/exists_spec.rb +56 -0
- data/spec/unit/mongoid/matchers/gt_spec.rb +39 -0
- data/spec/unit/mongoid/matchers/gte_spec.rb +49 -0
- data/spec/unit/mongoid/matchers/in_spec.rb +27 -0
- data/spec/unit/mongoid/matchers/lt_spec.rb +39 -0
- data/spec/unit/mongoid/matchers/lte_spec.rb +49 -0
- data/spec/unit/mongoid/matchers/ne_spec.rb +27 -0
- data/spec/unit/mongoid/matchers/nin_spec.rb +27 -0
- data/spec/unit/mongoid/matchers/size_spec.rb +27 -0
- data/spec/unit/mongoid/matchers_spec.rb +329 -0
- data/spec/unit/mongoid/memoization_spec.rb +75 -0
- data/spec/unit/mongoid/named_scope_spec.rb +123 -0
- data/spec/unit/mongoid/scope_spec.rb +240 -0
- data/spec/unit/mongoid/timestamps_spec.rb +25 -0
- data/spec/unit/mongoid/versioning_spec.rb +41 -0
- data/spec/unit/mongoid_spec.rb +37 -0
- metadata +431 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Humanoid #:nodoc:
|
|
3
|
+
module Callbacks
|
|
4
|
+
def self.included(base)
|
|
5
|
+
base.class_eval do
|
|
6
|
+
include ActiveSupport::Callbacks
|
|
7
|
+
|
|
8
|
+
# Define all the callbacks that are accepted by the document.
|
|
9
|
+
define_callbacks \
|
|
10
|
+
:before_create,
|
|
11
|
+
:after_create,
|
|
12
|
+
:before_destroy,
|
|
13
|
+
:after_destroy,
|
|
14
|
+
:before_save,
|
|
15
|
+
:after_save,
|
|
16
|
+
:before_update,
|
|
17
|
+
:after_update,
|
|
18
|
+
:before_validation,
|
|
19
|
+
:after_validation
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require "humanoid/collections/operations"
|
|
3
|
+
require "humanoid/collections/cyclic_iterator"
|
|
4
|
+
require "humanoid/collections/mimic"
|
|
5
|
+
require "humanoid/collections/master"
|
|
6
|
+
require "humanoid/collections/slaves"
|
|
7
|
+
|
|
8
|
+
module Humanoid #: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::PROXIED)
|
|
20
|
+
|
|
21
|
+
# Determines where to send the next read query. If the slaves are not
|
|
22
|
+
# defined then send to master. If the read counter is under the configured
|
|
23
|
+
# maximum then return the master. In any other case return the slaves.
|
|
24
|
+
#
|
|
25
|
+
# Example:
|
|
26
|
+
#
|
|
27
|
+
# <tt>collection.directed</tt>
|
|
28
|
+
#
|
|
29
|
+
# Return:
|
|
30
|
+
#
|
|
31
|
+
# Either a +Master+ or +Slaves+ collection.
|
|
32
|
+
def directed(options = {})
|
|
33
|
+
enslave = options.delete(:enslave) || @klass.enslaved?
|
|
34
|
+
enslave ? master_or_slaves : master
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Find documents from the database given a selector and options.
|
|
38
|
+
#
|
|
39
|
+
# Options:
|
|
40
|
+
#
|
|
41
|
+
# selector: A +Hash+ selector that is the query.
|
|
42
|
+
# options: The options to pass to the db.
|
|
43
|
+
#
|
|
44
|
+
# Example:
|
|
45
|
+
#
|
|
46
|
+
# <tt>collection.find({ :test => "value" })</tt>
|
|
47
|
+
def find(selector = {}, options = {})
|
|
48
|
+
cursor = Humanoid::Cursor.new(@klass, self, directed(options).find(selector, options))
|
|
49
|
+
if block_given?
|
|
50
|
+
yield cursor; cursor.close
|
|
51
|
+
else
|
|
52
|
+
cursor
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Find the first document from the database given a selector and options.
|
|
57
|
+
#
|
|
58
|
+
# Options:
|
|
59
|
+
#
|
|
60
|
+
# selector: A +Hash+ selector that is the query.
|
|
61
|
+
# options: The options to pass to the db.
|
|
62
|
+
#
|
|
63
|
+
# Example:
|
|
64
|
+
#
|
|
65
|
+
# <tt>collection.find_one({ :test => "value" })</tt>
|
|
66
|
+
def find_one(selector = {}, options = {})
|
|
67
|
+
directed(options).find_one(selector, options)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Initialize a new Humanoid::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>Humanoid::Collection.new(masters, slaves, "test")</tt>
|
|
76
|
+
def initialize(klass, name)
|
|
77
|
+
@klass, @name = klass, name
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Perform a map/reduce on the documents.
|
|
81
|
+
#
|
|
82
|
+
# Options:
|
|
83
|
+
#
|
|
84
|
+
# map: The map javascript funcdtion.
|
|
85
|
+
# reduce: The reduce javascript function.
|
|
86
|
+
def map_reduce(map, reduce, options = {})
|
|
87
|
+
directed(options).map_reduce(map, reduce, options)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
alias :mapreduce :map_reduce
|
|
91
|
+
|
|
92
|
+
# Return the object responsible for writes to the database. This will
|
|
93
|
+
# always return a collection associated with the Master DB.
|
|
94
|
+
#
|
|
95
|
+
# Example:
|
|
96
|
+
#
|
|
97
|
+
# <tt>collection.writer</tt>
|
|
98
|
+
def master
|
|
99
|
+
@master ||= Collections::Master.new(Humanoid.master, @name)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Return the object responsible for reading documents from the database.
|
|
103
|
+
# This is usually the slave databases, but in their absence the master will
|
|
104
|
+
# handle the task.
|
|
105
|
+
#
|
|
106
|
+
# Example:
|
|
107
|
+
#
|
|
108
|
+
# <tt>collection.reader</tt>
|
|
109
|
+
def slaves
|
|
110
|
+
@slaves ||= Collections::Slaves.new(Humanoid.slaves, @name)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
protected
|
|
114
|
+
def master_or_slaves
|
|
115
|
+
slaves.empty? ? master : slaves
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Humanoid #: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 Humanoid #: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, "humanoid_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 Humanoid #: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 humanoid 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 < Humanoid.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
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Humanoid #:nodoc:
|
|
3
|
+
module Collections #:nodoc:
|
|
4
|
+
module Operations #:nodoc:
|
|
5
|
+
# Constant definining all the read operations available for a
|
|
6
|
+
# Mongo:Collection. This is used in delegation.
|
|
7
|
+
READ = [
|
|
8
|
+
:[],
|
|
9
|
+
:db,
|
|
10
|
+
:count,
|
|
11
|
+
:distinct,
|
|
12
|
+
:find,
|
|
13
|
+
:find_one,
|
|
14
|
+
:group,
|
|
15
|
+
:index_information,
|
|
16
|
+
:map_reduce,
|
|
17
|
+
:mapreduce,
|
|
18
|
+
:options
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
# Constant definining all the write operations available for a
|
|
22
|
+
# Mongo:Collection. This is used in delegation.
|
|
23
|
+
WRITE = [
|
|
24
|
+
:<<,
|
|
25
|
+
:create_index,
|
|
26
|
+
:drop,
|
|
27
|
+
:drop_index,
|
|
28
|
+
:drop_indexes,
|
|
29
|
+
:insert,
|
|
30
|
+
:remove,
|
|
31
|
+
:rename,
|
|
32
|
+
:save,
|
|
33
|
+
:update
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
# Convenience constant for getting back all collection operations.
|
|
37
|
+
ALL = (READ + WRITE)
|
|
38
|
+
PROXIED = ALL - [ :find, :find_one, :map_reduce, :mapreduce ]
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Humanoid #:nodoc:
|
|
3
|
+
module Collections #:nodoc:
|
|
4
|
+
class Slaves
|
|
5
|
+
include Mimic
|
|
6
|
+
|
|
7
|
+
attr_reader :iterator
|
|
8
|
+
|
|
9
|
+
# All read operations should delegate to the slave connections.
|
|
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::READ)
|
|
16
|
+
|
|
17
|
+
# Is the collection of slaves empty or not?
|
|
18
|
+
#
|
|
19
|
+
# Return:
|
|
20
|
+
#
|
|
21
|
+
# True is the iterator is not set, false if not.
|
|
22
|
+
def empty?
|
|
23
|
+
@iterator.nil?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Create the new database reader. Will create a collection from the
|
|
27
|
+
# slave databases and cycle through them on each read.
|
|
28
|
+
#
|
|
29
|
+
# Example:
|
|
30
|
+
#
|
|
31
|
+
# <tt>Reader.new(slaves, "humanoid_people")</tt>
|
|
32
|
+
def initialize(slaves, name)
|
|
33
|
+
unless slaves.blank?
|
|
34
|
+
@iterator = CyclicIterator.new(slaves.collect { |db| db.collection(name) })
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
protected
|
|
39
|
+
def collection
|
|
40
|
+
@iterator.next
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require "humanoid/commands/create"
|
|
3
|
+
require "humanoid/commands/deletion"
|
|
4
|
+
require "humanoid/commands/delete"
|
|
5
|
+
require "humanoid/commands/delete_all"
|
|
6
|
+
require "humanoid/commands/destroy"
|
|
7
|
+
require "humanoid/commands/destroy_all"
|
|
8
|
+
require "humanoid/commands/save"
|
|
9
|
+
|
|
10
|
+
module Humanoid #:nodoc:
|
|
11
|
+
|
|
12
|
+
# This module is included in the +Document+ to provide all the persistence
|
|
13
|
+
# methods required on the +Document+ object and class.
|
|
14
|
+
module Commands
|
|
15
|
+
def self.included(base)
|
|
16
|
+
base.class_eval do
|
|
17
|
+
include InstanceMethods
|
|
18
|
+
extend ClassMethods
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
module InstanceMethods
|
|
23
|
+
|
|
24
|
+
# Delete the +Document+ from the database. This method is an optimized
|
|
25
|
+
# delete that does not force any callbacks.
|
|
26
|
+
#
|
|
27
|
+
# Example:
|
|
28
|
+
#
|
|
29
|
+
# <tt>document.delete</tt>
|
|
30
|
+
#
|
|
31
|
+
# Returns: true unless an error occurs.
|
|
32
|
+
def delete
|
|
33
|
+
Delete.execute(self)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Destroy the +Document+. This will delete the document from the database
|
|
37
|
+
# and run the before and after destroy callbacks.
|
|
38
|
+
#
|
|
39
|
+
# Example:
|
|
40
|
+
#
|
|
41
|
+
# <tt>document.destroy</tt>
|
|
42
|
+
#
|
|
43
|
+
# Returns: true unless an error occurs.
|
|
44
|
+
def destroy
|
|
45
|
+
Destroy.execute(self)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Save the +Document+. If the document is new, then the before and after
|
|
49
|
+
# create callbacks will get executed as well as the save callbacks.
|
|
50
|
+
# Otherwise only the save callbacks will run.
|
|
51
|
+
#
|
|
52
|
+
# Options:
|
|
53
|
+
#
|
|
54
|
+
# validate: Run validations or not. Defaults to true.
|
|
55
|
+
#
|
|
56
|
+
# Example:
|
|
57
|
+
#
|
|
58
|
+
# <tt>document.save # save with validations</tt>
|
|
59
|
+
# <tt>document.save(false) # save without validations</tt>
|
|
60
|
+
#
|
|
61
|
+
# Returns: true if validation passes, false if not.
|
|
62
|
+
def save(validate = true)
|
|
63
|
+
new = new_record?
|
|
64
|
+
run_callbacks(:before_create) if new
|
|
65
|
+
begin
|
|
66
|
+
saved = Save.execute(self, validate)
|
|
67
|
+
rescue Mongo::OperationFailure => e
|
|
68
|
+
errors.add(:humanoid, e.message)
|
|
69
|
+
end
|
|
70
|
+
run_callbacks(:after_create) if new && saved
|
|
71
|
+
saved
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Save the +Document+, dangerously. Before and after save callbacks will
|
|
75
|
+
# get run. If validation fails an error will get raised.
|
|
76
|
+
#
|
|
77
|
+
# Example:
|
|
78
|
+
#
|
|
79
|
+
# <tt>document.save!</tt>
|
|
80
|
+
#
|
|
81
|
+
# Returns: true if validation passes
|
|
82
|
+
def save!
|
|
83
|
+
return save(true) || (raise Errors::Validations.new(self.errors))
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Update the document attributes and persist the document to the
|
|
87
|
+
# database. Will delegate to save with all callbacks.
|
|
88
|
+
#
|
|
89
|
+
# Example:
|
|
90
|
+
#
|
|
91
|
+
# <tt>document.update_attributes(:title => "Test")</tt>
|
|
92
|
+
def update_attributes(attrs = {})
|
|
93
|
+
set_attributes(attrs); save
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Update the document attributes and persist the document to the
|
|
97
|
+
# database. Will delegate to save!
|
|
98
|
+
#
|
|
99
|
+
# Example:
|
|
100
|
+
#
|
|
101
|
+
# <tt>document.update_attributes!(:title => "Test")</tt>
|
|
102
|
+
def update_attributes!(attrs = {})
|
|
103
|
+
set_attributes(attrs); save!
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
protected
|
|
107
|
+
def set_attributes(attrs = {})
|
|
108
|
+
run_callbacks(:before_update)
|
|
109
|
+
write_attributes(attrs)
|
|
110
|
+
run_callbacks(:after_update)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
module ClassMethods
|
|
116
|
+
|
|
117
|
+
# Create a new +Document+. This will instantiate a new document and save
|
|
118
|
+
# it in a single call. Will always return the document whether save
|
|
119
|
+
# passed or not.
|
|
120
|
+
#
|
|
121
|
+
# Example:
|
|
122
|
+
#
|
|
123
|
+
# <tt>Person.create(:title => "Mr")</tt>
|
|
124
|
+
#
|
|
125
|
+
# Returns: the +Document+.
|
|
126
|
+
def create(attributes = {})
|
|
127
|
+
document = new(attributes)
|
|
128
|
+
begin
|
|
129
|
+
Create.execute(document)
|
|
130
|
+
rescue Mongo::OperationFailure => e
|
|
131
|
+
document.errors.add(:humanoid, e.message)
|
|
132
|
+
end
|
|
133
|
+
document
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Create a new +Document+. This will instantiate a new document and save
|
|
137
|
+
# it in a single call. Will always return the document whether save
|
|
138
|
+
# passed or not. Will raise an error if validation fails.
|
|
139
|
+
#
|
|
140
|
+
# Example:
|
|
141
|
+
#
|
|
142
|
+
# <tt>Person.create!(:title => "Mr")</tt>
|
|
143
|
+
#
|
|
144
|
+
# Returns: the +Document+.
|
|
145
|
+
def create!(attributes = {})
|
|
146
|
+
document = Create.execute(new(attributes), true)
|
|
147
|
+
raise Errors::Validations.new(document.errors) unless document.errors.empty?
|
|
148
|
+
document
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Delete all documents given the supplied conditions. If no conditions
|
|
152
|
+
# are passed, the entire collection will be dropped for performance
|
|
153
|
+
# benefits. Does not fire any callbacks.
|
|
154
|
+
#
|
|
155
|
+
# Example:
|
|
156
|
+
#
|
|
157
|
+
# <tt>Person.delete_all(:conditions => { :title => "Sir" })</tt>
|
|
158
|
+
# <tt>Person.delete_all</tt>
|
|
159
|
+
#
|
|
160
|
+
# Returns: true or raises an error.
|
|
161
|
+
def delete_all(conditions = {})
|
|
162
|
+
DeleteAll.execute(self, conditions)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Delete all documents given the supplied conditions. If no conditions
|
|
166
|
+
# are passed, the entire collection will be dropped for performance
|
|
167
|
+
# benefits. Fires the destroy callbacks if conditions were passed.
|
|
168
|
+
#
|
|
169
|
+
# Example:
|
|
170
|
+
#
|
|
171
|
+
# <tt>Person.destroy_all(:conditions => { :title => "Sir" })</tt>
|
|
172
|
+
# <tt>Person.destroy_all</tt>
|
|
173
|
+
#
|
|
174
|
+
# Returns: true or raises an error.
|
|
175
|
+
def destroy_all(conditions = {})
|
|
176
|
+
DestroyAll.execute(self, conditions)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
end
|
|
182
|
+
end
|