get 0.1.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.
data/lib/get/db.rb ADDED
@@ -0,0 +1,29 @@
1
+ module Get
2
+ class Db
3
+ class << self
4
+ attr_accessor :entity, :query_key, :collection, :store
5
+
6
+ def entity_factory
7
+ @factory ||= EntityFactory.new(entity, name, collection, query_key)
8
+ end
9
+ end
10
+
11
+ def initialize(actions)
12
+ @actions = actions
13
+ @query = Get.adapter.new(self.class.store)
14
+ end
15
+
16
+ def call
17
+ execute_queries
18
+ self.class.entity_factory.build(@query)
19
+ end
20
+
21
+ private
22
+
23
+ def execute_queries
24
+ @actions.each do |action, options|
25
+ @query.send(action, options)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,39 @@
1
+ module Get
2
+ module Entities
3
+ class Collection
4
+ def initialize(collection)
5
+ @collection = collection
6
+ end
7
+
8
+ def each
9
+ @collection.each do |result|
10
+ yield singular_entity(result)
11
+ end
12
+ end
13
+
14
+ def [](index)
15
+ singular_entity(@collection[index])
16
+ end
17
+
18
+ private
19
+
20
+ def method_missing(method)
21
+ if [:length, :size, :empty?, :present?].include? method
22
+ @collection.send(method)
23
+ elsif [:first, :last].include? method
24
+ singular_entity(@collection.send(method))
25
+ end
26
+ end
27
+
28
+ def singular_entity(record)
29
+ singular_entity_class.new(Get.adapter.new(record).to_hash)
30
+ end
31
+
32
+ # Collection classes have the form Get::Entities::TypesMapper
33
+ # Single output requires the form Get::Entities::TypeMapper
34
+ def singular_entity_class
35
+ @singular_entity ||= Kernel.const_get('Get').const_get('Entities').const_get(self.class.name.demodulize.singularize)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,13 @@
1
+ require 'hashie'
2
+
3
+ module Get
4
+ module Entities
5
+ class Single < Hash
6
+ include Hashie::Extensions::MethodAccess
7
+
8
+ def initialize(attributes)
9
+ update(attributes)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ module Get
2
+ module Entities
3
+ CLASS_PREFIX = 'Get'
4
+
5
+ class << self
6
+ def const_missing(name)
7
+ return super(name) unless name.to_s.match(/^#{CLASS_PREFIX}/)
8
+
9
+ parent_klass = name.to_s.plural? ? Get::Entities::Collection : Get::Entities::Single
10
+ Get::Entities.const_set(name, Class.new(parent_klass))
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,34 @@
1
+ module Get
2
+ class EntityFactory
3
+ def initialize(entity, query_class_name, is_collection, result_key)
4
+ @entity, @query_class_name, @is_collection, @result_key = entity, query_class_name, is_collection, result_key
5
+ end
6
+
7
+ def build(adapter_result)
8
+ klass.new(db_result(adapter_result))
9
+ end
10
+
11
+ private
12
+
13
+ def db_result(adapter_result)
14
+ @is_collection ? adapter_result.context : adapter_result.to_hash
15
+ end
16
+
17
+ def klass
18
+ Get.entity_for(key) || Get::Entities.const_get(dynamic_class)
19
+ end
20
+
21
+ def key
22
+ @query_class_name.demodulize.symbolize
23
+ end
24
+
25
+ def dynamic_class
26
+ "#{::Get::Entities::CLASS_PREFIX}#{dynamic_key.camelize}"
27
+ end
28
+
29
+ def dynamic_key
30
+ return @result_key.to_s if @result_key # Special case for ancestor queries
31
+ @is_collection ? @entity.to_s.pluralize : @entity.to_s
32
+ end
33
+ end
34
+ end
data/lib/get/errors.rb ADDED
@@ -0,0 +1,12 @@
1
+ module Get
2
+ module Errors
3
+ class Base < StandardError
4
+ end
5
+
6
+ class MethodNotImplemented < StandardError
7
+ end
8
+
9
+ class InvalidAncestry < StandardError
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ module Get
2
+ module RunMethods
3
+ def run(*context)
4
+ new(*context).run
5
+ end
6
+
7
+ def run!(*context)
8
+ new(*context).run!
9
+ end
10
+ end
11
+ end
data/lib/get.rb ADDED
@@ -0,0 +1,47 @@
1
+ require 'get/adapters/abstract_adapter'
2
+ require 'get/adapters/active_record'
3
+ require 'get/builders/base_builder'
4
+ require 'get/builders/ancestry_builder'
5
+ require 'get/builders/query_builder'
6
+ require 'get/core_extensions/string'
7
+ require 'get/entities/single'
8
+ require 'get/entities/collection'
9
+ require 'get/builders'
10
+ require 'get/configuration'
11
+ require 'get/db'
12
+ require 'get/entities'
13
+ require 'get/entity_factory'
14
+ require 'get/errors'
15
+ require 'get/run_methods'
16
+
17
+ module Get
18
+ extend Get::Configuration
19
+
20
+ ASK_CLASS_REGEX = /^(.*)(By|From)(.*)/
21
+
22
+ class << self
23
+ attr_writer :configuration
24
+
25
+ def included(base)
26
+ base.class_eval do
27
+ extend ::Get::RunMethods
28
+ end
29
+ end
30
+
31
+ def const_missing(name)
32
+ return super(name) unless name.to_s.match(ASK_CLASS_REGEX)
33
+ Builders.generate_class(name)
34
+ end
35
+ end
36
+
37
+ def run
38
+ run!
39
+ rescue ::Get::Errors::Base
40
+ end
41
+
42
+ def run!
43
+ call
44
+ rescue *Get.adapter.expected_errors => e
45
+ raise ::Get::Errors::Base.new(e.message)
46
+ end
47
+ end
data/spec/get_spec.rb ADDED
@@ -0,0 +1,326 @@
1
+ require 'spec_helper'
2
+
3
+ if !defined?(ActiveRecord::Base)
4
+ puts "** require 'active_record' to run the specs in #{__FILE__}"
5
+ else
6
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
7
+
8
+ ActiveRecord::Migration.suppress_messages do
9
+ ActiveRecord::Schema.define(:version => 0) do
10
+ create_table(:employers, force: true) {|t| t.string :name }
11
+ create_table(:users, force: true) {|t| t.string :first_name; t.string :last_name; t.references :employer; }
12
+ create_table(:sports_cars, force: true) {|t| t.string :make; t.references :employer; }
13
+ end
14
+ end
15
+
16
+ module GetSpec
17
+ class Employer < ActiveRecord::Base
18
+ has_many :users
19
+ has_many :sports_cars
20
+ end
21
+
22
+ class User < ActiveRecord::Base
23
+ belongs_to :employer
24
+ end
25
+
26
+ class SportsCar < ActiveRecord::Base
27
+ belongs_to :employer
28
+ end
29
+ end
30
+ end
31
+
32
+ describe Get do
33
+ let(:last_name) { 'Turner' }
34
+ let(:adapter) { :active_record }
35
+
36
+ # Preserve system config for other tests
37
+ before(:all) { @system_config = Get.configuration }
38
+ after(:all) { Get.configuration = @system_config }
39
+
40
+ # Reset base config with each iteration
41
+ before { Get.configure { |config| config.adapter = adapter } }
42
+ after do
43
+ GetSpec::User.delete_all
44
+ GetSpec::Employer.delete_all
45
+ Get.reset
46
+ end
47
+
48
+ class MyCustomEntity < Get::Entities::Collection
49
+ def east_london_length
50
+ "#{length}, bruv"
51
+ end
52
+ end
53
+
54
+ describe '#configure' do
55
+ context '#register_entity' do
56
+ let(:user_count) { 3 }
57
+
58
+ before do
59
+ Get.configure { |config| config.register_entity(:users_by_last_name, MyCustomEntity) }
60
+ user_count.times { GetSpec::User.create(last_name: last_name) }
61
+ end
62
+ after { Get.reset }
63
+
64
+ it 'gets registers entity' do
65
+ expect(Get.configuration.entity_for(:users_by_last_name)).to eq MyCustomEntity
66
+ end
67
+
68
+ it 'returns specified entity type after querying db' do
69
+ result = Get::UsersByLastName.run(last_name)
70
+ expect(result.is_a? MyCustomEntity).to be true
71
+ expect(result.east_london_length).to eq "#{user_count}, bruv"
72
+ end
73
+ end
74
+ end
75
+
76
+ context '#entity_for' do
77
+ context 'when entity has been registered' do
78
+ before do
79
+ Get.configure do |config|
80
+ config.adapter = adapter
81
+ config.register_entity(:users_by_last_name, MyCustomEntity)
82
+ end
83
+ end
84
+ after { Get.reset }
85
+
86
+ it 'registers entity' do
87
+ expect(Get.entity_for(:users_by_last_name)).to eq MyCustomEntity
88
+ end
89
+ end
90
+
91
+ context 'when entity has not been registered' do
92
+ it 'returns nil' do
93
+ expect(Get.entity_for(:users_by_last_name)).to be nil
94
+ end
95
+ end
96
+ end
97
+
98
+ context '#adapter' do
99
+ context 'when the adapter is set' do
100
+ it 'returns the correct adapter class' do
101
+ expect(Get.adapter).to eq Get::Adapters::ActiveRecord
102
+ end
103
+ end
104
+
105
+ context 'when the adapter is not set' do
106
+ before { Get.configure { |config| config.adapter = nil } }
107
+ after { Get.reset }
108
+
109
+ it 'throws error' do
110
+ expect { Get.adapter }.to raise_error(Get::Errors::Base)
111
+ end
112
+ end
113
+ end
114
+
115
+ context '#reset' do
116
+ before do
117
+ Get.configure do |config|
118
+ config.adapter = 'my_adapter'
119
+ config.register_entity(:users_by_last_name, MyCustomEntity)
120
+ end
121
+ Get.reset
122
+ end
123
+ it 'resets the config' do
124
+ expect(Get.configuration.adapter).to be nil
125
+ expect(Get.entity_for(:users_by_last_name)).to be nil
126
+ end
127
+ end
128
+
129
+ context '#run!' do
130
+ context 'singular form' do
131
+ context 'when the record exists' do
132
+ let!(:user) { GetSpec::User.create(last_name: last_name) }
133
+
134
+ context 'field in class name' do
135
+ it 'gets the records based on By[KEY]' do
136
+ result = Get::UserById.run!(user.id)
137
+ expect(result.to_h).to eq user.attributes
138
+ end
139
+
140
+ it 'returns a dynamically generated response entity' do
141
+ expect(Get::UserById.run!(user.id).is_a?(Get::Entities::Single)).to be true
142
+ end
143
+ end
144
+
145
+ context 'field in parameters' do
146
+ it 'gets the records based on parameters' do
147
+ result = Get::UserBy.run!(last_name: last_name)
148
+ expect(result.to_h).to eq user.attributes
149
+ end
150
+
151
+ it 'returns a dynamically generated response entity' do
152
+ expect(Get::UserBy.run!(last_name: last_name).is_a?(Get::Entities::Single)).to be true
153
+ end
154
+ end
155
+ end
156
+
157
+ context 'when the record does not exist' do
158
+ it 'returns nil' do
159
+ expect { Get::UserById.run!(999) }.to raise_error Get::Errors::Base
160
+ end
161
+ end
162
+ end
163
+ end
164
+
165
+ context '#run' do
166
+ context 'singular form' do
167
+ context 'when the record exists' do
168
+ let!(:user) { GetSpec::User.create(last_name: last_name) }
169
+
170
+ context 'field in class name' do
171
+ it 'gets the records based on By[KEY]' do
172
+ result = Get::UserById.run(user.id)
173
+ expect(result.to_h).to eq user.attributes
174
+ end
175
+
176
+ it 'returns a dynamically generated response entity' do
177
+ expect(Get::UserById.run(user.id).is_a?(Get::Entities::Single)).to be true
178
+ end
179
+ end
180
+
181
+ context 'field in parameters' do
182
+ it 'gets the records based on parameters' do
183
+ result = Get::UserBy.run(last_name: last_name)
184
+ expect(result.to_h).to eq user.attributes
185
+ end
186
+
187
+ it 'returns a dynamically generated response entity' do
188
+ expect(Get::UserBy.run(last_name: last_name).is_a?(Get::Entities::Single)).to be true
189
+ end
190
+ end
191
+ end
192
+
193
+ context 'when the record does not exist' do
194
+ it 'returns nil' do
195
+ expect(Get::UserById.run(999)).to eq nil
196
+ end
197
+ end
198
+ end
199
+
200
+ context 'plural form' do
201
+ let(:last_name) { 'Turner' }
202
+ let(:match_count) { 3 }
203
+ let(:miss_count) { 2 }
204
+
205
+ context 'when records exist' do
206
+ before do
207
+ match_count.times { GetSpec::User.create(last_name: last_name) }
208
+ miss_count.times { GetSpec::User.create }
209
+ end
210
+
211
+ context 'field in class name' do
212
+ it 'gets the records based on By[KEY]' do
213
+ result = Get::UsersByLastName.run(last_name)
214
+ expect(result.length).to eq match_count
215
+ end
216
+
217
+ it 'returns a dynamically generated response entity' do
218
+ expect(Get::UsersByLastName.run(last_name).is_a?(Get::Entities::Collection)).to be true
219
+ end
220
+ end
221
+
222
+ context 'field in parameters' do
223
+ it 'gets the records based on parameters' do
224
+ result = Get::UsersBy.run(last_name: last_name)
225
+ expect(result.length).to eq match_count
226
+ end
227
+
228
+ it 'returns a dynamically generated response entity' do
229
+ expect(Get::UsersBy.run(last_name: last_name).is_a?(Get::Entities::Collection)).to be true
230
+ end
231
+ end
232
+ end
233
+
234
+ context 'when no records exist' do
235
+ it 'returns empty collection' do
236
+ expect(Get::UsersBy.run(last_name: last_name).empty?).to be true
237
+ end
238
+ end
239
+ end
240
+
241
+ context 'ancestry' do
242
+ context 'direct relation' do
243
+ let(:employer) { GetSpec::Employer.create }
244
+ let!(:user1) { GetSpec::User.create(employer: employer) }
245
+ let!(:user2) { GetSpec::User.create(employer: employer) }
246
+
247
+ context 'ParentFromChild' do
248
+ it 'returns parent' do
249
+ expect(Get::EmployerFromUser.run(user1).to_h).to eq employer.attributes
250
+ end
251
+ end
252
+
253
+ context 'ChildrenFromParent' do
254
+ it 'returns children' do
255
+ result = Get::UsersFromEmployer.run(employer)
256
+ expect(result.first.to_h).to eq user1.attributes
257
+ expect(result.last.to_h).to eq user2.attributes
258
+ end
259
+ end
260
+
261
+ context 'invalid ancestry' do
262
+ it 'throws error' do
263
+ expect { Get::UserFromEmployer.run(employer) }.to raise_error Get::Errors::InvalidAncestry
264
+ end
265
+ end
266
+ end
267
+
268
+ context 'using via' do
269
+ let(:employer) { GetSpec::Employer.create }
270
+ let(:user) { GetSpec::User.create(employer: employer) }
271
+ let(:sportscar) { GetSpec::SportsCar.create(employer: employer) }
272
+
273
+ before do
274
+ employer.sports_cars << sportscar
275
+ end
276
+
277
+ it 'returns the correct ancestor' do
278
+ result = Get::SportsCarsFromUser.run(user, via: [:employer])
279
+ expect(result.first.to_h).to eq sportscar.attributes
280
+ end
281
+ end
282
+ end
283
+ end
284
+ end
285
+
286
+ describe Get::Builders::AncestryBuilder do
287
+ let(:name) { 'UserFromEmployer' }
288
+
289
+ before { Get.configure { |config| config.adapter = :active_record } }
290
+ after { Get.reset }
291
+
292
+ subject { Get::Builders::AncestryBuilder.new(name) }
293
+
294
+ describe '#class' do
295
+ it 'builds a class that inherits from Get::Db' do
296
+ expect(subject.class.superclass).to eq Get::Db
297
+ end
298
+
299
+ it 'correctly assigns class-level variables' do
300
+ [:entity, :query_key, :collection, :store, :result_key].each do |class_var|
301
+ expect(subject.class.respond_to? class_var).to be true
302
+ end
303
+ end
304
+ end
305
+ end
306
+
307
+ describe Get::Builders::QueryBuilder do
308
+ let(:name) { 'UserFromEmployer' }
309
+
310
+ before { Get.configure { |config| config.adapter = :active_record } }
311
+ after { Get.reset }
312
+
313
+ subject { Get::Builders::QueryBuilder.new(name) }
314
+
315
+ describe '#class' do
316
+ it 'builds a class that inherits from Get::Db' do
317
+ expect(subject.class.superclass).to eq Get::Db
318
+ end
319
+
320
+ it 'correctly assigns class-level variables' do
321
+ [:entity, :query_key, :collection, :store, :field].each do |class_var|
322
+ expect(subject.class.respond_to? class_var).to be true
323
+ end
324
+ end
325
+ end
326
+ end
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'byebug'
3
+ require 'rspec'
4
+ require 'active_record'
5
+ require 'bundler/setup'
6
+ Bundler.setup
7
+
8
+ require 'get'
metadata ADDED
@@ -0,0 +1,165 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: get
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Blake Turner
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: hashie
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 3.4.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 3.4.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.0.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: activerecord
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 3.2.15
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 3.2.15
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 3.2.15
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 3.2.15
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 2.4.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 2.4.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: sqlite3
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: byebug
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Encapsulate your database queries with dynamically generated classes
112
+ email: mail@blakewilliamturner.com
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - LICENSE.txt
118
+ - README.md
119
+ - lib/get.rb
120
+ - lib/get/adapters/abstract_adapter.rb
121
+ - lib/get/adapters/active_record.rb
122
+ - lib/get/builders.rb
123
+ - lib/get/builders/ancestry_builder.rb
124
+ - lib/get/builders/base_builder.rb
125
+ - lib/get/builders/query_builder.rb
126
+ - lib/get/configuration.rb
127
+ - lib/get/core_extensions/string.rb
128
+ - lib/get/db.rb
129
+ - lib/get/entities.rb
130
+ - lib/get/entities/collection.rb
131
+ - lib/get/entities/single.rb
132
+ - lib/get/entity_factory.rb
133
+ - lib/get/errors.rb
134
+ - lib/get/run_methods.rb
135
+ - spec/get_spec.rb
136
+ - spec/spec_helper.rb
137
+ homepage: https://github.com/BlakeTurner/get
138
+ licenses:
139
+ - MIT
140
+ metadata: {}
141
+ post_install_message:
142
+ rdoc_options: []
143
+ require_paths:
144
+ - lib
145
+ required_ruby_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ requirements: []
156
+ rubyforge_project:
157
+ rubygems_version: 2.2.2
158
+ signing_key:
159
+ specification_version: 4
160
+ summary: Get is a library designed to encapsulate Rails database queries and prevent
161
+ query pollution in the view layer.
162
+ test_files:
163
+ - spec/get_spec.rb
164
+ - spec/spec_helper.rb
165
+ has_rdoc: