humanoid 1.2.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|