mongodb_model 0.2.8 → 2.0.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.
data/Rakefile CHANGED
@@ -1,11 +1,10 @@
1
1
  require 'rake_ext'
2
2
 
3
- project(
3
+ project \
4
4
  name: "mongodb_model",
5
- # version: '0.2.1',
5
+ version: '2.0.0',
6
6
  gem: true,
7
7
  summary: "Object Model for MongoDB",
8
8
 
9
9
  author: "Alexey Petrushin",
10
- homepage: "http://alexeypetrushin.github.com/mongodb_model"
11
- )
10
+ homepage: "http://alexeypetrushin.github.com/mongodb_model"
@@ -1,3 +1,5 @@
1
+ require 'mongo/model/support/conversions'
2
+
1
3
  module Mongo::Model::Assignment
2
4
  class Dsl < BasicObject
3
5
  def initialize model
@@ -47,9 +49,9 @@ module Mongo::Model::Assignment
47
49
  dsl = ::Mongo::Model::Assignment::Dsl.new self
48
50
  dsl.instance_eval &block
49
51
  else
50
- args.size.must_be.in 2..3
52
+ args.size.must.be_in 2..3
51
53
  attr_name = args.shift
52
- attr_name.must_be.a Symbol
54
+ attr_name.must.be_a Symbol
53
55
 
54
56
  if args.first.is_a? Class
55
57
  type, mass_assignment = args
@@ -1,8 +1,33 @@
1
1
  module Mongo::Model::Callbacks
2
2
  inherit RubyExt::Callbacks
3
3
 
4
+ protected
5
+ def with_model_callbacks methods, options, models, &block
6
+ # Firing before callbacks.
7
+ unless options[:callbacks] == false
8
+ methods.each do |method|
9
+ models.each do |model|
10
+ return false unless model.run_before_callbacks method, method
11
+ end
12
+ end
13
+ end
14
+
15
+ result = block.call
16
+
17
+ # Firing after callbacks.
18
+ unless options[:callbacks] == false
19
+ methods.reverse.each do |method|
20
+ models.each do |model|
21
+ model.run_after_callbacks method, method
22
+ end
23
+ end
24
+ end
25
+
26
+ result
27
+ end
28
+
4
29
  module ClassMethods
5
- [:validate, :create, :update, :save, :destroy].each do |method_name|
30
+ [:validate, :create, :update, :save, :delete].each do |method_name|
6
31
  define_method "before_#{method_name}" do |*args, &block|
7
32
  opt = args.extract_options!
8
33
  if block
@@ -14,7 +39,7 @@ module Mongo::Model::Callbacks
14
39
  end
15
40
  end
16
41
 
17
- [:validate, :create, :update, :save, :destroy, :build].each do |method_name|
42
+ [:validate, :create, :update, :save, :delete, :build].each do |method_name|
18
43
  define_method "after_#{method_name}" do |*args, &block|
19
44
  opt = args.extract_options!
20
45
  if block
@@ -1,10 +1,10 @@
1
1
  module Mongo::Model::Conversion
2
2
  def model_to_json *args, &block
3
- to_rson(*args, &block).to_json
3
+ to_hash(*args, &block).to_json
4
4
  end
5
5
 
6
6
  def model_to_xml *args, &block
7
- to_rson(*args, &block).to_xml
7
+ to_hash(*args, &block).to_xml
8
8
  end
9
9
 
10
10
  alias_method :to_json, :model_to_json
@@ -14,24 +14,25 @@ module Mongo::Model::Conversion
14
14
  alias_method :to_json, :model_to_json
15
15
  alias_method :to_xml, :model_to_xml
16
16
 
17
- # hack to supress ActiveSupport as_json
17
+ # Hack to supress ActiveSupport as_json.
18
18
  alias_method :as_json, :model_to_json
19
19
  alias_method :as_xml, :model_to_xml
20
20
  end
21
21
 
22
- def to_rson options = {}
23
- # hack to suppress ActiveSupport as_json
24
- options ||= {}
22
+ def to_hash options = {}
23
+ # return super if args.empty?
24
+ #
25
+ # options = args.first
25
26
 
26
27
  if profile = options[:profile]
27
28
  raise "no other optins are allowed when using :profile option!" if options.size > 1
28
29
  profile_options = self.class.profiles[profile] || raise("profile :#{profile} not defined for #{self.class}!")
29
- to_rson profile_options.merge(_profile: profile)
30
+ to_hash profile_options.merge(_profile: profile)
30
31
  else
31
32
  options.validate_options! :only, :except, :methods, :errors, :_profile
32
33
  child_options = options[:_profile] ? {profile: options[:_profile]} : {}
33
34
 
34
- instance_variables = Mongo::Object.instance_variables(self)
35
+ instance_variables = self.persistent_instance_variable_names
35
36
 
36
37
  if only = options[:only]
37
38
  instance_variables &= Array(only).collect{|n| :"@#{n}"}
@@ -42,14 +43,14 @@ module Mongo::Model::Conversion
42
43
  result = {}
43
44
  instance_variables.each do |iv_name|
44
45
  value = instance_variable_get iv_name
45
- value = Mongo::Object.convert value, :to_rson, child_options
46
+ value = convert_model value, :to_hash, child_options
46
47
  result[iv_name[1.. -1]] = value
47
48
  end
48
49
 
49
50
  methods = options[:methods] ? Array(options[:methods]) : []
50
51
  methods.each do |method|
51
52
  value = send method
52
- value = Mongo::Object.convert value, :to_rson, child_options
53
+ value = convert_model value, :to_hash, child_options
53
54
  result[method.to_s] = value
54
55
  end
55
56
 
@@ -59,11 +60,25 @@ module Mongo::Model::Conversion
59
60
  errors.each{|k, v| stringified_errors[k.to_s] = v}
60
61
  result['errors'] = stringified_errors
61
62
  end
62
-
63
+
63
64
  result
64
65
  end
65
66
  end
66
67
 
68
+ protected
69
+ def convert_model obj, method, options
70
+ if obj.respond_to? :collect_with_value
71
+ # Array or Hash.
72
+ obj.collect_with_value{|v| convert_model v, method, options}
73
+ elsif obj.respond_to? method
74
+ # Model.
75
+ obj.send method, options
76
+ else
77
+ # Simple object.
78
+ obj
79
+ end
80
+ end
81
+
67
82
  module ClassMethods
68
83
  inheritable_accessor :profiles, {}
69
84
  def profile name, options = {}
@@ -1,4 +1,29 @@
1
1
  module Mongo::Model::Crud
2
+ # Enhancing Mongo::Object CRUD.
3
+
4
+ def create_object collection, options
5
+ with_model_crud_callbacks [:save, :create], options do |mongo_options|
6
+ super collection, mongo_options
7
+ true
8
+ end
9
+ end
10
+
11
+ def update_object collection, options
12
+ with_model_crud_callbacks [:save, :update], options do |mongo_options|
13
+ super collection, mongo_options
14
+ true
15
+ end
16
+ end
17
+
18
+ def delete_object collection, options
19
+ with_model_crud_callbacks [:delete], options do |mongo_options|
20
+ super collection, mongo_options
21
+ true
22
+ end
23
+ end
24
+
25
+ # Model CRUD.
26
+
2
27
  def save options = {}
3
28
  with_collection options do |collection, options|
4
29
  collection.save self, options
@@ -9,20 +34,38 @@ module Mongo::Model::Crud
9
34
  save(*args) || raise(Mongo::Error, "can't save invalid model (#{self.errors})!")
10
35
  end
11
36
 
12
- def destroy options = {}
37
+ def delete options = {}
13
38
  with_collection options do |collection, options|
14
- collection.destroy self, options
39
+ collection.delete self, options
15
40
  end
16
41
  end
17
42
 
18
- def destroy! *args
19
- destroy(*args) || raise(Mongo::Error, "can't destroy invalid model #{self.errors}!")
43
+ def delete! *args
44
+ delete(*args) || raise(Mongo::Error, "can't delete invalid model #{self.errors}!")
20
45
  end
21
46
 
22
47
  def update doc, options = {}
23
- self.class.collection.update({_id: _id}, doc, options)
48
+ with_collection options do |collection, options|
49
+ collection.update({_id: _id}, doc, options)
50
+ end
24
51
  end
25
52
 
53
+ protected
54
+ def with_model_crud_callbacks methods, options, &block
55
+ models = [self] + embedded_models(true)
56
+
57
+ return false if (options[:validate] != false) and invalid?(options)
58
+
59
+ with_model_callbacks methods, options, models do
60
+ mongo_options = options.clone
61
+ mongo_options.delete :validate
62
+ mongo_options.delete :callbacks
63
+
64
+ block.call mongo_options
65
+ end
66
+ end
67
+
68
+
26
69
  module ClassMethods
