stores_in_mongo 0.3.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 64ec51cf6d63a3e3a5e66c1acb4d4ed90b8c3b07
4
- data.tar.gz: c5cdbdde558ef53ccef4763b65f6f9845d0c53fe
3
+ metadata.gz: 05ff0ec8463f5f848e7e736bb3e50d71de2c4ae7
4
+ data.tar.gz: 327f6d173bbda76ebd7e4a614ab58279d9f1422f
5
5
  SHA512:
6
- metadata.gz: f950dea3950ca559fd9146f8ef32c5cafec65ea1f6cec22d6c1deafb5ee316f9a5b4e849e8732b431464d2f336f76399386f5696d9bb9d6ddc227dedd48c93a1
7
- data.tar.gz: 763d446a09154d1bf09bb1a886817698224d0f2716e14d2d57780ead34941a268d077d08e45ab350e104a1ad8c69db2156f8e20b56a50a7140ddd4dcb8e036b6
6
+ metadata.gz: 3af0be54c3fdee52d4820b781fb7a82c54fabd6c280138c939af99826390b1fcee5a0ce3d469b8bf867f489418be32a69fa6b72cd9b7a556397c261ef9877c9c
7
+ data.tar.gz: 3765c8da5b23fbf0c2c8219992aa6296fa87c5dd3e507584695afd980492baf6aabdf7b27b6e2becda7021a2c2460d380f8f2f8ac7319a2c082980cbd7ea786c
@@ -1,5 +1,8 @@
1
1
  require "stores_in_mongo/base.rb"
2
2
  require "stores_in_mongo/builder.rb"
3
3
  require "stores_in_mongo/document_methods.rb"
4
+ require "stores_in_mongo/interpreter.rb"
4
5
 
5
- ActiveRecord::Base.extend(StoresInMongo::Base)
6
+ class StoresInMongo::RuntimeError < RuntimeError; end
7
+
8
+ ActiveRecord::Base.include(StoresInMongo::Base)
@@ -1,7 +1,36 @@
1
1
  module StoresInMongo
2
2
  module Base
3
- def stores_in_mongo(field, data_type = Hash)
4
- ::StoresInMongo::Builder.new(self, field, data_type).build
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def stores_in_mongo(field_name = nil, data_type = nil,
7
+ as: nil,
8
+ class_name: nil,
9
+ foreign_key: nil,
10
+ &blk)
11
+ raise ArgumentError, "Provide either inline field_name or block syntax, you cannot provide both to stores_in_mongo" if field_name.present? && blk.present?
12
+ raise ArgumentError, "Cannot use :class_name or :foreign_key with polymorphic :as option" if as.present? && (class_name.present? || foreign_key.present?)
13
+ if as.present?
14
+ foreign_key = as.to_s.foreign_key
15
+ class_name = foreign_key.sub(/id$/, "type")
16
+ end
17
+ class_attribute :stores_in_mongo_options
18
+ self.stores_in_mongo_options = {
19
+ polymorphic: as.present?,
20
+ foreign_key: foreign_key,
21
+ class_name: class_name,
22
+ use_sessions: false
23
+ }
24
+ builder = ::StoresInMongo::Builder.new(self)
25
+ if field_name.present?
26
+ builder.build do
27
+ field(field_name, data_type)
28
+ end
29
+ else
30
+ builder.build(&blk)
31
+ end
32
+ end
5
33
  end
34
+
6
35
  end
7
36
  end
@@ -1,44 +1,64 @@
1
1
  module StoresInMongo
2
2
  class Builder
3
3
 
4
- def initialize(model, field, data_type)
4
+ def initialize(model)
5
5
  @model = model
6
- @field = field.to_s
7
- @data_type = data_type
6
+ @class_given = @model.stores_in_mongo_options[:class_name].present?
8
7
  end
9
8
 
10
- def build
11
- @model.class_attribute :mongo_data_field
12
- @model.mongo_data_field = @field
13
-
14
- document_methods = build_document_methods_module
15
- @model.instance_exec(document_methods, @data_type) do |document_methods, data_type|
16
- const_set("MongoDocumentMethods", document_methods)
17
- include self::MongoDocumentMethods
9
+ def build(&blk)
10
+ if !@class_given
11
+ klass = build_mongo_class
12
+ @model.stores_in_mongo_options[:class_name] = klass.name
13
+ end
14
+ @model.stores_in_mongo_options[:foreign_key] ||= @model.stores_in_mongo_options[:class_name].foreign_key
15
+ @model.include(get_document_methods_module)
16
+ Interpreter.new(self).instance_exec(&blk)
17
+ end
18
18
 
