mongo_db 0.1.9 → 0.1.10

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 (51) hide show
  1. data/lib/mongo_db/driver/{core/collection.rb → collection.rb} +13 -5
  2. data/lib/mongo_db/driver/{core/database.rb → database.rb} +0 -0
  3. data/lib/mongo_db/driver/dynamic_finders.rb +41 -0
  4. data/lib/mongo_db/driver.rb +33 -2
  5. data/lib/mongo_db/migration/definition.rb +19 -0
  6. data/lib/mongo_db/migration/migration.rb +68 -0
  7. data/lib/mongo_db/migration/tasks.rb +19 -0
  8. data/lib/mongo_db/migration.rb +8 -0
  9. data/lib/mongo_db/model/assignment.rb +54 -0
  10. data/lib/mongo_db/model/callbacks.rb +36 -0
  11. data/lib/mongo_db/model/crud.rb +28 -0
  12. data/lib/mongo_db/model/db.rb +53 -0
  13. data/lib/mongo_db/model/misc.rb +14 -0
  14. data/lib/mongo_db/model/model.rb +10 -0
  15. data/lib/mongo_db/model/query.rb +36 -0
  16. data/lib/mongo_db/model/scope.rb +99 -0
  17. data/lib/mongo_db/model/spec.rb +12 -0
  18. data/lib/mongo_db/model/support/types.rb +110 -0
  19. data/lib/mongo_db/model/validation.rb +5 -0
  20. data/lib/mongo_db/model.rb +30 -0
  21. data/lib/mongo_db/object/object_serializer.rb +20 -21
  22. data/readme.md +132 -19
  23. data/spec/driver/{core/collection_spec.rb → collection_spec.rb} +13 -0
  24. data/spec/driver/{core/crud_spec.rb → crud_spec.rb} +0 -0
  25. data/spec/driver/{core/database_spec.rb → database_spec.rb} +0 -0
  26. data/spec/driver/dynamic_finders_spec.rb +50 -0
  27. data/spec/driver/{core/hash_helper_spec.rb → hash_helper_spec.rb} +0 -0
  28. data/spec/{model/example.rb → integration/am_conversion_spec.rb} +0 -0
  29. data/spec/integration/am_validation_spec.rb +40 -0
  30. data/spec/migration/migration_spec.rb +60 -0
  31. data/spec/model/assignment_spec.rb +79 -0
  32. data/spec/model/callbacks_spec.rb +47 -0
  33. data/spec/model/{model_crud.rb → crud_spec.rb} +46 -36
  34. data/spec/model/db_spec.rb +63 -0
  35. data/spec/model/misc_spec.rb +32 -0
  36. data/spec/model/query_spec.rb +47 -0
  37. data/spec/model/scope_spec.rb +149 -0
  38. data/spec/model/spec_helper.rb +4 -0
  39. data/spec/model/validation_spec.rb +37 -0
  40. data/spec/object/callbacks_spec.rb +6 -4
  41. data/spec/object/crud_shared.rb +1 -1
  42. data/spec/object/crud_spec.rb +15 -10
  43. data/spec/object/spec_helper.rb +3 -2
  44. data/spec/object/validation_spec.rb +4 -2
  45. metadata +39 -18
  46. data/lib/mongo_db/driver/core.rb +0 -29
  47. data/lib/mongo_db/driver/more/collection_finders.rb +0 -43
  48. data/lib/mongo_db/driver/more.rb +0 -10
  49. data/spec/driver/more/querying_spec.rb +0 -59
  50. data/spec/model/callbacks.rb +0 -100
  51. data/spec/test.rb +0 -10
@@ -38,7 +38,7 @@ module Mongo::CollectionExt
38
38
  def destroy *args
39
39
  remove *args
40
40
  end
41
-
41
+
42
42
  def create *args
43
43
  insert *args
44
44
  end
@@ -46,19 +46,23 @@ module Mongo::CollectionExt
46
46
  #
47
47
  # Querying
48
48
  #
49
- def first selector = nil, opts = {}
49
+ def first selector = {}, opts = {}
50
50
  selector = convert_underscore_to_dollar_in_selector selector if selector.is_a? Hash
51
51
 
52
52
  h = find_one selector, opts
53
53
  symbolize_doc h
54
54
  end
55
55
 
