get 0.1.0

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