19
- before_save :save_document
20
- before_destroy :destroy_document
19
+ def build_mongo_class
20
+ klass = @model.const_set("MongoDocument", Class.new)
21
+ klass.include Mongoid::Document
22
+ klass.include Mongoid::Timestamps
23
+ return klass
24
+ end
21
25
 
22
- mongo_klass = const_set("MongoDocument", Class.new)
23
- mongo_klass.include Mongoid::Document
24
- mongo_klass.include Mongoid::Timestamps
25
- mongo_klass.field self.mongo_data_field.to_sym, :type => data_type, default: data_type.try(:new)
26
+ def get_document_methods_module
27
+ @model.const_set("MongoDocumentMethods", Module.new)
28
+ @model::MongoDocumentMethods.include(StoresInMongo::DocumentMethods)
29
+ @model::MongoDocumentMethods.extend ActiveSupport::Concern
30
+ @model::MongoDocumentMethods.included do
31
+ before_save :save_mongo_document
32
+ after_save :clear_mongo_owner_dirty
33
+ before_destroy :destroy_mongo_document
26
34
  end
35
+ return @model::MongoDocumentMethods
27
36
  end
28
37
 
29
- def build_document_methods_module
30
- mod = Module.new
31
- mod.include(StoresInMongo::DocumentMethods)
32
- mod.instance_exec(@model) do |model|
33
- define_method(model.mongo_data_field) do |reload = false|
34
- document(reload)[model.mongo_data_field]
38
+ def add_field(field_name, data_type)
39
+ @model.stores_in_mongo_options[:class_name].constantize.field field_name, :type => data_type, default: data_type.try(:new) if !@class_given
40
+
41
+ @model::MongoDocumentMethods.instance_exec(field_name) do |field_name|
42
+ # getter
43
+ define_method(field_name) do
44
+ mongo_document.public_send(field_name)
35
45
  end
36
46
 
37
- define_method(model.mongo_data_field + "=") do |data|
38
- document[model.mongo_data_field] = data
47
+ # setter
48
+ define_method("#{field_name}=") do |data|
49
+ mongo_document.public_send("#{field_name}=", data)
50
+ end
51
+
52
+ end
53
+ end
54
+
55
+ def define_session(&blk)
56
+ @model.stores_in_mongo_options[:use_sessions] = true
57
+ @model::MongoDocumentMethods.instance_exec(blk) do |blk|
58
+ define_method("mongo_session") do
59
+ instance_exec(&blk)
39
60
  end
40
61
  end
41
- return mod
42
62
  end
43
63
 
44
64
  end
@@ -1,45 +1,108 @@
1
1
  module StoresInMongo
2
2
  module DocumentMethods
3
+ def mongo_class_name
4
+ if self.stores_in_mongo_options[:polymorphic]
5
+ self.public_send(mongo_class_column) or raise RuntimeError, "No mongo type specified in #{mongo_class_column} for #{self}! Initialize your model with a value or use a column default."
6
+ else
7
+ self.stores_in_mongo_options[:class_name]
8
+ end
9
+ end
10
+
11
+ def mongo_class
12
+ if self.stores_in_mongo_options[:use_sessions]
13
+ mongo_class_name.constantize.with(session: mongo_session)
14
+ else
15
+ mongo_class_name.constantize
16
+ end
17
+ end
18
+
19
+ def mongo_key
20
+ self.public_send(mongo_key_column)
21
+ end
22
+
23
+ def mongo_class_column
24
+ self.stores_in_mongo_options[:polymorphic] && self.stores_in_mongo_options[:class_name]
25
+ end
26
+
27
+ def mongo_key_column
28
+ stores_in_mongo_options[:foreign_key]
29
+ end
30
+
3
31
  def reload(*args)
32
+ clear_mongo_cache
4
33
  super
5
- find_or_initialize_document if document_loaded?
6
- return self
7
34
  end
8
35
 
9
- def dup
36
+ def deep_dup(*args)
10
37
  copy = super
11
- copy.document = document.dup if document_loaded?
38
+ copy.mongo_document = mongo_document.deep_dup
12
39
  return copy
13
40
  end
14
41
 
42
+ def changed?
43
+ @mongo_dirty || super
44
+ end
45
+
46
+ protected
47
+
48
+ def mongo_document=(mongo_document)
49
+ # We can't easily determine in-place data modification, so always mark as dirty if data has been loaded
50
+ # TODO: only mark as dirty if data has actually changed. Clear dirty on a fresh load.
51
+ mark_mongo_owner_as_dirty
52
+ @mongo_document = mongo_document
53
+ set_mongo_document_id
54
+ return mongo_document
55
+ end
56
+
15
57
  private
