mongo_db 0.1.9 → 0.1.10

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