27
70
  def build attributes = {}, options = {}, &block
28
71
  model = self.new
@@ -43,14 +86,14 @@ module Mongo::Model::Crud
43
86
  model
44
87
  end
45
88
 
46
- def destroy_all selector = {}, options = {}
89
+ def delete_all selector = {}, options = {}
47
90
  success = true
48
- each(selector){|o| success = false unless o.destroy options}
91
+ each(selector){|o| success = false unless o.delete options}
49
92
  success
50
93
  end
51
94
 
52
- def destroy_all! selector = {}, options = {}
53
- destroy_all(selector, options) || raise(Mongo::Error, "can't destroy #{selector.inspect}!")
95
+ def delete_all! selector = {}, options = {}
96
+ delete_all(selector, options) || raise(Mongo::Error, "can't delete #{selector.inspect}!")
54
97
  end
55
98
 
56
99
  def update selector, doc, options = {}
@@ -23,7 +23,7 @@ module Mongo::Model::FileModel
23
23
 
24
24
  after_save{|model| model.send(attr_name).save!}
25
25
 
26
- after_destroy{|model| model.send(attr_name).destroy!}
26
+ after_delete{|model| model.send(attr_name).delete!}
27
27
  end
28
28
  end
29
29
  end
@@ -1,4 +1,4 @@
1
- class Mongo::Model::UniquenessValidator < Validatable::ValidationBase
1
+ class Validatable::UniquenessValidator < Validatable::ValidationBase
2
2
  attr_accessor :scope, :case_sensitive
3
3
 
4
4
  def initialize(klass, attribute, options={})
@@ -18,7 +18,7 @@ class Mongo::Model::UniquenessValidator < Validatable::ValidationBase
18
18
  conditions[attribute] = /^#{Regexp.escape(value.to_s)}$/i
19
19
  end
20
20
 
21
- # Make sure we're not including the current document in the query
21
+ # Make sure we're not including the current document in the query.
22
22
  conditions[:_id] = {_ne: instance._id} if instance._id
23
23
 
24
24
  !klass.exists?(conditions)
@@ -0,0 +1,41 @@
1
+ module Validatable::Model
2
+ def errors
3
+ @_errors ||= Validatable::Errors.new
4
+ end
5
+
6
+ protected
7
+ def run_validations_for_this_model_only
8
+ self.class.validations.each do |v|
9
+ if v.respond_to?(:validate)
10
+ v.validate self
11
+ elsif v.is_a? Proc
12
+ v.call self
13
+ else
14
+ send v
15
+ end
16
+ end
17
+ end
18
+
19
+ module ClassMethods
20
+ include ::Validatable::Macros
21
+
22
+ inheritable_accessor :validations, []
23
+
24
+ def validates_uniqueness_of *args
25
+ add_validations(args, ::Validatable::UniquenessValidator)
26
+ end
27
+
28
+ def validate validation = nil, &block
29
+ validations.push block || validation
30
+ end
31
+
32
+ protected
33
+ def add_validations(args, klass)
34
+ options = args.last.is_a?(Hash) ? args.pop : {}
35
+ args.each do |attribute|
36
+ new_validation = klass.new self, attribute, options
37
+ validate new_validation
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,59 @@
1
+ require 'mongo/object'
2
+ require 'ruby_ext'
3
+
4
+ module Mongo::Model; end
5
+
6
+ %w(
7
+ support
8
+
9
+ db
10
+ conversion
11
+ assignment
12
+ callbacks
13
+ validation
14
+ crud
15
+ query
16
+ query_mixin
17
+ scope
18
+ attribute_convertors
19
+ misc
20
+ model
21
+ ).each{|f| require "mongo/model/#{f}"}
22
+
23
+ module Mongo
24
+ module Model
25
+ autoload :FileModel, 'mongo/model/integration/file_model'
26
+
27
+ inherit \
28
+ Db,
29
+ Conversion,
30
+ Assignment,
31
+ Callbacks,
32
+ Validation,
33
+ Crud,
34
+ QueryMixin,
35
+ Scope,
36
+ AttributeConvertors,
37
+ Misc
38
+ end
39
+ end
40
+
41
+ Mongo.defaults.merge! \
42
+ convert_underscore_to_dollar: true,
43
+ batch_size: 50,
44
+ multi: true,
45
+ safe: true,
46
+ generate_id: true
47
+
48
+ # Integration with Rails.
49
+ unless $dont_use_rails
50
+ require 'mongo/model/integration/rails' if defined? Rails
51
+ end
52
+
53
+ # Integration with Validatable2
54
+ unless $dont_use_validatable
55
+ require 'validatable'
56
+ require 'mongo/model/integration/validatable'
57
+ require 'mongo/model/integration/validatable/uniqueness_validator'
58
+ Mongo::Model.inherit Validatable::Model
59
+ end
@@ -1,12 +1,6 @@
1
1
  module Mongo::Model
