mongodbmodel 0.0.1

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