My setup:
Ruby 2.0.0
Rails 3.2.12
most recent pg gem
most recent activerecord-postgis-adapter gem
most recent rgeo-geojson gem
Postgres 9.1.6
PostGIS 2
I've asked something similar a few days ago. (Need to convert a Boolean from Postgres (== String) to a Ruby Boolean). There I had to convert a value from a custom select to boolean. This was pretty straight forward, by just adding this to my model:
def value_name
ActiveRecord::ConnectionAdapters::Column.value_to_boolean(self[:value_name])
end
But now I receive a value of type Point (which is a type from PostGIS). Its string representation in database looks like "0101000000EE7C3F355EF24F4019390B7BDA011940", but it has to become a (I think) RGeo::Feature::Point or maybe RGeo::Geos::CAPIPointImpl ?!
Now I looked at ActiveRecord::ConnectionAdapters::Column (http://rubydoc.info/docs/rails/ActiveRecord/ConnectionAdapters/Column), I can only find value_to_boolean and value_to_decimal.
Then I recognized that there is also ActiveRecord::ConnectionAdapters::PostgreSQLColumn (http://rubydoc.info/docs/rails/ActiveRecord/ConnectionAdapters/PostgreSQLColumn), but it doesn't seem to have any useful methods.
Thank you!
Try something like that :
def value_name
point_regex = /POINT \(([0-9]*\.[0-9]*) ([0-9]*\.[0-9]*)\)/
match_data = self[:value_name].match(point_regex)
match_data[1], match_data[2]
end
It will return a couple of value [x, y] representing your point.
You have to do the inverse, i.e. define a value_name=(x,y).
I found a solution (searched for rgeo and parse):
def my_value
a = RGeo::WKRep::WKBParser.new
a.parse(self[:my_value])
end
I just need to know if it's the right way. Coming from the Java world, I read it like this:
For every(!) my_value: Create a new instance of WKBParser
If that's the case: How can I create just one instance of it and reuse it every time the method is called?
Or in more detail: How does the automatic parser handle this? Where does it get called?
I think it get's created here: https://github.com/dazuma/activerecord-postgis-adapter/blob/master/lib/active_record/connection_adapters/postgis_adapter/spatial_column.rb (Line 179 and 181)
But how can I reuse this in my model?
Background information: The parser automatically works for real table columns, but my column gets created within the query.
Found the right way:
def my_value
RGeo::Geos.factory.parse_wkb(self[:my_value])
end
:)
Yep, postgis adapter really works, and provides much more elegant solution.
In my Gemfile:
gem 'rails', '3.2.12'
gem 'pg'
gem 'rgeo-activerecord'
gem 'activerecord-postgis-adapter'
then in the model:
class MyPoint < ActiveRecord::Base
set_rgeo_factory_for_column(:geom, RGeo::Geos.factory(srid: 4326))
attr_accessible :geom
end
to find if Geos is supported in your RGeo installation:
>> RGeo::Geos::supported?
true
And that's what you get - model's attribute geom which is in this case RGeo::Geos::CAPIPointImpl (will vary depending on factory class). To get this factory working you need to have Geos with development files installed before installing RGeo. Doesn't have to be Geos factory and 4326, RGeo has factories implemented in pure Ruby, find your best match factory class and SRID in the docs: http://rubydoc.info/github/dazuma/rgeo/master/index
Related
I'm working on a ruby gem which can generate some code in other language. The gem needs to load the models in the current rails app. And it's implemented as a generator which accepts one parameter -- the table name. Inside it, read the columns definition from that table in this way:
tableklass = table_name.to_s.constantize # get the class name from table_name
cols = tableklazz.columns # get columns definitions.
When I run the generator 'rails g mygen Product'. It always gave me the error below:
.../ruby/gems/2.3.0/gems/activesupport-4.2.4/lib/active_support/inflector/methods.rb:261:in `const_get': wrong constant name products (NameError)
How can I fix this error? Or whether is there other better way to do so (read table information to generate some code)?
constantize expects camelized input. I am not sure, what is happening inside your generator, but it looks like constantize finally receives products as a parameter. Safest version of what you are trying to do:
table_name.to_s.singularize.camelize.constantize
Everything below would work:
:products.to_s.singularize.camelize.constantize
:product.to_s.singularize.camelize.constantize
'product'.to_s.singularize.camelize.constantize
Product.to_s.singularize.camelize.constantize
I tried out the globalize gem but this seems like a big overhead. Most of the time I just need one attribute translated. I'm using postgreSQL and would rather use a hstore for this. That way I won't get additional tables and performance should be at least as good if not better.
Are there any gems that use this approach or would this mean a custom development?
Seems like you're searching for the hstore_translate gem. I have not tested it but it appears to suit your needs precisely from the description:
Rails I18n library for ActiveRecord model/data translation using PostgreSQL's hstore datatype. It provides an interface inspired by Globalize3 but removes the need to maintain separate translation tables.
I've been using the hstore_translate gem and love it.
Say for example you have a Project model schema with title:string and content:text. If you want content to be translated, all that needs to be done is to create a migration:
class AddTranslationToProjects < ActiveRecord::Migration
def change
add_column :projects, :title_translations, 'hstore'
end
end
and inside of project.rb:
class Project < ActiveRecord::Base
translates :title
end
thats it! Nothing else to do in the form or wherever. Works for integers and booleans as well. The only extra step to do is activate hstore if using postgres: CREATE EXTENSION hstore
I've recently created the gem awesome_hstore_translate, which is based on the original hstore_translate by Rob Worley.
hstore_translate uses alias_method_chain, which got deprecated with Rails 5.0.
My gem has the same functionality as it's original, but it's a bit more modern. It stores it's data in columns without a suffix, because I think the database model looks more clean that way. The raw data is still available. E. g. Page.first.title_raw will give you the hstore hash.
I'd like to use a database within a Ruby gem that I'm writing. The gem is meant to be used within Rails applications, and will contain an inverted index of documents passed in from the main Rails app.
I'm a bit confused as to how to go about this. Should I hook into the main Rails database somehow? Or should I have a standalone database? Ideally I'd just like to use ActiveRecord to create, update, delete and query entries but I am not sure how I'd set this up.
Data would go into the database at this point:
module ActiveRecordExtension
extend ActiveSupport::Concern
class_methods do
def foo
"bar"
end
end
included do
after_save :add_to_inverted_index
end
def add_to_inverted_index
# This is where I'd take the fields from the Rails app
# and include them to my inverted index. However, I'm struggling
# to find a way to hook into a database from my gem to do this.
end
end
# Include the extension
ActiveRecord::Base.send(:include, ActiveRecordExtension)
Suggestions are much appreciated! Thanks
Well after your clarification, you should use main Rails database. Just create a migration and insert the table(s) you need. You should do that because:
Everyone that uses your gem will know what is stored at the database.
With migrations, you can easily rollback the migration, making it simple to reverse something, if needed.
There's no need to create extra dependencies. Imagine a project that you did in RoR and think if the mess it would be if every gem that you used created its own database.
Maybe you should take a look at known gems and how they do that. I'm thinking about Devise.
I have a project with PostGIS integration through RGeo gem. Rails 4, ruby 2.1.
RGeo has standard method contains? which checks is point is located in polygon. Everything is fine in development (on MacOS X). But when I push my code to production server (Ubuntu 12.04) I have this error:
ActionView::Template::Error (Method Geometry#contains? not defined.):
app/models/address.rb:28:in `block in define_courier_area'
app/models/address.rb:28:in `define_courier_area'
The code is very simple. I just need to return an array of all CourierArea instances, where are Address coords located.
address.rb
class Address < ActiveRecord::Base
def define_courier_area # HAVE TO BE REFACTORED
arr = []
CourierArea.all.each { |ca| arr << ca if ca.poly.contains?(self.coords)}
arr.first if arr.first
end
end
I suppose, production environment can't obtain access to contains? method, which is provided by RGeo gem. How can I solve this problem? Maybe I should insert require 'some_rgeo_file'?
Problem has been solved.
RGeo uses some external libraries. For geometry calculations (distance, area, point-in-polygon) this library is GEOS. When I tried to invoke contains? RGeo was seeking for GEOS but it was not installed.
Moreover there is an RGeo method RGeo::Geos.supported? for checking GEOS availability. If it will return false, you probably has no GEOS installed or RGeo has problems with connecting with it. If it will return true – boya! you can use geometry calculation methods!
It looks like RGeo might not be installed in your production environment. Run this:
gem install rgeo
That should fix it.
I am using the geokit gem and plugin with rails 3. It seems there is a known issue with them, which can be seen here http://github.com/andre/geokit-rails/issues#issue/15
Now, I tried to follow the solution provided at the bottom. I pasted that function definition, at the end of the file, just above acts_as_mapable, and just after the first time it was called, but nothing happened each time.
Any idea what else can be done?
Thanks
I ran into similar problems upgrading my app to rails 3. I'm still using Geokit for geocoding but Active Record scopes for distance based database queries. It's pretty convenient, and you still get all of the Active Record 3 goodness. Here's an example from my User model:
scope :near, lambda{ |*args|
origin = *args.first[:origin]
if (origin).is_a?(Array)
origin_lat, origin_lng = origin
else
origin_lat, origin_lng = origin.lat, origin.lng
end
origin_lat, origin_lng = deg2rad(origin_lat), deg2rad(origin_lng)
within = *args.first[:within]
{
:conditions => %(
(ACOS(COS(#{origin_lat})*COS(#{origin_lng})*COS(RADIANS(users.lat))*COS(RADIANS(users.lng))+
COS(#{origin_lat})*SIN(#{origin_lng})*COS(RADIANS(users.lat))*SIN(RADIANS(users.lng))+
SIN(#{origin_lat})*SIN(RADIANS(users.lat)))*3963) <= #{within}
),
:select => %( users.*,
(ACOS(COS(#{origin_lat})*COS(#{origin_lng})*COS(RADIANS(users.lat))*COS(RADIANS(users.lng))+
COS(#{origin_lat})*SIN(#{origin_lng})*COS(RADIANS(users.lat))*SIN(RADIANS(users.lng))+
SIN(#{origin_lat})*SIN(RADIANS(users.lat)))*3963) AS distance
)
}
}
Here's a blog post with a little more detail on the subject: http://stcorbett.com/code/distance-queries-with-rails-3-without-geokit/
jlecour's port to rails 3 should solve any issues you were having last year.
Make sure you're using mysql or postgres if you're doing distance calculations.
After trouble installing the geokit-rails3 gem on Rails 3.1 I moved to the geocoder gem. It has distance calculation as well (be sure to not forget the s in #your_model.nearby*s*(5)). There is also a Railscast.
Here is port of geokit to rails 3, incomplete through:
https://github.com/jlecour/geokit-rails3
For those still having trouble with geokit, i moved on to using mongodb... which has inbuilt distance search n all...
Hey Amit, Not sure if you sorted this out yet but I'll tell you what I did just in case.
I forked andre's geokit-rails source and then cloned it locally and added the code from this gist at line 34 of lib/geokit-rails/acts-as-mappable.rb, just after the line that reads module ClassMethods # :nodoc:.
Then I commited those changes back to my forked repo on github and used my fork to install the source as a plugin to my rails 3 app. That seemed to work straight away, but make sure you have the acts_as_mappable line added to whatever model you are wanting to do distance calculations on and make sure you have two float columns on that db named :lat and :lng.