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,39 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Humanoid #:nodoc:
|
|
3
|
+
class Identity #:nodoc:
|
|
4
|
+
class << self
|
|
5
|
+
# Create the identity for the +Document+.
|
|
6
|
+
#
|
|
7
|
+
# The id will be set in either in the form of a Mongo
|
|
8
|
+
# +ObjectID+ or a composite key set up by defining a key on the document.
|
|
9
|
+
#
|
|
10
|
+
# The _type will be set to the document's class name.
|
|
11
|
+
def create(doc)
|
|
12
|
+
identify(doc); type(doc); doc
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
protected
|
|
16
|
+
# Return the proper id for the document.
|
|
17
|
+
def generate_id
|
|
18
|
+
id = Mongo::ObjectID.new
|
|
19
|
+
Humanoid.use_object_ids ? id : id.to_s
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Set the id for the document.
|
|
23
|
+
def identify(doc)
|
|
24
|
+
doc.id = compose(doc).join(" ").identify if doc.primary_key
|
|
25
|
+
doc.id = generate_id unless doc.id
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Set the _type field on the document.
|
|
29
|
+
def type(doc)
|
|
30
|
+
doc._type = doc.class.name
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Generates the composite key for a document.
|
|
34
|
+
def compose(doc)
|
|
35
|
+
doc.primary_key.collect { |key| doc.attributes[key] }.reject { |val| val.nil? }
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Humanoid #:nodoc
|
|
3
|
+
module Indexes #:nodoc
|
|
4
|
+
def self.included(base)
|
|
5
|
+
base.class_eval do
|
|
6
|
+
extend ClassMethods
|
|
7
|
+
|
|
8
|
+
cattr_accessor :indexed
|
|
9
|
+
self.indexed = false
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module ClassMethods #:nodoc
|
|
14
|
+
# Add the default indexes to the root document if they do not already
|
|
15
|
+
# exist. Currently this is only _type.
|
|
16
|
+
def add_indexes
|
|
17
|
+
unless indexed
|
|
18
|
+
self._collection.create_index(:_type, false)
|
|
19
|
+
self.indexed = true
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Adds an index on the field specified. Options can be :unique => true or
|
|
24
|
+
# :unique => false. It will default to the latter.
|
|
25
|
+
def index(name, options = { :unique => false })
|
|
26
|
+
collection.create_index(name, options[:unique])
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require "humanoid/matchers/default"
|
|
3
|
+
require "humanoid/matchers/all"
|
|
4
|
+
require "humanoid/matchers/exists"
|
|
5
|
+
require "humanoid/matchers/gt"
|
|
6
|
+
require "humanoid/matchers/gte"
|
|
7
|
+
require "humanoid/matchers/in"
|
|
8
|
+
require "humanoid/matchers/lt"
|
|
9
|
+
require "humanoid/matchers/lte"
|
|
10
|
+
require "humanoid/matchers/ne"
|
|
11
|
+
require "humanoid/matchers/nin"
|
|
12
|
+
require "humanoid/matchers/size"
|
|
13
|
+
|
|
14
|
+
module Humanoid #:nodoc:
|
|
15
|
+
module Matchers
|
|
16
|
+
# Determines if this document has the attributes to match the supplied
|
|
17
|
+
# MongoDB selector. Used for matching on embedded associations.
|
|
18
|
+
def matches?(selector)
|
|
19
|
+
selector.each_pair do |key, value|
|
|
20
|
+
return false unless matcher(key, value).matches?(value)
|
|
21
|
+
end
|
|
22
|
+
true
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
protected
|
|
26
|
+
# Get the matcher for the supplied key and value. Will determine the class
|
|
27
|
+
# name from the key.
|
|
28
|
+
def matcher(key, value)
|
|
29
|
+
if value.is_a?(Hash)
|
|
30
|
+
name = "Humanoid::Matchers::#{value.keys.first.gsub("$", "").camelize}"
|
|
31
|
+
return name.constantize.new(attributes[key])
|
|
32
|
+
end
|
|
33
|
+
Default.new(attributes[key])
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Humanoid #:nodoc:
|
|
3
|
+
module Matchers #:nodoc:
|
|
4
|
+
class Default
|
|
5
|
+
# Creating a new matcher only requires the value.
|
|
6
|
+
def initialize(attribute)
|
|
7
|
+
@attribute = attribute
|
|
8
|
+
end
|
|
9
|
+
# Return true if the attribute and value are equal.
|
|
10
|
+
def matches?(value)
|
|
11
|
+
@attribute == value
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
protected
|
|
15
|
+
# Return the first value in the hash.
|
|
16
|
+
def first(value)
|
|
17
|
+
value.values.first
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# If object exists then compare, else return false
|
|
21
|
+
def determine(value, operator)
|
|
22
|
+
@attribute ? @attribute.send(operator, first(value)) : false
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Humanoid #:nodoc:
|
|
3
|
+
module Matchers #:nodoc:
|
|
4
|
+
class Exists < Default
|
|
5
|
+
# Return true if the attribute exists and checking for existence or
|
|
6
|
+
# return true if the attribute does not exist and checking for
|
|
7
|
+
# non-existence.
|
|
8
|
+
def matches?(value)
|
|
9
|
+
@attribute.nil? != value.values.first
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Humanoid #:nodoc
|
|
2
|
+
module Memoization
|
|
3
|
+
|
|
4
|
+
# Handles cases when accessing an association that should be memoized in
|
|
5
|
+
# the Humanoid specific manner.
|
|
6
|
+
def memoized(name, &block)
|
|
7
|
+
var = "@#{name}"
|
|
8
|
+
if instance_variable_defined?(var)
|
|
9
|
+
return instance_variable_get(var)
|
|
10
|
+
end
|
|
11
|
+
value = yield
|
|
12
|
+
instance_variable_set(var, value)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Humanoid specific behavior is to remove the memoized object when setting
|
|
16
|
+
# the association, or if it wasn't previously memoized it will get set.
|
|
17
|
+
def reset(name, &block)
|
|
18
|
+
var = "@#{name}"
|
|
19
|
+
value = yield
|
|
20
|
+
if instance_variable_defined?(var)
|
|
21
|
+
remove_instance_variable(var)
|
|
22
|
+
else
|
|
23
|
+
instance_variable_set(var, value)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Humanoid #:nodoc:
|
|
3
|
+
module NamedScope
|
|
4
|
+
# Creates a named_scope for the +Document+, similar to ActiveRecord's
|
|
5
|
+
# named_scopes. +NamedScopes+ are proxied +Criteria+ objects that can be
|
|
6
|
+
# chained.
|
|
7
|
+
#
|
|
8
|
+
# Example:
|
|
9
|
+
#
|
|
10
|
+
# class Person
|
|
11
|
+
# include Humanoid::Document
|
|
12
|
+
# field :active, :type => Boolean
|
|
13
|
+
# field :count, :type => Integer
|
|
14
|
+
#
|
|
15
|
+
# named_scope :active, :where => { :active => true }
|
|
16
|
+
# named_scope :count_gt_one, :where => { :count.gt => 1 }
|
|
17
|
+
# named_scope :at_least_count, lambda { |count| { :where => { :count.gt => count } } }
|
|
18
|
+
# end
|
|
19
|
+
def named_scope(name, options = {}, &block)
|
|
20
|
+
name = name.to_sym
|
|
21
|
+
scopes[name] = lambda do |parent, *args|
|
|
22
|
+
Scope.new(parent, options.scoped(*args), &block)
|
|
23
|
+
end
|
|
24
|
+
(class << self; self; end).class_eval <<-EOT
|
|
25
|
+
def #{name}(*args)
|
|
26
|
+
scopes[:#{name}].call(self, *args)
|
|
27
|
+
end
|
|
28
|
+
EOT
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
alias :scope :named_scope
|
|
32
|
+
|
|
33
|
+
# Return the scopes or default to an empty +Hash+.
|
|
34
|
+
def scopes
|
|
35
|
+
read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Humanoid #:nodoc:
|
|
3
|
+
class Scope #:nodoc:
|
|
4
|
+
|
|
5
|
+
delegate :scopes, :to => :parent
|
|
6
|
+
|
|
7
|
+
attr_reader :parent, :conditions
|
|
8
|
+
|
|
9
|
+
# If the other is a scope then compare the parent and conditions, otherwise
|
|
10
|
+
# if its enumerable collect and compare.
|
|
11
|
+
def ==(other)
|
|
12
|
+
case other
|
|
13
|
+
when Scope
|
|
14
|
+
@parent == other.parent && @conditions == other.conditions
|
|
15
|
+
when Enumerable
|
|
16
|
+
@collection ||= entries
|
|
17
|
+
return (@collection == other)
|
|
18
|
+
else
|
|
19
|
+
return false
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Create the new +Scope+. If a block is passed in, this Scope will extend
|
|
24
|
+
# the block.
|
|
25
|
+
#
|
|
26
|
+
# Options:
|
|
27
|
+
#
|
|
28
|
+
# parent: The class the scope belongs to, or a parent +Scope+.
|
|
29
|
+
# conditions: A +Hash+ of conditions.
|
|
30
|
+
#
|
|
31
|
+
# Example:
|
|
32
|
+
#
|
|
33
|
+
# Humanoid::Scope.new(Person, { :title => "Sir" }) do
|
|
34
|
+
# def knighted?
|
|
35
|
+
# title == "Sir"
|
|
36
|
+
# end
|
|
37
|
+
# end
|
|
38
|
+
def initialize(parent, conditions, &block)
|
|
39
|
+
@parent, @conditions = parent, conditions
|
|
40
|
+
extend Module.new(&block) if block_given?
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Return the class for the +Scope+. This will be the parent if the parent
|
|
44
|
+
# is a class, otherwise will be nil.
|
|
45
|
+
def klass
|
|
46
|
+
@klass ||= @parent unless @parent.is_a?(Scope)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Chaining is supported through method_missing. If a scope is already
|
|
50
|
+
# defined with the method name the call will be passed there, otherwise it
|
|
51
|
+
# will be passed to the target or parent.
|
|
52
|
+
def method_missing(name, *args, &block)
|
|
53
|
+
if scopes.include?(name)
|
|
54
|
+
scopes[name].call(self, *args)
|
|
55
|
+
elsif klass
|
|
56
|
+
target.send(name, *args, &block)
|
|
57
|
+
else
|
|
58
|
+
@parent.fuse(@conditions); @parent.send(name, *args, &block)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# The +Scope+ must respond like a +Criteria+ object. If this is a parent
|
|
63
|
+
# criteria delegate to the target, otherwise bubble up to the parent.
|
|
64
|
+
def respond_to?(name)
|
|
65
|
+
super || (klass ? target.respond_to?(name) : @parent.respond_to?(name))
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Returns the target criteria if it has already been set or creates a new
|
|
69
|
+
# criteria from the parent class.
|
|
70
|
+
def target
|
|
71
|
+
@target ||= klass.criteria.fuse(@conditions)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Humanoid
|
|
3
|
+
module Timestamps
|
|
4
|
+
|
|
5
|
+
def self.included(base)
|
|
6
|
+
base.class_eval do
|
|
7
|
+
include InstanceMethods
|
|
8
|
+
field :created_at, :type => Time
|
|
9
|
+
field :updated_at, :type => Time
|
|
10
|
+
before_save :set_created_at, :set_updated_at
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module InstanceMethods
|
|
15
|
+
|
|
16
|
+
# Update the created_at field on the Document to the current time. This is
|
|
17
|
+
# only called on create.
|
|
18
|
+
def set_created_at
|
|
19
|
+
self.created_at = Time.now.utc if !created_at
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Update the updated_at field on the Document to the current time.
|
|
23
|
+
# This is only called on create and on save.
|
|
24
|
+
def set_updated_at
|
|
25
|
+
self.updated_at = Time.now.utc
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Humanoid #:nodoc:
|
|
3
|
+
# Include this module to get automatic versioning of root level documents.
|
|
4
|
+
# This will add a version field to the +Document+ and a has_many association
|
|
5
|
+
# with all the versions contained in it.
|
|
6
|
+
module Versioning
|
|
7
|
+
def self.included(base)
|
|
8
|
+
base.class_eval do
|
|
9
|
+
field :version, :type => Integer, :default => 1
|
|
10
|
+
has_many :versions, :class_name => self.name
|
|
11
|
+
before_save :revise
|
|
12
|
+
include InstanceMethods
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
module InstanceMethods
|
|
16
|
+
# Create a new version of the +Document+. This will load the previous
|
|
17
|
+
# document from the database and set it as the next version before saving
|
|
18
|
+
# the current document. It then increments the version number.
|
|
19
|
+
def revise
|
|
20
|
+
last_version = self.class.first(:conditions => { :_id => id, :version => version })
|
|
21
|
+
if last_version
|
|
22
|
+
self.versions << last_version.clone
|
|
23
|
+
self.version = version + 1
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|