mongodbmodel 0.0.1

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.
@@ -0,0 +1,11 @@
1
+ require 'rake_ext'
2
+
3
+ project(
4
+ name: "mongodbmodel",
5
+ # version: '0.1.0',
6
+ gem: true,
7
+ summary: "Object Model for MongoDB",
8
+
9
+ author: "Alexey Petrushin",
10
+ homepage: "http://github.com/alexeypetrushin/mongodb_model"
11
+ )
@@ -0,0 +1,50 @@
1
+ require 'mongodb_model/gems'
2
+
3
+ require 'validatable'
4
+ require 'file_model'
5
+ require 'i18n'
6
+ require 'ruby_ext'
7
+ require 'mongo/object'
8
+
9
+ module Mongo::Model; end
10
+
11
+ %w(
12
+ support/types
13
+
14
+ db
15
+ assignment
16
+ callbacks
17
+ validation
18
+ validation/uniqueness_validator
19
+ crud
20
+ query
21
+ query_mixin
22
+ scope
23
+ attribute_convertors
24
+ file_model
25
+ misc
26
+ model
27
+ ).each{|f| require "mongo/model/#{f}"}
28
+
29
+ module Mongo
30
+ module Model
31
+ inherit \
32
+ Db,
33
+ Assignment,
34
+ Callbacks,
35
+ Validation,
36
+ Crud,
37
+ QueryMixin,
38
+ Scope,
39
+ AttributeConvertors,
40
+ Mongo::Model::FileModel,
41
+ Misc
42
+ end
43
+ end
44
+
45
+ Mongo.defaults.merge! \
46
+ symbolize: true,
47
+ convert_underscore_to_dollar: true,
48
+ batch_size: 50,
49
+ multi: true,
50
+ safe: true
@@ -0,0 +1,65 @@
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, *args
18
+ attribute_name.must_be.a Symbol
19
+
20
+ args.size.must_be.in 1..2
21
+ if args.first.is_a? Class
22
+ type, mass_assignment = args
23
+ mass_assignment ||= false
24
+ type.must.respond_to :cast
25
+ else
26
+ type, mass_assignment = nil, args.first
27
+ end
28
+
29
+ attributes[attribute_name] = [type, mass_assignment]
30
+ end
31
+ end
32
+
33
+ def set attributes, options = {}
34
+ if rules = self.class._assign
35
+ force = options[:force]
36
+ attributes.each do |n, v|
37
+ n = n.to_sym
38
+ if rule = rules[n]
39
+ type, mass_assignment = rule
40
+ if mass_assignment or force
41
+ v = type.cast(v) if type
42
+ send "#{n}=", v
43
+ end
44
+ end
45
+ end
46
+ else
47
+ attributes.each{|n, v| send "#{n}=", v}
48
+ end
49
+ self
50
+ end
51
+
52
+ def set! attributes, options = {}
53
+ set attributes, options.merge(force: true)
54
+ end
55
+
56
+ module ClassMethods
57
+ inheritable_accessor :_assign, nil
58
+
59
+ def assign &block
60
+ dsl = ::Mongo::Model::Assignment::Dsl.new
61
+ dsl.instance_eval &block
62
+ self._assign = (_assign || {}).merge dsl.to_h
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,54 @@
1
+ require 'json'
2
+ require 'yaml'
3
+
4
+ module Mongo::Model::AttributeConvertors
5
+ CONVERTORS = {
6
+ line: {
7
+ from_string: -> s {(s || "").split(',').collect{|s| s.strip}},
8
+ to_string: -> v {v.join(', ')}
9
+ },
10
+ column: {
11
+ from_string: -> s {(s || "").split("\n").collect{|s| s.strip}},
12
+ to_string: -> v {v.join("\n")}
13
+ },
14
+ yaml: {
15
+ from_string: -> s {YAML.load s rescue {}},
16
+ to_string: -> v {v.to_yaml.strip}
17
+ },
18
+ json: {
19
+ from_string: -> s {JSON.parse s rescue {}},
20
+ to_string: -> v {v.to_json.strip}
21
+ }
22
+ }
23
+
24
+ module ClassMethods
25
+ def available_as_string name, converter_name
26
+ converter = CONVERTORS[converter_name]
27
+ raise "unknown converter name :#{converter_name} for :#{name} field!" unless converter
28
+
29
+ from_string, to_string = converter[:from_string], converter[:to_string]
30
+ name_as_string = "#{name}_as_string".to_sym
31
+ define_method name_as_string do
32
+ _cache[name_as_string] ||= to_string.call(send(name))
33
+ end
34
+
35
+ define_method "#{name_as_string}=" do |value|
36
+ _cache.delete name_as_string
37
+ self.send "#{name}=", from_string.call(value)
38
+ end
39
+ end
40
+
41
+ def available_as_yaml name
42
+ raise "delimiter not specified for :#{name} field!" unless delimiter
43
+ method = "#{name}_as_string"
44
+ define_method method do
45
+ self.send(name).join(delimiter)
46
+ end
47
+ define_method "#{method}=" do |value|
48
+ value = (value || "").split(delimiter.strip).collect{|s| s.strip}
49
+ self.send "#{name}=", value
50
+ end
51
+ end
52
+ end
53
+
54
+ end
@@ -0,0 +1,26 @@
1
+ module Mongo::Model::Callbacks
2
+ inherit RubyExt::Callbacks
3
+
4
+ module ClassMethods
5
+ [:validate, :create, :update, :save, :destroy].each do |method_name|
6
+ define_method "before_#{method_name}" do |*args, &block|
7
+ opt = args.extract_options!
8
+ if block
9
+ set_callback method_name, :before, opt, &block
10
+ else
11
+ opt[:terminator] = false unless opt.include? :terminator
12
+ args.each{|executor| set_callback method_name, :before, executor, opt}
13
+ end
14
+ end
15
+
16
+ define_method "after_#{method_name}" do |*args, &block|
17
+ opt = args.extract_options!
18
+ if block
19
+ set_callback method_name, :after, opt, &block
20
+ else
21
+ args.each{|executor| set_callback method_name, :after, executor, opt}
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,57 @@
1
+ module Mongo::Model::Crud
2
+ def save options = {}
3
+ with_collection options do |collection, options|
4
+ collection.save self, options
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 options = {}
13
+ with_collection options do |collection, options|
14
+ collection.destroy self, options
15
+ end
16
+ end
17
+
18
+ def destroy! *args
19
+ destroy(*args) || raise(Mongo::Error, "can't destroy #{self.inspect}!")
20
+ end
21
+
22
+ module ClassMethods
23
+ def build attributes = {}, options = {}
24
+ self.new.set attributes, options
25
+ end
26
+
27
+ def create attributes = {}, options = {}
28
+ o = build attributes, options
29
+ o.save
30
+ o
31
+ end
32
+
33
+ def create! attributes = {}, options = {}
34
+ o = create attributes
35
+ raise(Mongo::Error, "can't create #{attributes.inspect}!") if o.new_record?
36
+ o
37
+ end
38
+
39
+ def destroy_all selector = {}, options = {}
40
+ success = true
41
+ collection = options[:collection] || self.collection
42
+ each(selector){|o| success = false unless o.destroy}
43
+ success
44
+ end
45
+
46
+ def destroy_all! selector = {}, options = {}
47
+ destroy_all(selector, options) || raise(Mongo::Error, "can't destroy #{selector.inspect}!")
48
+ end
49
+ end
50
+
51
+ protected
52
+ def with_collection options, &block
53
+ options = options.clone
54
+ collection = options.delete(:collection) || self.class.collection
55
+ block.call collection, options
56
+ end
57
+ 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,27 @@
1
+ module Mongo::Model::FileModel
2
+ inherit ::FileModel::Helper
3
+
4
+ def attribute_get name
5
+ instance_variable_get :"@#{name}"
6
+ end
7
+
8
+ def attribute_set name, value
9
+ instance_variable_set :"@#{name}", value
10
+ end
11
+
12
+ module ClassMethods
13
+ def mount_file attr_name, file_model_class
14
+ super
15
+
16
+ before_validate do |model|
17
+ file_model = model.send(attr_name)
18
+ file_model.run_validations
19
+ model.errors[attr_name] = file_model.errors unless file_model.errors.empty?
20
+ end
21
+
22
+ after_save{|model| model.send(attr_name).save}
23
+
24
+ after_destroy{|model| model.send(attr_name).destroy}
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,44 @@
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
+
9
+ def _cache
10
+ @_cache ||= {}
11
+ end
12
+ # def _clear_cache
13
+ # @_cache = {}
14
+ # end
15
+
16
+ def dom_id
17
+ # new_record? ? "new_#{self.class.name.underscore}" : to_param
18
+ to_param
19
+ end
20
+
21
+ def to_param
22
+ (_id || '').to_s
23
+ end
24
+
25
+ delegate :t, to: I18n
26
+
27
+ def reload
28
+ obj = self.class.by_id!(_id || raise("can't reload new document (#{self})!"))
29
+ instance_variables.each{|n| remove_instance_variable n}
30
+ obj.instance_variables.each do |n|
31
+ instance_variable_set n, obj.instance_variable_get(n)
32
+ end
33
+ nil
34
+ end
35
+
36
+ module ClassMethods
37
+ delegate :t, to: I18n
38
+
39
+ def timestamps!
40
+ attr_accessor :created_at, :updated_at
41
+ before_save :update_timestamps
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,13 @@
1
+ module Mongo::Model
2
+ include Mongo::Object
3
+
4
+ attr_accessor :_id, :_class
5
+
6
+ def _id?; !!_id end
7
+ def new_record?; !_id end
8
+
9
+ class << self
10
+ attr_accessor :db, :connection
11
+ attr_required :db, :connection
12
+ end
13
+ end
@@ -0,0 +1,46 @@
1
+ class Mongo::Model::Query < Object
2
+ attr_reader :model, :selector, :options
3
+
4
+ def initialize model, selector = {}, options = {} *args
5
+ @model, @selector, @options = model, selector, options
6
+ end
7
+
8
+ def class
9
+ ::Mongo::Model::Query
10
+ end
11
+
12
+ def merge query
13
+ raise "can't merge queries with different models!" unless model == query.model
14
+ self.class.new model, selector.merge(query.selector), options.merge(query.options)
15
+ end
16
+
17
+ def inspect
18
+ "#<Mongo::Model::Query: #{model} #{@selector.inspect} #{@options.inspect}>"
19
+ end
20
+ alias_method :to_s, :inspect
21
+
22
+ def == o
23
+ self.class == o.class and ([model, selector, options] == [o.model, o.selector, o.options])
24
+ end
25
+
26
+ def build attributes = {}, options = {}
27
+ model.build selector.merge(attributes), options
28
+ end
29
+
30
+ def create attributes = {}, options = {}
31
+ model.create selector.merge(attributes), options
32
+ end
33
+
34
+ def create! attributes = {}, options = {}
35
+ model.create! selector.merge(attributes), options
36
+ end
37
+
38
+ protected
39
+ def method_missing method, *args, &block
40
+ model.with_scope selector, options do
41
+ result = model.send method, *args, &block
42
+ result = self.merge result if result.is_a? self.class
43
+ result
44
+ end
45
+ end
46
+ end