hoodoo 1.1.2 → 1.1.3

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,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