mince_data_model 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.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .idea
3
+ .bundle
4
+ Gemfile.lock
5
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.3@mince_data_model --create
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in mince_data_model.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # What is this?
2
+
3
+ Provides a very lightweight interface to store and retrieve data in MongoDB in Ruby.
4
+
5
+ # Why would you want this?
6
+
7
+ - To build a non-active-record implementation application using MongoDB.
8
+ - Allows interchanging data persistence with HashyDb
9
+
10
+ # Todos
11
+
12
+ - Remove rails dependency
13
+ - Add a better readme
14
+
15
+ # Contribute
16
+
17
+ - fork into a topic branch, write specs, make a pull request.
18
+
19
+ # Owners
20
+
21
+ Matt Simpson - [@railsgrammer](https://twitter.com/railsgrammer)
22
+ <br />
23
+ Jason Mayer - [@farkerhaiku](https://twitter.com/farkerhaiku)
24
+
25
+ # Contributors
26
+ - Your name here!
27
+
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,16 @@
1
+ require_relative 'guitar_data_model'
2
+
3
+ class Guitar
4
+ attr_reader :brand, :price, :type, :color, :id
5
+
6
+ def initialize(attrs)
7
+ @brand = attrs[:brand]
8
+ @price = attrs[:price]
9
+ @type = attrs[:type]
10
+ @color = attrs[:color]
11
+ end
12
+
13
+ def store
14
+ @id = GuitarDataModel.store(self)
15
+ end
16
+ end
@@ -0,0 +1,8 @@
1
+ require_relative '../lib/mince_data_model'
2
+
3
+ class GuitarDataModel
4
+ include MinceDataModel
5
+
6
+ data_collection :guitars
7
+ data_fields :brand, :price, :type, :color
8
+ end
@@ -0,0 +1,7 @@
1
+ require 'rails/application'
2
+
3
+ module DataStoreConfig
4
+ def self.data_store
5
+ Rails.application.config.data_store.to_s.camelize.constantize
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module MinceDataModel
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,166 @@
1
+ require "mince_data_model/version"
2
+ # SRP: Interface to read and write to and from the data store class for a specific collection
3
+
4
+ require 'active_support/hash_with_indifferent_access'
5
+ require 'active_support/core_ext/hash/slice'
6
+ require 'active_support/concern'
7
+
8
+ require_relative 'data_store_config'
9
+
10
+ module MinceDataModel
11
+ extend ActiveSupport::Concern
12
+
13
+ included do
14
+ attr_reader :id, :model
15
+ end
16
+
17
+ module ClassMethods
18
+ def data_store
19
+ DataStoreConfig.data_store
20
+ end
21
+
22
+ def data_fields(*fields)
23
+ create_data_fields(*fields) if fields.any?
24
+ @data_fields
25
+ end
26
+
27
+ def data_collection(collection_name = nil)
28
+ set_data_collection(collection_name) if collection_name
29
+ @data_collection
30
+ end
31
+
32
+ def store(model)
33
+ new(model).tap do |p|
34
+ p.add_to_data_store
35
+ end.id
36
+ end
37
+
38
+ def update(model)
39
+ new(model).tap do |p|
40
+ p.replace_in_data_store
41
+ end
42
+ end
43
+
44
+ def remove_from_array(id, field, value)
45
+ data_store.instance.remove_from_array(data_collection, data_store.primary_key_identifier, id, field, value)
46
+ end
47
+
48
+ def push_to_array(id, field, value)
49
+ data_store.instance.push_to_array(data_collection, data_store.primary_key_identifier, id, field, value)
50
+ end
51
+
52
+ def find(id)
53
+ translate_from_data_store data_store.instance.find(data_collection, data_store.primary_key_identifier, id)
54
+ end
55
+
56
+ def all
57
+ translate_each_from_data_store data_store.instance.find_all(data_collection)
58
+ end
59
+
60
+ def all_by_field(field, value)
61
+ translate_each_from_data_store data_store.instance.get_all_for_key_with_value(data_collection, field, value)
62
+ end
63
+
64
+ def all_by_fields(hash)
65
+ translate_each_from_data_store data_store.instance.get_by_params(data_collection, hash)
66
+ end
67
+
68
+ def find_by_fields(hash)
69
+ translate_from_data_store all_by_fields(hash).first
70
+ end
71
+
72
+ def containing_any(field, values)
73
+ translate_each_from_data_store data_store.instance.containing_any(data_collection, field, values)
74
+ end
75
+
76
+ def array_contains(field, value)
77
+ translate_each_from_data_store data_store.instance.array_contains(data_collection, field, value)
78
+ end
79
+
80
+ def translate_from_data_store(hash)
81
+ if hash
82
+ hash["id"] = hash[data_store.primary_key_identifier] if hash[data_store.primary_key_identifier]
83
+ HashWithIndifferentAccess.new hash
84
+ end
85
+ end
86
+
87
+ def translate_each_from_data_store(data)
88
+ data.collect {|d| translate_from_data_store(d) }
89
+ end
90
+
91
+ private
92
+
93
+ def set_data_collection(collection_name)
94
+ @data_collection = collection_name
95
+ end
96
+
97
+ def create_data_fields(*fields)
98
+ attr_accessor *fields
99
+ @data_fields = fields
100
+ end
101
+ end
102
+
103
+ def initialize(model)
104
+ @model = model
105
+ set_data_field_values
106
+ set_id
107
+ end
108
+
109
+ def data_store
110
+ self.class.data_store
111
+ end
112
+
113
+ def data_fields
114
+ self.class.data_fields
115
+ end
116
+
117
+ def data_collection
118
+ self.class.data_collection
119
+ end
120
+
121
+ def add_to_data_store
122
+ data_store.instance.add(data_collection, attributes)
123
+ end
124
+
125
+ def replace_in_data_store
126
+ data_store.instance.replace(data_collection, attributes)
127
+ end
128
+
129
+ private
130
+
131
+ def attributes
132
+ model_instance_values.merge(primary_key_identifier => id)
133
+ end
134
+
135
+ def model_instance_values
136
+ HashWithIndifferentAccess.new(model.instance_values).slice(*data_fields)
137
+ end
138
+
139
+ def set_id
140
+ @id = model_has_id? ? model.id : generated_id
141
+ end
142
+
143
+ def generated_id
144
+ data_store.generate_unique_id(model)
145
+ end
146
+
147
+ def model_has_id?
148
+ model.respond_to?(:id) && model.id
149
+ end
150
+
151
+ def set_data_field_values
152
+ data_fields.each { |field| set_data_field_value(field) }
153
+ end
154
+
155
+ def primary_key_identifier
156
+ data_store.primary_key_identifier
157
+ end
158
+
159
+ def set_data_field_value(field)
160
+ self.send("#{field}=", model.send(field)) if field_exists?(field)
161
+ end
162
+
163
+ def field_exists?(field)
164
+ model.respond_to?(field) && !model.send(field).nil?
165
+ end
166
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "mince_data_model/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "mince_data_model"
7
+ s.version = MinceDataModel::VERSION
8
+ s.authors = ["Jason Mayer", "Matt Simpson", "Asynchrony Solutions"]
9
+ s.email = ["matt@railsgrammer.com", "jason.mayer@gmail.com"]
10
+ s.homepage = "https://github.com/asynchrony/mince_data_model"
11
+ s.summary = %q{Interface for interchanging which type of data store to persist data to}
12
+ s.description = %q{Interface for interchanging which type of data store to persist data to}
13
+
14
+ s.rubyforge_project = "mince_data_model"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency "rspec"
22
+ s.add_development_dependency "rake"
23
+ s.add_dependency("rails", "~> 3.0")
24
+ end
@@ -0,0 +1,17 @@
1
+ require_relative '../examples/guitar_data_model'
2
+
3
+ require_relative 'support/shared_examples/mince_data_model_examples'
4
+
5
+ describe GuitarDataModel do
6
+ let(:collection_name) { :guitars }
7
+ let(:data_field_attributes) do
8
+ {
9
+ brand: 'a brand everyone knows',
10
+ price: 'a price you save up for',
11
+ type: 'the kind you want',
12
+ color: 'should be your favorite'
13
+ }
14
+ end
15
+
16
+ it_behaves_like 'a data model'
17
+ end
@@ -0,0 +1,32 @@
1
+ require_relative '../examples/guitar'
2
+
3
+ describe Guitar do
4
+ let(:attrs) { { brand: 'Gibson', price: 3399.99, type: 'Les Paul', color: 'Mahogany' } }
5
+
6
+ subject { described_class.new(attrs)}
7
+
8
+ its(:brand){ should == attrs[:brand] }
9
+ its(:price){ should == attrs[:price] }
10
+ its(:type){ should == attrs[:type] }
11
+ its(:color){ should == attrs[:color] }
12
+
13
+ describe "storing the guitar" do
14
+ let(:mock_id) { mock 'unique id for the guitar' }
15
+
16
+ before do
17
+ GuitarDataModel.stub(store: mock_id)
18
+ end
19
+
20
+ it 'uses the guitar document to store it' do
21
+ GuitarDataModel.should_receive(:store).with(subject).and_return(mock_id)
22
+
23
+ subject.store
24
+ end
25
+
26
+ it 'sets the id received from the document to the guitar so we can find it later' do
27
+ subject.store
28
+
29
+ subject.id.should == mock_id
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,155 @@
1
+ require 'digest'
2
+
3
+ shared_examples_for 'a data model' do
4
+ let(:mock_data_store) { mock 'data store data model' }
5
+ let(:mock_data_store_class) { mock 'data store data model', :instance => mock_data_store, :generate_unique_id => unique_id, :primary_key_identifier => primary_key }
6
+ let(:unique_id) { mock 'id' }
7
+ let(:primary_key) { "custom_id" }
8
+
9
+ before do
10
+ DataStoreConfig.stub(:data_store => mock_data_store_class)
11
+ end
12
+
13
+ describe "storing a data model" do
14
+ let(:mock_model) { mock 'a model', instance_values: data_field_attributes }
15
+
16
+ before do
17
+ mock_data_store.stub(:add)
18
+ end
19
+
20
+ it 'generates a unique id using the model as a salt' do
21
+ mock_data_store_class.should_receive(:generate_unique_id).with(mock_model).and_return(unique_id)
22
+
23
+ described_class.store(mock_model)
24
+ end
25
+
26
+ it 'adds the data model to the db store' do
27
+ mock_data_store.should_receive(:add).with(collection_name, HashWithIndifferentAccess.new({primary_key => unique_id}).merge(data_field_attributes))
28
+
29
+ described_class.store(mock_model)
30
+ end
31
+ end
32
+
33
+ describe "updating a data model" do
34
+ let(:data_model_id) { '1234567' }
35
+ let(:mock_model) { mock 'a model', id: data_model_id, instance_values: data_field_attributes }
36
+
37
+ before do
38
+ mock_data_store.stub(:replace)
39
+ end
40
+
41
+ it 'replaces the data model in the db store' do
42
+ mock_data_store.should_receive(:replace).with(collection_name, HashWithIndifferentAccess.new({primary_key => data_model_id}).merge(data_field_attributes))
43
+
44
+ described_class.update(mock_model)
45
+ end
46
+ end
47
+
48
+ describe "pushing a value to an array for a data model" do
49
+ let(:data_model_id) { '1234567' }
50
+
51
+ it 'replaces the data model in the db store' do
52
+ mock_data_store.should_receive(:push_to_array).with(collection_name, primary_key, data_model_id, :array_field, 'some value')
53
+
54
+ described_class.push_to_array(data_model_id, :array_field, 'some value')
55
+ end
56
+ end
57
+
58
+ describe "getting all data models with a specific value for a field" do
59
+ let(:mock_data_model) { {primary_key => 'some id'} }
60
+ let(:expected_data_models) { [HashWithIndifferentAccess.new({:id => 'some id', primary_key => 'some id'})] }
61
+ let(:mock_data_models) { [mock_data_model] }
62
+ subject { described_class.array_contains(:some_field, 'some value') }
63
+
64
+ it 'returns the stored data models with the requested field / value' do
65
+ mock_data_store.should_receive(:array_contains).with(collection_name, :some_field, 'some value').and_return(mock_data_models)
66
+
67
+ subject.should == expected_data_models
68
+ end
69
+ end
70
+
71
+ describe "removing a value from an array for a data model" do
72
+ let(:data_model_id) { '1234567' }
73
+
74
+ it 'removes the value from the array' do
75
+ mock_data_store.should_receive(:remove_from_array).with(collection_name, primary_key, data_model_id, :array_field, 'some value')
76
+
77
+ described_class.remove_from_array(data_model_id, :array_field, 'some value')
78
+ end
79
+ end
80
+
81
+ describe 'getting all of the data models' do
82
+ let(:mock_data_model) { {primary_key => 'some id'} }
83
+ let(:expected_data_models) { [HashWithIndifferentAccess.new({:id => 'some id', primary_key => 'some id'})] }
84
+ let(:mock_data_models) { [mock_data_model] }
85
+
86
+ it 'returns the stored data models' do
87
+ mock_data_store.should_receive(:find_all).with(collection_name).and_return(mock_data_models)
88
+
89
+ described_class.all.should == expected_data_models
90
+ end
91
+ end
92
+
93
+ describe "getting all the data fields by a parameter hash" do
94
+ let(:mock_data_model) { {primary_key => 'some id'} }
95
+ let(:expected_data_models) { [{:id => 'some id', primary_key => 'some id'}] }
96
+ let(:mock_data_models) { [mock_data_model] }
97
+ let(:expected_data_models) { [HashWithIndifferentAccess.new(mock_data_model)] }
98
+ let(:sample_hash) { {field1: nil, field2: 'not nil'} }
99
+
100
+ it 'passes the hash to the mock_data_store_class' do
101
+ mock_data_store.should_receive(:get_by_params).with(collection_name, sample_hash).and_return(mock_data_models)
102
+
103
+ described_class.all_by_fields(sample_hash).should == expected_data_models
104
+ end
105
+ end
106
+
107
+ describe "getting a record by a set of key values" do
108
+ let(:mock_data_model) { {primary_key => 'some id'} }
109
+ let(:mock_data_models) { [mock_data_model] }
110
+ let(:expected_data_models) { [{:id => 'some id', primary_key => 'some id'}] }
111
+
112
+ let(:sample_hash) { {field1: nil, field2: 'not nil'} }
113
+
114
+ it 'passes the hash to the mock_data_store_class' do
115
+ mock_data_store.should_receive(:get_by_params).with(collection_name, sample_hash).and_return(mock_data_models)
116
+
117
+ described_class.find_by_fields(sample_hash).should == HashWithIndifferentAccess.new(expected_data_models.first)
118
+ end
119
+ end
120
+
121
+ describe "getting all of the data models for a where a field contains any value of a given array of values" do
122
+ let(:mock_data_models) { [{primary_key => 'some id'}, {primary_key => 'some id 2'}] }
123
+ let(:expected_data_models) { [{"id" => 'some id', primary_key => 'some id'}, {"id" => 'some id 2', primary_key => 'some id 2'}] }
124
+
125
+ subject { described_class.containing_any(:some_field, ['value 1', 'value 2']) }
126
+
127
+ it 'returns the stored data models' do
128
+ mock_data_store.should_receive(:containing_any).with(collection_name, :some_field, ['value 1', 'value 2']).and_return(mock_data_models)
129
+
130
+ subject.should == expected_data_models
131
+ end
132
+ end
133
+
134
+ describe "getting all data models with a specific value for a field" do
135
+ let(:mock_data_models) { [{primary_key => 'some id'}, {primary_key => 'some id 2'}] }
136
+ let(:expected_data_models) { [HashWithIndifferentAccess.new({:id => 'some id', primary_key => 'some id'}), HashWithIndifferentAccess.new({id: 'some id 2', primary_key => 'some id 2'})] }
137
+ subject { described_class.all_by_field(:some_field, 'some value') }
138
+
139
+ it 'returns the stored data models with the requested field / value' do
140
+ mock_data_store.should_receive(:get_all_for_key_with_value).with(collection_name, :some_field, 'some value').and_return(mock_data_models)
141
+
142
+ subject.should == expected_data_models
143
+ end
144
+ end
145
+
146
+ describe 'getting a specific data model' do
147
+ let(:mock_data_model) { {primary_key => 'id', :id => 'id' } }
148
+
149
+ it 'returns the data model from the data store' do
150
+ mock_data_store.should_receive(:find).with(collection_name, primary_key, 'id').and_return(mock_data_model)
151
+
152
+ described_class.find(mock_data_model[:id]).should == HashWithIndifferentAccess.new(mock_data_model)
153
+ end
154
+ end
155
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mince_data_model
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jason Mayer
9
+ - Matt Simpson
10
+ - Asynchrony Solutions
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2012-03-02 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rspec
18
+ requirement: &16308420 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ! '>='
22
+ - !ruby/object:Gem::Version
23
+ version: '0'
24
+ type: :development
25
+ prerelease: false
26
+ version_requirements: *16308420
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: &16307660 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ! '>='
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: *16307660
38
+ - !ruby/object:Gem::Dependency
39
+ name: rails
40
+ requirement: &16305880 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '3.0'
46
+ type: :runtime
47
+ prerelease: false
48
+ version_requirements: *16305880
49
+ description: Interface for interchanging which type of data store to persist data
50
+ to
51
+ email:
52
+ - matt@railsgrammer.com
53
+ - jason.mayer@gmail.com
54
+ executables: []
55
+ extensions: []
56
+ extra_rdoc_files: []
57
+ files:
58
+ - .gitignore
59
+ - .rspec
60
+ - .rvmrc
61
+ - Gemfile
62
+ - README.md
63
+ - Rakefile
64
+ - examples/guitar.rb
65
+ - examples/guitar_data_model.rb
66
+ - lib/data_store_config.rb
67
+ - lib/mince_data_model.rb
68
+ - lib/mince_data_model/version.rb
69
+ - mince_data_model.gemspec
70
+ - spec/guitar_data_model_spec.rb
71
+ - spec/guitar_spec.rb
72
+ - spec/support/shared_examples/mince_data_model_examples.rb
73
+ homepage: https://github.com/asynchrony/mince_data_model
74
+ licenses: []
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ! '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ! '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project: mince_data_model
93
+ rubygems_version: 1.8.10
94
+ signing_key:
95
+ specification_version: 3
96
+ summary: Interface for interchanging which type of data store to persist data to
97
+ test_files: []