16
58
 
17
- def document_loaded?
18
- @document.present?
59
+ def mark_mongo_owner_as_dirty
60
+ @mongo_dirty = true
61
+ return true
19
62
  end
20
63
 
21
- def document(reload = false)
22
- return @document if !reload && document_loaded?
23
- @document = find_or_initialize_document
64
+ def clear_mongo_owner_dirty
65
+ @mongo_dirty = false
66
+ return true
24
67
  end
25
68
 
26
- def save_document
27
- return true if document.nil?
28
- document.save
29
- self.document_id = document.id
69
+ def mongo_document_loaded?
70
+ @mongo_document.present?
30
71
  end
31
72
 
32
- def destroy_document
33
- return true if document.nil?
34
- document.destroy
35
- end
73
+ def clear_mongo_cache
74
+ @mongo_document = nil
75
+ end
76
+
77
+ def set_mongo_document_id
78
+ assign_attributes(stores_in_mongo_options[:class_name] => mongo_document.class.name) if self.stores_in_mongo_options[:polymorphic]
79
+ assign_attributes(stores_in_mongo_options[:foreign_key] => mongo_document.id)
80
+ return mongo_key
81
+ end
36
82
 
37
- def find_or_initialize_document
38
- @document = fetch_document || self.class::MongoDocument.new
83
+ def mongo_document(reload = false)
84
+ return @mongo_document if !reload && mongo_document_loaded?
85
+ self.mongo_document = fetch_mongo_document || initialize_mongo_document
39
86
  end
40
87
 
41
- def fetch_document
42
- self.class::MongoDocument.where(id: self.document_id).first
88
+ def fetch_mongo_document
89
+ mongo_class.where(id: mongo_key).first
43
90
  end
91
+
92
+ def initialize_mongo_document
93
+ mongo_class.new
94
+ end
95
+
96
+ def save_mongo_document
97
+ return true if !mongo_document_loaded?
98
+ mongo_document.save!
99
+ set_mongo_document_id
100
+ return true
101
+ end
102
+
103
+ def destroy_mongo_document
104
+ return true if !mongo_document_loaded?
105
+ mongo_document.destroy
106
+ end
44
107
  end
45
108
  end
@@ -0,0 +1,15 @@
1
+ module StoresInMongo
2
+ class Interpreter < BasicObject
3
+ def initialize(builder)
4
+ @builder = builder
5
+ end
6
+
7
+ def field(name, data_type = nil)
8
+ @builder.add_field(name, data_type)
9
+ end
10
+
11
+ def session(&blk)
12
+ @builder.define_session(&blk)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ module StoresInMongo
2
+ class MongoStore
3
+ attr_reader :name, :klass, :foreign_key
4
+
5
+ def initialize(klass, foreign_key)
6
+ @klass = klass
7
+ @foreign_key = foreign_key
8
+ end
9
+
10
+ def find_or_initialize_for(owner)
11
+ owner[@foreign_key].present? && @klass.where(id: owner[@foreign_key]).first || @klass.new
12
+ end
13
+ end
14
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stores_in_mongo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Schwartz
@@ -30,8 +30,10 @@ dependencies:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
32
  version: 4.0.0
33
- description: Use stores_in_mongo <field> in an ActiveRecord object, use it as if it
34
- were local. Includes ActiveRecord-like caching behavior.
33
+ description: Define new or provide existing Mongoid::Document classes that access
34
+ a mongo db, use the mongo connection for persistence of specified virtual fields
35
+ in an ActiveRecord model. Document creation, persistence, and reloading is handled
36
+ automatically and highly customizeable.
35
37
  email: ozydingo@gmail.com
36
38
  executables: []
37
39
  extensions: []
@@ -41,6 +43,8 @@ files:
41
43
  - lib/stores_in_mongo/base.rb
42
44
  - lib/stores_in_mongo/builder.rb
43
45
  - lib/stores_in_mongo/document_methods.rb
46
+ - lib/stores_in_mongo/interpreter.rb
47
+ - lib/stores_in_mongo/mongo_store.rb
44
48
  homepage: https://github.com/ozydingo/stores_in_mongo
45
49
  licenses:
46
50
  - MIT
@@ -64,5 +68,5 @@ rubyforge_project:
64
68
  rubygems_version: 2.4.5
65
69
  signing_key:
66
70
  specification_version: 4
67
- summary: Seamlessly access and store mongo document fields from an ActiveRecord object
71
+ summary: Seamlessly interact with mongo storage as fields of an ActiveRecord model
68
72
  test_files: []