Closed haxney closed 13 years ago
Yeah, I think you're right. I'll fix this in the adapters. Thanks!
Cool! Man, you're a responsive maintainer! :)
Hey, I was looking at this and I realized I can't quite reproduce what you're seeing. Can you post the exception that gets raised (including the stack trace if you have it)?
Sorry I didn't get back sooner, I've been busy.
Anyway, here's the stack trace I get when running it from RSpec.
Failure/Error: post :create, :event => {:pos => 'POINT(1 1'}
RGeo::Error::ParseError:
:end expected but nil found.
# /usr/lib/ruby/gems/1.9.1/gems/rgeo-0.3.1/lib/rgeo/wkrep/wkt_parser.rb:431:in `_expect_token_type'
# /usr/lib/ruby/gems/1.9.1/gems/rgeo-0.3.1/lib/rgeo/wkrep/wkt_parser.rb:305:in `_parse_point'
# /usr/lib/ruby/gems/1.9.1/gems/rgeo-0.3.1/lib/rgeo/wkrep/wkt_parser.rb:233:in `_parse_type_tag'
# /usr/lib/ruby/gems/1.9.1/gems/rgeo-0.3.1/lib/rgeo/wkrep/wkt_parser.rb:163:in `parse'
# /usr/lib/ruby/gems/1.9.1/gems/activerecord-mysql2spatial-adapter-0.3.2/lib/active_record/connection_adapters/mysql2spatial_adapter/spatial_column.rb:106:in `convert_to_geometry'
# /usr/lib/ruby/gems/1.9.1/gems/activerecord-mysql2spatial-adapter-0.3.2/lib/active_record/connection_adapters/mysql2spatial_adapter/spatial_column.rb:78:in `type_cast'
# /usr/lib/ruby/gems/1.9.1/gems/activerecord-3.0.7/lib/active_record/attribute_methods/dirty.rb:83:in `field_changed?'
# /usr/lib/ruby/gems/1.9.1/gems/activerecord-3.0.7/lib/active_record/attribute_methods/dirty.rb:57:in `write_attribute'
# /usr/lib/ruby/gems/1.9.1/gems/activerecord-3.0.7/lib/active_record/attribute_methods/write.rb:14:in `pos='
# /usr/lib/ruby/gems/1.9.1/gems/activerecord-3.0.7/lib/active_record/base.rb:1559:in `block in attributes='
# /usr/lib/ruby/gems/1.9.1/gems/activerecord-3.0.7/lib/active_record/base.rb:1555:in `each'
# /usr/lib/ruby/gems/1.9.1/gems/activerecord-3.0.7/lib/active_record/base.rb:1555:in `attributes='
# /usr/lib/ruby/gems/1.9.1/gems/activerecord-3.0.7/lib/active_record/base.rb:1407:in `initialize'
# ./app/controllers/events_controller.rb:38:in `new'
# ./app/controllers/events_controller.rb:38:in `create'
# /usr/lib/ruby/gems/1.9.1/gems/actionpack-3.0.7/lib/action_controller/metal/implicit_render.rb:5:in `send_action'
# /usr/lib/ruby/gems/1.9.1/gems/actionpack-3.0.7/lib/abstract_controller/base.rb:150:in `process_action'
# /usr/lib/ruby/gems/1.9.1/gems/actionpack-3.0.7/lib/action_controller/metal/rendering.rb:11:in `process_action'
# /usr/lib/ruby/gems/1.9.1/gems/actionpack-3.0.7/lib/abstract_controller/callbacks.rb:18:in `block in process_action'
# /usr/lib/ruby/gems/1.9.1/gems/activesupport-3.0.7/lib/active_support/callbacks.rb:436:in `_run__595098887__process_action__153318358__callbacks'
# /usr/lib/ruby/gems/1.9.1/gems/activesupport-3.0.7/lib/active_support/callbacks.rb:410:in `_run_process_action_callbacks'
# /usr/lib/ruby/gems/1.9.1/gems/activesupport-3.0.7/lib/active_support/callbacks.rb:94:in `run_callbacks'
# /usr/lib/ruby/gems/1.9.1/gems/actionpack-3.0.7/lib/abstract_controller/callbacks.rb:17:in `process_action'
# /usr/lib/ruby/gems/1.9.1/gems/actionpack-3.0.7/lib/action_controller/metal/instrumentation.rb:30:in `block in process_action'
# /usr/lib/ruby/gems/1.9.1/gems/activesupport-3.0.7/lib/active_support/notifications.rb:52:in `block in instrument'
# /usr/lib/ruby/gems/1.9.1/gems/activesupport-3.0.7/lib/active_support/notifications/instrumenter.rb:21:in `instrument'
# /usr/lib/ruby/gems/1.9.1/gems/activesupport-3.0.7/lib/active_support/notifications.rb:52:in `instrument'
# /usr/lib/ruby/gems/1.9.1/gems/actionpack-3.0.7/lib/action_controller/metal/instrumentation.rb:29:in `process_action'
# /usr/lib/ruby/gems/1.9.1/gems/actionpack-3.0.7/lib/action_controller/metal/rescue.rb:17:in `process_action'
# /usr/lib/ruby/gems/1.9.1/gems/actionpack-3.0.7/lib/abstract_controller/base.rb:119:in `process'
# /usr/lib/ruby/gems/1.9.1/gems/actionpack-3.0.7/lib/abstract_controller/rendering.rb:41:in `process'
# /usr/lib/ruby/gems/1.9.1/gems/actionpack-3.0.7/lib/action_controller/metal/testing.rb:12:in `process_with_new_base_test'
# /usr/lib/ruby/gems/1.9.1/gems/actionpack-3.0.7/lib/action_controller/test_case.rb:412:in `process'
# /usr/lib/ruby/gems/1.9.1/gems/actionpack-3.0.7/lib/action_controller/test_case.rb:47:in `process'
# /usr/lib/ruby/gems/1.9.1/gems/actionpack-3.0.7/lib/action_controller/test_case.rb:355:in `post'
# ./spec/controllers/events_controller_spec.rb:76:in `block (5 levels) in <top (required)>'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/example.rb:48:in `instance_eval'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/example.rb:48:in `block in run'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/example.rb:107:in `with_around_hooks'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/example.rb:45:in `run'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/example_group.rb:294:in `block in run_examples'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/example_group.rb:290:in `map'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/example_group.rb:290:in `run_examples'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/example_group.rb:262:in `run'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/example_group.rb:263:in `block in run'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/example_group.rb:263:in `map'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/example_group.rb:263:in `run'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/example_group.rb:263:in `block in run'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/example_group.rb:263:in `map'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/example_group.rb:263:in `run'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/example_group.rb:263:in `block in run'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/example_group.rb:263:in `map'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/example_group.rb:263:in `run'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/command_line.rb:24:in `block (2 levels) in run'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/command_line.rb:24:in `map'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/command_line.rb:24:in `block in run'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/reporter.rb:12:in `report'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/core/command_line.rb:21:in `run'
# /usr/lib/ruby/gems/1.9.1/gems/rspec-core-2.6.3/lib/rspec/monkey/spork/test_framework/rspec.rb:5:in `run_tests'
# /usr/lib/ruby/gems/1.9.1/gems/spork-0.9.0.rc7/lib/spork/run_strategy/forking.rb:13:in `block in run'
# /usr/lib/ruby/gems/1.9.1/gems/spork-0.9.0.rc7/lib/spork/forker.rb:21:in `block in initialize'
# /usr/lib/ruby/gems/1.9.1/gems/spork-0.9.0.rc7/lib/spork/forker.rb:18:in `fork'
# /usr/lib/ruby/gems/1.9.1/gems/spork-0.9.0.rc7/lib/spork/forker.rb:18:in `initialize'
# /usr/lib/ruby/gems/1.9.1/gems/spork-0.9.0.rc7/lib/spork/run_strategy/forking.rb:9:in `new'
# /usr/lib/ruby/gems/1.9.1/gems/spork-0.9.0.rc7/lib/spork/run_strategy/forking.rb:9:in `run'
# /usr/lib/ruby/gems/1.9.1/gems/spork-0.9.0.rc7/lib/spork/server.rb:48:in `run'
# /usr/lib/ruby/1.9.1/drb/drb.rb:1558:in `perform_without_block'
# /usr/lib/ruby/1.9.1/drb/drb.rb:1518:in `perform'
# /usr/lib/ruby/1.9.1/drb/drb.rb:1592:in `block (2 levels) in main_loop'
# /usr/lib/ruby/1.9.1/drb/drb.rb:1588:in `loop'
# /usr/lib/ruby/1.9.1/drb/drb.rb:1588:in `block in main_loop
I suppose there should be some part of ActiveRecord which catches these errors and adds the appropriate entry into errors
.
A simpler example would be:
::RGeo::Cartesian.preferred_factory.parse_wkt('POINT(1 ')
Which gives
RGeo::Error::ParseError: Numeric expected but nil found.
from /usr/lib/ruby/gems/1.9.1/gems/rgeo-0.3.1/lib/rgeo/wkrep/wkt_parser.rb:431:in `_expect_token_type'
As it should, but this error should be caught and dealt with at the ActiveRecord level.
Thanks. So I finally got a chance to look into this. It looks like the reason I wasn't able to repro it was because it's not happening with the postgis adapter, which is where I was trying to do my tests. It happens only with the mysql and spatialite adapters.
Regarding errors, I believe the intent of the rails errors
hash is to hold validation results. Here we're instead doing a typecast from strings to geometry objects when the attribute is set. Since this isn't happening during validation, I don't think it's right to be mucking with the errors object at that time. Instead, we can match the behavior of the other specialized field types (such as dates) that do typecasts: they just silently degrade the value to nil if the typecast fails. For example, if "my_date" is a date attribute:
obj = MyTable.new
obj.my_date = "2001-01-01"
obj.my_date # => Mon, 01 Jan 2001
obj.my_date = "hello"
obj.my_date # => nil
obj.errors # => {}
This is in fact the behavior of the postgis adapter already. I'll fix the mysql and spatialite adapters to match. I know this means we don't actually get to see the parse exception, but this is how Rails's other field types work, so I think it's best to follow suit.
That seems like a strange behavior to have; I feel like it would be more useful to have some indication mechanism of a failure to typecast. Of course, if that's what Rails does for its other fields, it absolutely makes sense for rgeo-activerecord
to do the same. Just a weird decision on Rails' part.
I agree, probably not the best decision on Rails' part, unless it's using some other error reporting side channel that I'm not aware of. For now it is what it is.
Anyway, the mysql and spatialite adapters have now been updated (version 0.4.0) to return nil rather than raise the exception in this case (among other changes). I'm going to close this issue for now.
I am writing some tests of the validation, and I am running into the problem that trying to set a spatial column to an invalid value raises an exception before validators can run. For example, I have a spec like this:
which tries to create a
Model
with the invalid WKT string"POINT(1 )"
. I expect to get an entry inmodel.errors
, but an exception is raised, causing the spec to fail. There should be a way to have this problem show up as an entry inerrors
rather than an exception, since Rails has an easier time dealing with the former.As a workaround, I overrode the
pos=
method with the following:Which simply turns the exception into an entry in
errors
.