hoodoo 1.1.2 → 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZGUwNzBiZDE0NDVjYjljNDM0MzNjYzYzMjYxMWY0MzhhYmY4NTI2OQ==
4
+ NWM1MTMyZWRmNGNkZDExY2IyYThiYjdhZDA2OGJmNWFiNTY2NDY4ZA==
5
5
  data.tar.gz: !binary |-
6
- YmUxY2FiOWQzMDY1ZmEwZGM1NzYxM2YzNDVjYzFhNWE2N2FmNDZmNA==
6
+ N2YwMGE5ODIxNmFiNzEzMDc4YjE3YTk1ZWZiOTk2ODJhNTJmNjFhOQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- YjJkZGJkMTM4MmM5ZWNhYmQxOWI3Y2JkYzdmN2I3OGFjZTU4OGQ4MDY3MDI0
10
- MWYwZGI5MDIzZDNmYjI2ZTJiNzE4ZmU3MjNkZDM5YjA0ZjU1ODA1OTM1MzA0
11
- OWNmYjFlNjRlZTVjZThiMWIxNTJlZTA1NzU0MTQ1Y2U5NDFhMTA=
9
+ MmY2ODExNTRkZmJiYThmN2MyNGFkYTk0MjU4YjBhMzFkMTExZmU2YmNiMDNk
10
+ MjAwYzQ4NzRiM2FlMTA5NjQxNjdlMzM3OTA3YzQ3NzA2N2FlMTRhYTI4OGY4
11
+ NWUzNjFlNTE4NjdiNmE4YzMwYmZmMTJkZTg4NTdiZWVlYTVkODg=
12
12
  data.tar.gz: !binary |-
13
- MDdmOTMyNTVhMGZiZDAxMzkyNDE1OGQ3MTIxNTJiMzViZWQ2MGJhMTUzYjRh
14
- MGEwMWM1M2EyZTI3MWVlZTA5ZTE4ZjkwN2M5OGRiZDBlMTY3NjE3M2JhNTYw
15
- NWQ5ZTYyN2EzODI2MWVmODBmMmQ4YzMyMDMyZjQ0OTc1MDdjMGI=
13
+ ZjViMjVmM2FjMzY5YzBmYTU2NTljM2FiMjAwNWU3OTRiYzZhZjdiMmMzNzgz
14
+ MmE2ZmNjYWE2NTA4MGU4ODkwNjhlOTI0OGRlNjQ5ZjM1ZjIxYTI0ZjA5YWMz
15
+ ZGQ1OWIyYzNhMzBmZTU4Njk2Nzc0YmJjNWI4OGNhYmY4NDIwNjY=
@@ -152,48 +152,7 @@ module Hoodoo
152
152
  # Returns a found model instance or +nil+ for no match.
153
153
  #
154
154
  def acquire( ident )
155
- extra_fields = self.acquired_with()
156
-
157
- id_fields = [ :id ] + extra_fields
158
- id_fields.each do | field |
159
-
160
- # This is fiddly.
161
- #
162
- # You must use a string with field substitution approach, rather
163
- # than e.g. ".where( :field => :ident )". AREL/ActiveRecord will,
164
- # in the latter case, compose rational SQL based on column data
165
- # types. If you have an *integer* ID field, then, it'll try to
166
- # convert a *string* ident to an integer. This can give Hilarious
167
- # Consequences. Consider looking up on (integer) field "id" or
168
- # (text) field "uuid", with a string ident of "1f294942..." - the
169
- # text UUID would be fine, but the integer ID may end up with the
170
- # UUID being "to_i"'d, yielding integer 1. If the ID field is
171
- # looked at first, you're highly likely to find the wrong record.
172
- #
173
- # The solution is, as written, simple; just use the substitution
174
- # approach rather than higher level AREL, causing a string-like SQL
175
- # query on all adapters which SQL handles just fine for varying
176
- # field data types.
177
- #
178
- # The caveat is that should the database object to mismatched type
179
- # comparisons it will actually raise an error - we see this on
180
- # for example PostgreSQL where a column is an integer but we try
181
- # to match it against a string that cannot be cleanly converted to
182
- # one (e.g. trying to find an integer column 'id' based on a text
183
- # UUID value containing alphabetic characters). That is still
184
- # preferable to looking up the wrong record!
185
-
186
- checker = where( [ "\"#{ self.table_name }\".\"#{ field }\" = ?", ident ] )
187
-
188
- # If we have found a record return it, otherwise continue to loop
189
- # through +id_fields+
190
- #
191
- if checker.first
192
- return checker.first
193
- end
194
- end
195
-
196
- return nil
155
+ return acquisition_scope( ident ).first
197
156
  end
198
157
 
199
158
  # Implicily secure, translated, dated etc. etc. version of #acquire,
@@ -294,6 +253,31 @@ module Hoodoo
294
253
  self.nz_co_loyalty_hoodoo_show_id_fields || []
295
254
  end
296
255
 
256
+ # Back-end to #acquire and therefore, in turn, #acquire_in. Returns
257
+ # an ActiveRecord::Relation instance which scopes the search for a
258
+ # record by +id+ and across any other columns specified by
259
+ # #acquire_with, via SQL +OR+.
260
+ #
261
+ # Normally such a scope could only ever return a single record based
262
+ # on an assuption of uniqueness constraints around columns which one
263
+ # might use in an equivalent of a +find+ call. In some instances
264
+ # however - e.g. a table that contains historic representations of a
265
+ # model as well as its 'current' representation - there may be more
266
+ # than one result and the returned value from this method may need to
267
+ # be chained in with other scopes to form a useful query.
268
+ #
269
+ def acquisition_scope( ident )
270
+ extra_fields = self.acquired_with()
271
+ arel_table = self.arel_table()
272
+ arel_query = arel_table[ :id ].eq( ident )
273
+
274
+ extra_fields.each do | field |
275
+ arel_query = arel_query.or( arel_table[ field ].eq( ident ) )
276
+ end
277
+
278
+ return where( arel_query )
279
+ end
280
+
297
281
  # Generate an ActiveRecord::Relation instance which can be used to