2
2
  include Mongo::Object
3
3
 
4
- attr_accessor :_id, :_class
5
-
6
- def _id?; !!_id end
7
- def new?; !_id end
8
- alias_method :new_record?, :new?
9
-
10
4
  inherited do
11
5
  unless is?(Array) or is?(Hash)
12
6
  alias_method :eql?, :model_eql?
@@ -14,6 +8,8 @@ module Mongo::Model
14
8
  end
15
9
  end
16
10
 
11
+ # Equality.
12
+
17
13
  def model_eql? o
18
14
  return true if equal? o
19
15
  self.class == o.class and self == o
@@ -21,22 +17,58 @@ module Mongo::Model
21
17
 
22
18
  def model_eq? o
23
19
  return true if equal? o
20
+ return false unless o.is_a? Mongo::Model
24
21
 
25
- variables = {}; ::Mongo::Object.each_instance_variable(self){|n, v| variables[n] = v}
26
- o_variables = {}; ::Mongo::Object.each_instance_variable(o){|n, v| o_variables[n] = v}
22
+ variables = {}.tap do |h|
23
+ persistent_instance_variable_names.each{|n| h[n] = instance_variable_get(n)}
24
+ end
25
+
26
+ o_variables = {}.tap do |h|
27
+ o.persistent_instance_variable_names.each{|n| h[n] = o.instance_variable_get(n)}
28
+ end
27
29
 
28
30
  variables == o_variables
29
31
  end
30
32
 
31
- # class << self
32
- # attr_accessor :db, :connection
33
- # attr_required :db, :connection
34
- #
35
- # # Override this method to provide custom alias to db name translation,
36
- # # for example db_name = my_config[alias_name]
37
- # def resolve_db_alias alias_name
38
- # db_name = alias_name.to_s
39
- # connection.db db_name
40
- # end
41
- # end
33
+ protected
34
+ # Traversing models.
35
+
36
+ def embedded_models recursive = false
37
+ [].tap{|a| each_embedded(recursive){|m| a.add m}}
38
+ end
39
+
40
+ def each_embedded recursive = false, &block
41
+ self.class.embedded.each do |name|
42
+ if o = instance_variable_get(name)
43
+ _each_embedded o, recursive, &block
44
+ end
45
+ end
46
+ end
47
+
48
+ # Hash or Array also can be the Model by itsef, so we need to check
49
+ # for presence both `:each_embedded` and `:each_value` methods.
50
+ def _each_embedded o, recursive, &block
51
+ # If o is model adding it and its children.
52
+ if o.respond_to? :each_embedded
53
+ block.call o
54
+ o.each_embedded recursive, &block if recursive
55
+ end
56
+
57
+ # If o is Hash or Array, iterating and adding all models from there,
58
+ # recursivelly, hashes and arrays can be nested in any order.
59
+ if o.respond_to? :each_value
60
+ o.each_value do |o|
61
+ _each_embedded o, recursive, &block
62
+ end
63
+ end
64
+ end
65
+
66
+ module ClassMethods
67
+ inheritable_accessor :_embedded, []
68
+
69
+ def embedded *list
70
+ list.collect!{|n| :"@#{n}"}
71
+ if list.empty? then _embedded else _embedded.push(*list) end
72
+ end
73
+ end
42
74
  end
@@ -1,4 +1,4 @@
1
- class Mongo::Model::Query < Object
1
+ class Mongo::Model::Query
2
2
  attr_reader :model_class, :selector, :options
3
3
 
4
4
  def initialize model_class, selector = {}, options = {}
@@ -53,10 +53,8 @@ module Mongo::Model::Scope
53
53
  end
54
54
  end
55
55
 
56
+ # Finders.
56
57
 
57
- #
58
- # finders
59
- #
60
58
  def count selector = {}, options = {}
61
59
  if current = current_scope
62
60
  super current.selector.merge(selector), current.options.merge(options)
