It appears that find_or_create_by_xxx is attempting type conversion
where a simple create(…) does not. Bug?
Before going further, here’s the schema and the model:
create_table “weather_stations”, :force => true do |t|
t.string “callsign”
t.decimal “lat”, :precision => 9, :scale => 6
t.decimal “lng”, :precision => 9, :scale => 6
t.decimal “elevation_m”, :precision => 5, :scale => 1
end
class WeatherStation < ActiveRecord::Base
validates_presence_of :callsign, :lat, :lng
validates_uniqueness_of :callsign, :case_sensitive => false
acts_as_mappable
…
end
[Note the “validates_uniqueness_of :callsign, :case_sensitive => false”
– that comes into play here.]
** Assume ‘specs’ is a hash whose :callsign is a string:
specs
=> {:callsign=>“0N6”, :lat=>39.0128889, :lng=>-75.5339722,
:elevation_m=>15.0}specs[:callsign].class
=> String
** The call to find_or_create_by_callsign fails – note that id comes
back nil and the other fields are not filled in:
w = WeatherStation.find_or_create_by_callsign(specs[:callsign], :lat => specs[:lat], :lng => specs[:lng])
=> #<WeatherStation id: nil, callsign: “0N6”, domain: nil, created_at:
nil, updated_at: nil, lat: nil, lng: nil, elevation_m: nil>
** The generated SQL shows a rollback:
WeatherStation Load (22.2ms) SELECT * FROM weather_stations
WHERE
(weather_stations
.callsign
= ‘0N6’) LIMIT 1
SQL (0.4ms) BEGIN
WeatherStation Load (230.6ms) SELECT weather_stations
.id FROM
weather_stations
WHERE (LOWER(weather_stations
.callsign
) = BINARY
‘0n6’) LIMIT 1
SQL (1.0ms) ROLLBACK
** Using a simple .create() instead works:
x = WeatherStation.create(:callsign => specs[:callsign], :lat => specs[:lat], :lng => specs[:lng])
=> #<WeatherStation id: 30774, callsign: “0N6”, domain: nil, created_at:
“2010-05-16 06:38:22”, updated_at: “2010-05-16 06:38:22”, lat:
#BigDecimal:8db79b0,‘0.390128889E2’,12(16), lng:
#BigDecimal:8db7828,’-0.755339722E2’,12(16), elevation_m: nil>
** …but looking at the SQL shows that it does the same check for
uniqueness before inserting the row. Since the record isn’t in the
table, it’s okay that it fails:
SQL (70.0ms) BEGIN
WeatherStation Load (365.4ms) SELECT weather_stations
.id FROM
weather_stations
WHERE (LOWER(weather_stations
.callsign
) = BINARY
‘0n6’) LIMIT 1
WeatherStation Create (2.0ms) INSERT INTO weather_stations
(callsign
, domain
, created_at
, updated_at
, lat
, lng
,
elevation_m
) VALUES(‘0N6’, NULL, ‘2010-05-16 06:38:22’, ‘2010-05-16
06:38:22’, 39.0128889, -75.5339722, NULL)
SQL (3.3ms) COMMIT
I don’t understand enough about SQL’s “BINARY ‘0n6’” – is that the
source of the trouble? Or is my validation bogus? Why is
find_or_create_by_callsign() failing?
TIA.
- ff