298
282
  # count, retrieve or further refine a list of model instances from
299
283
  # the database.
@@ -12,6 +12,6 @@ module Hoodoo
12
12
  # The Hoodoo gem version. If this changes, ensure that the date in
13
13
  # "hoodoo.gemspec" is correct and run "bundle install" (or "update").
14
14
  #
15
- VERSION = '1.1.2'
15
+ VERSION = '1.1.3'
16
16
 
17
17
  end
@@ -154,47 +154,50 @@ describe Hoodoo::ActiveRecord::Finder do
154
154
  expect( found ).to eq(nil) # Not in 'group 1'
155
155
  end
156
156
 
157
- # previously the finder would always cause an extra
158
- # call to the database to preform a count
157
+ # Early versions of the 'acquire'-backed methods always inadvertently
158
+ # performed two database calls, via a count then a true find. This was
159
+ # refactored to only make one call per attribute, but that in turn can
160
+ # be much improved via the AREL table to compose a single query that
161
+ # uses "OR" to get the database to check each attribute in order. Thus
162
+ # every test below expects exactly one database call only.
159
163
  #
160
- it 'does not do excessive database calls' do
164
+ context 'only makes one database call when' do
161
165
 
162
- count = count_database_calls_in do
163
- found = RSpecModelFinderTest.acquire( @id )
164
- expect( found ).to eq(@a)
165
- end
166
- # the id will be the first searched in the finder
167
- # therefore only one call will be made
168
- expect( count ).to eq( 1 )
166
+ it 'finding on the first attribute' do
167
+ count = count_database_calls_in do
168
+ found = RSpecModelFinderTest.acquire( @id )
169
+ expect( found ).to eq(@a)
170
+ end
169
171
 
170
- count = count_database_calls_in do
171
- found = RSpecModelFinderTest.acquire( @uuid )
172
- expect( found ).to eq(@b)
172
+ expect( count ).to eq( 1 )
173
173
  end
174
174
 
175
- # the uuid will be the second searched in the finder
176
- # therefore two calls will be made, one for the
177
- # id and one for the uuid
178
- expect( count ).to eq( 2 )
175
+ it 'finding on the second attribute' do
176
+ count = count_database_calls_in do
177
+ found = RSpecModelFinderTest.acquire( @uuid )
178
+ expect( found ).to eq(@b)
179
+ end
179
180
 
180
- count = count_database_calls_in do
181
- found = RSpecModelFinderTest.acquire( @code )
182
- expect( found ).to eq(@c)
181
+ expect( count ).to eq( 1 )
183
182
  end
184
183
 
185
- # the code will be the third searched in the finder
186
- # therefore three calls will be made, one for the
187
- # id, one for the uuid and one for the code
188
- expect( count ).to eq( 3 )
189
-
184
+ it 'finding on the third attribute' do
185
+ count = count_database_calls_in do
186
+ found = RSpecModelFinderTest.acquire( @code )
187
+ expect( found ).to eq(@c)
188
+ end
190
189
 
191
- count = count_database_calls_in do
192
- found = RSpecModelFinderTest.acquire( Hoodoo::UUID.generate )
193
- expect( found ).to be_nil
190
+ expect( count ).to eq( 1 )
194
191
  end
195
192
 
196
- # the finder will search all three
197
- expect( count ).to eq( 3 )
193
+ it 'checking all three attributes but finding nothing' do
194
+ count = count_database_calls_in do
195
+ found = RSpecModelFinderTest.acquire( Hoodoo::UUID.generate )
196
+ expect( found ).to be_nil
197
+ end
198
+
199
+ expect( count ).to eq( 1 )
200
+ end
198
201
  end
199
202
  end
200
203
 
@@ -347,6 +350,23 @@ describe Hoodoo::ActiveRecord::Finder do
347
350
 
348
351
  # ==========================================================================
349
352
 
353
+ context 'acquisition_scope' do
354
+ it 'SQL generation is as expected' do
355
+ sql = RSpecModelFinderTest.acquisition_scope( @id ).to_sql()
356
+ expect( sql ).to eq( "SELECT \"r_spec_model_finder_tests\".* "<<
357
+ "FROM \"r_spec_model_finder_tests\" " <<
358
+ "WHERE (" <<
359
+ "(" <<
360
+ "\"r_spec_model_finder_tests\".\"id\" = '#{ @id }' OR " <<
361
+ "\"r_spec_model_finder_tests\".\"uuid\" = '#{ @id }'" <<
362
+ ") OR " <<
363
+ "\"r_spec_model_finder_tests\".\"code\" = '#{ @id }'" <<
364
+ ")" )
365
+ end
366
+ end
367
+
368
+ # ==========================================================================
369
+
350
370
  context 'lists' do
351
371
  it 'lists with pages, offsets and counts' do
352
372
  @list_params.offset = 1 # 0 is first record
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hoodoo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Loyalty New Zealand
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-03 00:00:00.000000000 Z
11
+ date: 2016-02-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: uuidtools