56
- def all *args, &block
56
+ def first! selector = {}, opts = {}
57
+ first(selector, opts) || raise(Mongo::NotFound, "document with selector #{selector} not found!")
58
+ end
59
+
60
+ def all selector = {}, opts = {}, &block
57
61
  if block
58
- each *args, &block
62
+ each selector, opts, &block
59
63
  else
60
64
  list = []
61
- each(*args){|doc| list << doc}
65
+ each(selector, opts){|doc| list << doc}
62
66
  list
63
67
  end
64
68
  end
@@ -80,6 +84,10 @@ module Mongo::CollectionExt
80
84
  nil
81
85
  end
82
86
 
87
+ def count_with_ext selector = {}, opts = {}
88
+ find(selector, opts).count()
89
+ end
90
+
83
91
  protected
84
92
  QUERY_KEYWORDS = [
85
93
  :_lt, :_lte, :_gt, :_gte,
@@ -0,0 +1,41 @@
1
+ module Mongo::DynamicFinders
2
+ protected
3
+ #
4
+ # first_by_field, all_by_field, each_by_field, first_by_field
5
+ #
6
+ def method_missing clause, *args, &block
7
+ if clause =~ /^([a-z]_by_[a-z_])|(by_[a-z_])/
8
+ clause = clause.to_s
9
+
10
+ bang = clause =~ /!$/
11
+ clause = clause[0..-2] if bang
12
+
13
+ finder, field = if clause =~ /^by_/
14
+ ['first', clause.sub(/by_/, '')]
15
+ else
16
+ clause.split(/_by_/, 2)
17
+ end
18
+
19
+ finder = 'first' if finder == 'find'
20
+ field = '_id' if field == 'id'
21
+
22
+ if bang
23
+ raise "You can't use bang version with :#{finder}!" unless finder == 'first'
24
+ finder = "#{finder}!"
25
+ end
26
+
27
+ raise "invalid arguments for finder (#{args})!" unless args.size == 1
28
+ field_value = args.first
29
+
30
+ finder, field = finder.to_sym, field.to_sym
31
+
32
+ if respond_to? finder
33
+ send finder, {field => field_value}, &block
34
+ else
35
+ super
36
+ end
37
+ else
38
+ super
39
+ end
40
+ end
41
+ end
@@ -1,2 +1,33 @@
1
- require 'mongo_db/driver/core'
2
- require 'mongo_db/driver/more'
1
+ require 'mongo_db/gems'
2
+
3
+ require 'mongo'
4
+
5
+ class Mongo::Error < StandardError; end
6
+ class Mongo::NotFound < Mongo::Error; end
7
+
8
+ %w(
9
+ database
10
+ collection
11
+ dynamic_finders
12
+ ).each{|f| require "mongo_db/driver/#{f}"}
13
+
14
+ # defaults
15
+ Mongo.class_eval do
16
+ class << self
17
+ def defaults; @defaults ||= {} end
18
+ attr_writer :defaults
19
+ end
20
+ end
21
+
22
+ # database
23
+ Mongo::DB.send :include, Mongo::DBExt
24
+
25
+ # collection
26
+ Mongo::Collection.class_eval do
27
+ include Mongo::CollectionExt, Mongo::DynamicFinders
28
+
29
+ %w(insert update remove save count).each do |method|
30
+ alias_method "#{method}_without_ext", method
31
+ alias_method method, "#{method}_with_ext"
32
+ end
33
+ end
@@ -0,0 +1,19 @@
1
+ class Mongo::Migration::Definition
2
+ def upgrade &block
3
+ if block
4
+ @upgrade = block
5
+ else
6
+ @upgrade
7
+ end
8
+ end
9
+ alias_method :up, :upgrade
10
+
11
+ def downgrade &block
12
+ if block
13
+ @downgrade = block
14
+ else
15
+ @downgrade
16
+ end
17
+ end
18
+ alias_method :down, :downgrade
19
+ end
@@ -0,0 +1,68 @@
1
+ class Mongo::Migration
2
+ def initialize db
3
+ @db, @definitions = db, {}
4
+ end
5
+
6
+ def add version, &block
7
+ raise "version should be an Integer! (but you provided '#{version}' instad)!" unless version.is_a? Integer
8
+ definition = Definition.new
9
+ block.call definition
10
+ definitions[version] = definition
11
+ end
12
+
13
+ def update version = nil
14
+ version ||= definitions.keys.max
15
+
16
+ if current_version == version
17
+ info "database '#{db.name}' already is of #{version} version, no migration needed"
18
+ return false
19
+ else
20
+ info "updating '#{db.name}' to #{version}"
21
+ end
22
+
23
+ increase_db_version while current_version < version
24
+ decrease_db_version while current_version > version
25
+ true
26
+ end
27
+
28
+ def current_version
29
+ if doc = db.db_metadata.first(name: 'migration')
30
+ doc[:version] || doc['version']
31
+ else
32
+ 0
33
+ end
34
+ end
35
+
36
+ protected
37
+ attr_accessor :db, :definitions
38
+
39
+ def info msg
40
+ db.connection.logger and db.connection.logger.info(msg)
41
+ end
42
+
43
+ def update_version new_version
44
+ db.db_metadata.update({name: 'migration'}, {name: 'migration', version: new_version}, {upsert: true, safe: true})
45
+ end
46
+
47
+ def increase_db_version
48
+ new_version = current_version + 1
49
+ migration = definitions[new_version]
50
+ raise "no upgrade of #{db.name} database to #{new_version} version!" unless migration and migration.up
51
+
52
+ migration.up.call db
53
+ update_version new_version
54
+
55
+ info "database '#{db.name}' upgraded to #{new_version} version."
56
+ end
57
+
58
+ def decrease_db_version
59
+ new_version = current_version - 1
60
+ migration = definitions[new_version + 1]
61
+ raise "no downgrade of #{db.name} database to #{new_version} version!" unless migration and migration.down
62
+
63
+ migration.down.call db
64
+ update_version new_version
65
+
66
+ info "database '#{db.name}' downgraded to #{new_version} version."
67
+ end
68
+ end
@@ -0,0 +1,19 @@
1
+ # namespace :db do
2
+ # desc "Migrate Database"
3
+ # task migrate: :migration_evnironment do
4
+ # require 'mongo_migration'
5
+ #
6
+ # database_name = (ENV['d'] || ENV['database'] || :default).to_sym
7
+ # version = ENV['v'] || ENV['version']
8
+ #
9
+ # if version.blank?
10
+ # size = Mongo.migration.definitions[database_name].size
11
+ # highest_defined_version = size == 0 ? 0 : size - 1
12
+ # version = highest_defined_version
13
+ # else
14
+ # version = version.to_i
15
+ # end
16
+ #
17
+ # Mongo.migration.update version, database_name
18
+ # end
19
+ # end
@@ -0,0 +1,8 @@
1
+ require 'mongo_db/driver'
2
+
3
+ class Mongo::Migration; end
4
+
5
+ %w(
6
+ definition
7
+ migration
8
+ ).each{|f| require "mongo_db/migration/#{f}"}
@@ -0,0 +1,54 @@
1
+ module Mongo::Model::Assignment
2
+ class Dsl < BasicObject
3
+ def initialize
4
+ @attributes = {}
5
+ end
6
+
7
+ def self.const_missing name
8
+ # BasicObject doesn't have access to any constants like String, Symbol, ...
9
+ ::Object.const_get name
10
+ end
11
+
12
+ def to_h; attributes end
13
+
14
+ protected
15
+ attr_reader :attributes
16
+
17
+ def method_missing attribute_name, type, mass_assignment = false
18
+ attribute_name.must_be.a Symbol
19
+ type.must.respond_to :cast
20
+ attributes[attribute_name] = [type, mass_assignment]
21
+ end
22
+ end
23
+
24
+ def set attributes, options = {}
25
+ if rules = self.class._assignment
26
+ force = options[:force]
27
+ attributes.each do |n, v|
28
+ n = n.to_sym
29
+ type, mass_assignment = rules[n]
30
+ if type and (mass_assignment or force)
31
+ v = type.cast(v)
32
+ send "#{n}=", v
33
+ end
34
+ end
35
+ else
36
+ attributes.each{|n, v| send "#{n}=", v}
37
+ end
38
+ self
39
+ end
40
+
41
+ def set! attributes, options = {}
42
+ set attributes, options.merge(force: true)
43
+ end
44
+
45
+ module ClassMethods
46
+ inheritable_accessor :_assignment, nil
47
+
48
+ def assignment &block
49
+ dsl = ::Mongo::Model::Assignment::Dsl.new
50
+ dsl.instance_eval &block
51
+ self._assignment = (_assignment || {}).merge dsl.to_h
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,36 @@
1
+ module Mongo::Model::Callbacks
2
+ inherit RubyExt::Callbacks
3
+
4
+ def _run_callbacks type, method_name
5
+ if type == :before
6
+ run_before_callbacks method_name, method: method_name
7
+ elsif type == :after
8
+ run_after_callbacks method_name, method: method_name
9
+ else
10
+ raise "invalid callback type (#{type})!"
11
+ end
12
+ end
13
+
14
+ module ClassMethods
15
+ [:validate, :update, :save, :destroy].each do |method_name|
16
+ define_method "before_#{method_name}" do |*args, &block|
17
+ opt = args.extract_options!
18
+ if block
19
+ set_callback method_name, :before, opt, &block
20
+ else
21
+ opt[:terminator] = false unless opt.include? :terminator
22
+ args.each{|executor| set_callback method_name, :before, executor, opt}
23
+ end
24
+ end
25
+
26
+ define_method "after_#{method_name}" do |*args, &block|
27
+ opt = args.extract_options!
28
+ if block
29
+ set_callback method_name, :after, opt, &block
30
+ else
31
+ args.each{|executor| set_callback method_name, :after, executor, opt}
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,28 @@
1
+ module Mongo::Model::Crud
2
+ def save opts = {}
3
+ with_collection opts do |collection, opts|
4
+ collection.save self, opts
5
+ end
6
+ end
7
+
8
+ def save! *args
9
+ save(*args) || raise(Mongo::Error, "can't save #{self.inspect}!")
10
+ end
11
+
12
+ def destroy opts = {}
13
+ with_collection opts do |collection, opts|
14
+ collection.destroy self, opts
15
+ end
16
+ end
17
+
18
+ def destroy! *args
19
+ destroy(*args)|| raise(Mongo::Error, "can't destroy #{self.inspect}!")
20
+ end
21
+
22
+ protected
23
+ def with_collection opts, &block
24
+ opts = opts.clone
25
+ collection = opts.delete(:collection) || self.class.collection
26
+ block.call collection, opts
27
+ end
28
+ end
@@ -0,0 +1,53 @@
1
+ module Mongo::Model::Db
2
+ module ClassMethods
3
+ inheritable_accessor :_db, nil
4
+ def db= v
5
+ self._db = if v.is_a? ::Proc
6
+ v
7
+ elsif v.is_a? ::Symbol
8
+ -> {::Mongo::Model.connection.db v.to_s}
9
+ else
10
+ -> {v}
11
+ end
12
+ end
13
+
14
+ def db *args, &block
15
+ if block
16
+ self.db = block
17
+ elsif !args.empty?
18
+ args.size.must == 1
19
+ self.db = args.first
20
+ else
21
+ (_db && _db.call) || ::Mongo::Model.db
22
+ end
23
+ end
24
+
25
+ inheritable_accessor :_collection, nil
26
+ def collection= v
27
+ self._collection = if v.is_a? ::Proc
28
+ v
29
+ elsif v.is_a? ::Symbol
30
+ -> {db.collection v}
31
+ else
32
+ -> {v}
33
+ end
34
+ end
35
+
36
+ def collection *args, &block
37
+ if block
38
+ self.collection = block
39
+ elsif !args.empty?
40
+ args.size.must == 1
41
+ self.collection = args.first
42
+ else
43
+ (_collection && _collection.call) || db.collection(default_collection_name)
44
+ end
45
+ end
46
+
47
+ def default_collection_name
48
+ first_ancestor_class = ancestors.find{|a| a.is_a? Class} ||
49
+ raise("can't evaluate default collection name for #{self}!")
50
+ first_ancestor_class.alias.pluralize.underscore.to_sym
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,14 @@
1
+ module Mongo::Model::Misc
2
+ def update_timestamps
3
+ now = Time.now.utc
4
+ self.created_at ||= now
5
+ self.updated_at = now
6
+ end
7
+
8
+ module ClassMethods
9
+ def timestamps!
10
+ attr_accessor :created_at, :updated_at
11
+ before_save :update_timestamps
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ module Mongo::Model
2
+ attr_accessor :_id, :_class
3
+
4
+ def new_record?; !!_id end
5
+
6
+ class << self
7
+ attr_accessor :db, :connection
8
+ attr_required :db, :connection
9
+ end
10
+ end
@@ -0,0 +1,36 @@
1
+ module Mongo::Model::Query
2
+ module ClassMethods
3
+ include Mongo::DynamicFinders
4
+
5
+ def count selector = {}, opts = {}
6
+ collection.count selector, opts
7
+ end
8
+
9
+ def first selector = {}, opts = {}
10
+ collection.first selector, opts
11
+ end
12
+
13
+ def each selector = {}, opts = {}, &block
14
+ collection.each selector, opts, &block
15
+ end
16
+
17
+ def all selector = {}, opts = {}, &block
18
+ if block
19
+ each selector, opts, &block
20
+ else
21
+ list = []
22
+ each(selector, opts){|doc| list << doc}
23
+ list
24
+ end
25
+ end
26
+
27
+ def first! selector = {}, opts = {}
28
+ first(selector, opts) || raise(Mongo::NotFound, "document with selector #{selector} not found!")
29
+ end
30
+
31
+ def exists? selector = {}, opts = {}
32
+ count(selector, opts) > 0
33
+ end
34
+ alias :exist? :exists?
35
+ end
36
+ end
@@ -0,0 +1,99 @@
1
+ module Mongo::Model::Scope
2
+ class ScopeProxy < BasicObject
3
+ def initialize model, scope
4
+ @model, @scope = model, scope
5
+ end
6
+
7
+ def class
8
+ ::Mongo::Model::Scope::ScopeProxy
9
+ end
10
+
11
+ def reverse_merge! scope
12
+ @scope = scope.merge @scope
13
+ end
14
+
15
+ def inspect
16
+ "#<ScopeProxy:{#{scope.inspect}}>"
17
+ end
18
+ alias_method :to_s, :inspect
19
+
20
+ protected
21
+ attr_reader :model, :scope
22
+
23
+ def method_missing method, *args, &block
24
+ model.with_scope scope do
25
+ result = model.send method, *args, &block
26
+ result.reverse_merge! scope if result.class == ::Mongo::Model::Scope::ScopeProxy
27
+ result
28
+ end
29
+ end
30
+ end
31
+
32
+ module ClassMethods
33
+ def current_scope
34
+ scope, exclusive = Thread.current[:mongo_model_scope]
35
+ if exclusive
36
+ scope
37
+ elsif scope
38
+ default_scope.merge scope
39
+ else
40
+ default_scope
41
+ end
42
+ end
43
+
44
+ def with_exclusive_scope options = {}, &block
45
+ with_scope options, true, &block
46
+ end
47
+
48
+ def with_scope options = {}, exclusive = false, &block
49
+ previous_options, previous_exclusive = Thread.current[:mongo_model_scope]
50
+ raise "exclusive scope already applied!" if previous_exclusive
51
+
52
+ begin
53
+ options = previous_options.merge options if previous_options and !exclusive
54
+ Thread.current[:mongo_model_scope] = [options, exclusive]
55
+ return block.call
56
+ ensure
57
+ Thread.current[:mongo_model_scope] = [previous_options, false]
58
+ end
59
+ end
60
+
61
+ inheritable_accessor :_default_scope, -> {{}}
62
+ def default_scope *args, &block
63
+ if block
64
+ self._default_scope = block
65
+ elsif !args.empty?
66
+ args.size.must == 1
67
+ args.first.must_be.a Hash
68
+ scope = args.first
69
+ self._default_scope = -> {args.first}
70
+ else
71
+ _default_scope.call
72
+ end
73
+ end
74
+
75
+ def scope name, options = nil, &block
76
+ model = self
77
+ metaclass.define_method name do
78
+ scope = (block && block.call) || options
79
+ ScopeProxy.new model, scope
80
+ end
81
+ end
82
+
83
+
84
+ #
85
+ # finders
86
+ #
87
+ def count selector = {}, opts = {}
88
+ super current_scope.merge(selector), opts
89
+ end
90
+
91
+ def first selector = {}, opts = {}
92
+ super current_scope.merge(selector), opts
93
+ end
94
+
95
+ def each selector = {}, opts = {}, &block
96
+ super current_scope.merge(selector), opts, &block
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,12 @@
1
+ require 'mongo_db/driver/spec'
2
+
3
+ rspec do
4
+ class << self
5
+ def with_mongo_model
6
+ with_mongo
7
+
8
+ before{Mongo::Model.db = mongo.db}
9
+ after{Mongo::Model.db = nil}
10
+ end
11
+ end
12
+ end