stores_in_mongo 0.3.1 → 1.0.0

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.
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: []