@@ -81,10 +79,8 @@ module Mongo::Model::Scope
81
79
  end
82
80
  end
83
81
 
82
+ # Shortcuts for frequently used scopes.
84
83
 
85
- #
86
- # Handy scopes
87
- #
88
84
  def limit n; query({}, limit: n) end
89
85
  def skip n; query({}, skip: n) end
90
86
  def sort *list
@@ -5,9 +5,6 @@ rspec do
5
5
  class << self
6
6
  def with_mongo_model
7
7
  with_mongo
8
- #
9
- # before{Mongo::Model.db = mongo.db}
10
- # after{Mongo::Model.db = nil}
11
8
  end
12
9
  end
13
10
  end
@@ -1,6 +1,7 @@
1
- #
2
- # Boolean
3
- #
1
+ # Converting basic Ruby types to and from String, needed
2
+ # in mass assignment.
3
+
4
+ # Boolean.
4
5
  module Mongo::Model::BooleanType
5
6
  Mapping = {
6
7
  true => true,
@@ -37,10 +38,7 @@ class Boolean; end unless defined?(Boolean)
37
38
 
38
39
  Boolean.extend Mongo::Model::BooleanType
39
40
 
40
-
41
- #
42
- # Date
43
- #
41
+ # Date.
44
42
  require 'date'
45
43
  Date.class_eval do
46
44
  def self.cast value
@@ -55,20 +53,14 @@ Date.class_eval do
55
53
  end
56
54
  end
57
55
 
58
-
59
- #
60
- # Float
61
- #
56
+ # Float.
62
57
  Float.class_eval do
63
58
  def self.cast value
64
59
  value.nil? ? nil : value.to_f
65
60
  end
66
61
  end
67
62
 
68
-
69
- #
70
- # Integer
71
- #
63
+ # Integer.
72
64
  Integer.class_eval do
73
65
  def self.cast value
74
66
  value_to_i = value.to_i
@@ -80,20 +72,14 @@ Integer.class_eval do
80
72
  end
81
73
  end
82
74
 
83
-
84
- #
85
- # String
86
- #
75
+ # String.
87
76
  String.class_eval do
88
77
  def self.cast value
89
78
  value.nil? ? nil : value.to_s
90
79
  end
91
80
  end
92
81
 
93
-
94
- #
95
- # Time
96
- #
82
+ # Time.
97
83
  Time.class_eval do
98
84
  def self.cast value
99
85
  if value.nil? || value == ''
@@ -0,0 +1,11 @@
1
+ Array.class_eval do
2
+ alias_method :each_value, :each
3
+ alias_method :collect_with_value, :collect
4
+ end
5
+
6
+ Hash.class_eval do
7
+ def collect_with_value &block
8
+ {}.tap{|h| self.each{|k, v| h[k] = block.call v}}
9
+ end
10
+ end
11
+
@@ -1,41 +1,21 @@
1
1
  module Mongo::Model::Validation
2
- def errors
3
- @_errors ||= Validatable::Errors.new
2
+ def valid? options = {}
3
+ errors.clear
4
+ run_validations options
4
5
  end
6
+ def invalid?(options = {}); !valid?(options) end
5
7
 
6
- def run_model_validations
7
- self.class.validations.each do |v|
8
- if v.respond_to?(:validate)
9
- v.validate self
10
- elsif v.is_a? Proc
11
- v.call self
12
- else
13
- send v
14
- end
15
- end
16
- true
17
- end
8
+ protected
9
+ def run_validations options = {}
10
+ with_model_callbacks [:validate], options, [self] do
11
+ # Validating main model.
12
+ self.run_validations_for_this_model_only
13
+ result = errors.empty?
18
14
 
19
- module ClassMethods
20
- include ::Validatable::Macros
21
-
22
- inheritable_accessor :validations, []
23
-
24
- def validates_uniqueness_of *args
25
- add_validations(args, Mongo::Model::UniquenessValidator)
26
- end
27
-
28
- def validate validation = nil, &block
29
- validations.push block || validation
30
- end
31
-
32
- protected
33
- def add_validations(args, klass)
34
- options = args.last.is_a?(Hash) ? args.pop : {}
35
- args.each do |attribute|
36
- new_validation = klass.new self, attribute, options
37
- validate new_validation
15
+ # Validating embedded models.
16
+ embedded_models.reduce result do |result, model|
17
+ result &= model.valid?(options)
38
18
  end
39
19
  end
40
- end
20
+ end
41
21
  end