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