Rails Postgresql array of hstore - ruby-on-rails

Does somoone knows if it is possible to have array of hstore in rails 4 ? i tryied with
add_column :orders, :frozen_content, :hstore , array: true
but i got
PG::Error: ERROR: malformed array literal:
when i try to save

In principle, yes, but as you've found it isn't being escaped correctly when saved. I've just today logged an issue about that, see https://github.com/rails/rails/issues/11135 (includes a fix patch and some demo code)

This is a bug that exists at least in Rails 4.0.1 .
A pull request was proposed to fix it, but until it is merged you can monkey-patch Rails:
# config/initializers/extensions/postgres.rb
module ActiveRecord
module ConnectionAdapters
class PostgreSQLColumn < Column
module Cast
private
def quote_and_escape(value)
case value
when "NULL"
value
else
"\"#{value.gsub(/(["\\])/, '\\\\\1')}\""
end
end
end
end
end
end
Sidenote, I had trouble testing this in the Rails console because the initializer wasn't getting loaded there. You can do so with:
load "#{Rails.root}/config/initializers/extensions/postgres.rb"

You can sue the activerecord-postgres-hstore gem:
https://github.com/engageis/activerecord-postgres-hstore
From the docs:
Create a hstore-backed field:
class Person < ActiveRecord::Base
serialize :data, ActiveRecord::Coders::Hstore
end
Add fields to to it:
person = Person.new
person.data['foo'] = 'bar'
person.save
Query it:
Perosn.where("data -> 'foo' = 'bar'")
Railscast #345 (which is behind a paywall) covers using hstore in more details, using the activerecord-postgres-hstore gem:
http://railscasts.com/episodes/345-hstore
Note: I haven't tried it with rails 4... YMMV.

Related

Model's changes for Algolia not showing in the rails console in PRODUCTION

I have a model as bellow:
class Note < Record
include Shared::ContentBasedModel
algoliasearch disable_indexing: AppConfig.apis.algolia.disable_indexing do
attributes :id, :key
[:keywords, :tags, :description, :summary].each do |attr|
attribute [attr] do
self.meta[attr.to_s]
end
end
attribute :content do
Nokogiri.HTML(self.meta["html"]).text.split(' ').reject { |i| i.to_s.length < 5 }.map(&:strip).join ' '
end
attribute :photo do
unless self.meta["images"].blank?
self.meta["images"].first["thumb"]
end
end
attribute :slug do
to_param
end
attribute :url do
Rails.application.routes.url_helpers.note_path(self)
end
end
end
I am using AlgoliaSearch gem to index my models into the Algolia's API and when I was trying to index the model with some long content I get the following error:
Error: Algolia::AlgoliaProtocolError (400: Cannot POST to https://XXXX.algolia.net/1/indexes/Note/batch: {"message":"Record at the position 1 objectID=56 is too big size=20715 bytes. Contact us if you need an extended quota","position":1,"objectID":"56","status":400} (400))
After this, I removed EVERYTHING as the following, BUT I am still getting the exact same error!!
class Note < Record
include Shared::ContentBasedModel
algoliasearch disable_indexing: AppConfig.apis.algolia.disable_indexing do
attributes :id
end
end
It seems that Rails does not update the cached models.
Envirnoment: production
Rails version: v6
Question: Why is this happening & how can I clear cached model?
Note: I have tried everything, including removing the tmp/cache folder but it does not go away!
It looks like the object's size itself is bigger than some max allowed size.
objectID=56 is too big size=20715 bytes
Contact https://www.algolia.com/ (as the suggest)
Contact us if you need an extended quota
How do you check your code? Are you entering in rails console on your server? Might it be that you run an old release instead of the new one, in the case if you use Capistrano or Mina for deploy?

Using a store with Postgres HSTORE in Rails

I have a User Model with a database field 'remark'. I am using Postgres as Database and the 'remark' field is of type HSTORE. So 'remark' stores a hash with extra user information.
As suggested by someone I added a store to my 'User' model like this:
class User < ActiveRecord::Base
store :remark, accessors: [ :info ]
#some code
end
Now I can get the value in #user.remark['info'] by using this accessor '#user.info'. That works fine. But when I try to set and save a new value like this:
#user.info = "some info"
#user.save
I get the following error:
ActiveRecord::StatementInvalid: PG::InternalError: ERROR: Syntax error near '!' at position 4
Is it possible to use a HSTORE type databasefield this way? What am I doing wrong?
Add a custom coder to store:
class User < ActiveRecord::Base
store :remark, accessors: [ :info ], coder: HstoreCoder
#some code
end
HstoreCoder class:
class HstoreCoder
class << self
def load(hash)
hash
end
def dump(value)
value.to_hash
end
end
end
It should help.
For Rails 5 and hstore use store_accessor instead of store
store_accessor :remark, :info
NOTE: If you are using PostgreSQL specific columns like hstore or json there is no need for the serialization provided by .store. Simply use .store_accessor instead to generate the accessor methods. Be aware that these columns use a string keyed hash and do not allow access using a symbol.
https://api.rubyonrails.org/classes/ActiveRecord/Store.html

ActiveRecord does not convert array in string to array

Prior to Rails 4.2.0, ActiveRecord would automatically convert a String to the type specified by serialize.
class Post < ActiveRecord::Base
serialize :options, Array # Column type: 'text', DB: PostgreSQL
end
class PostTest < ActiveSupport::TestCase
test "assign options" do
post = Post.new
post.options = "[1,2,3]" # => "[1,2,3]"
end
end
In Rails 4.2.1
class Post < ActiveRecord::Base
serialize :options, Array
end
class PostTest < ActiveSupport::TestCase
test "assign options" do
post = Post.new
post.options = "[1,2,3]" # ActiveRecord::SerializationTypeMismatch: Attribute was supposed to be a Array, but was a String.
end
end
I can't find this in the documentation, changelogs. Was this type of String to Array conversion removed or is it a bug? In my use case, I have such a String from params assigned to a model. It works in Rails 4.1.10 but in Rails 4.2.1 it raises ActiveRecord::SerializationTypeMismatch.
I'm not sure which commit between 4.1.10 and 4.2.1 introduced this behaviour but it's not a bug as it is well documented here. In particular,
If class_name is specified, the serialized object must be of that class on assignment and retrieval. Otherwise SerializationTypeMismatch will be raised.
That's strange because YAMLColumn has been behaving like this for the years. Take a look at this commit.

How do I handle migration of ActiveRecord::Base which implemented a tableless model

We had the following class to process SOAP responses from external API(s) which worked fine in ruby 1.8.7, but it is looking for a table with these columns post migration (which has never been there) to ruby 1.9.2/rails 3.1, How do I handle this migratation?
class SoapResponse < ActiveRecord::Base
def self.columns
#columns ||= [];
end
def self.column(name, sql_type = nil, default = nil, null = true)
columns << ActiveRecord::ConnectionAdapters::Column.new(
name.to_s, default, sql_type.to_s, null)
end
def save(validate = true)
validate ? valid? : true
end
column :soap_payload, :text
serialize :soap_payload
end
You don't (have any migrations for it).
You don't have migrations and you don't inherit from ActiveRecord::Base as that is the database ORM component.
If you use a generator to create the model use --skip-migration to avoid generating the database migration file.
You can still get validations and conversions though, e.g.
class SoapResponse
include ActiveModel::Validations
include ActiveModel::Conversion
If you want some setup data (i.e. Constants, given there is no db! ) you can just define them here (Constants start with uppercase).
I guess you want the serialization capability of ActiveRecord::Base. That seems to be the only thing this class is using it for. If so, try calling this in your class definition:
self.abstract_class = true
Or you could try using ActiveModel::Serialization.
The pattern in your code looks like what's suggested in this answer for table-less AR models in Rails 2.

Rails model without database

I want to create a Rails (2.1 and 2.2) model with ActiveRecord validations, but without a database table. What is the most widely used approach? I've found some plugins that claim to offer this functionality, but many of them don't appear to be widely used or maintained. What does the community recommend I do? Right now I am leaning toward coming up with my own solution based on this blog post.
There is a better way to do this in Rails 3: http://railscasts.com/episodes/219-active-model
This is an approach I have used in the past:
In app/models/tableless.rb
class Tableless < ActiveRecord::Base
def self.columns
#columns ||= [];
end
def self.column(name, sql_type = nil, default = nil, null = true)
columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default,
sql_type.to_s, null)
end
# Override the save method to prevent exceptions.
def save(validate = true)
validate ? valid? : true
end
end
In app/models/foo.rb
class Foo < Tableless
column :bar, :string
validates_presence_of :bar
end
In script/console
Loading development environment (Rails 2.2.2)
>> foo = Foo.new
=> #<Foo bar: nil>
>> foo.valid?
=> false
>> foo.errors
=> #<ActiveRecord::Errors:0x235b270 #errors={"bar"=>["can't be blank"]}, #base=#<Foo bar: nil>>
There is easier way now:
class Model
include ActiveModel::Model
attr_accessor :var
validates :var, presence: true
end
ActiveModel::Model code:
module ActiveModel
module Model
def self.included(base)
base.class_eval do
extend ActiveModel::Naming
extend ActiveModel::Translation
include ActiveModel::Validations
include ActiveModel::Conversion
end
end
def initialize(params={})
params.each do |attr, value|
self.public_send("#{attr}=", value)
end if params
end
def persisted?
false
end
end
end
http://api.rubyonrails.org/classes/ActiveModel/Model.html
I think the blog post you are linking is the best way to go. I would only suggest moving the stubbed out methods into a module not to pollute your code.
just create a new file ending in ".rb" following the conventions you're used to (singular for file name and class name, underscored for file name, camel case for class name) on your "models/" directory. The key here is to not inherit your model from ActiveRecord (because it is AR that gives you the database functionality).
e.g.: for a new model for cars, create a file called "car.rb" in your models/ directory and inside your model:
class Car
# here goes all your model's stuff
end
edit: btw, if you want attributes on your class, you can use here everything you use on ruby, just add a couple lines using "attr_accessor":
class Car
attr_accessor :wheels # this will create for you the reader and writer for this attribute
attr_accessor :doors # ya, this will do the same
# here goes all your model's stuff
end
edit #2: after reading Mike's comment, I'd tell you to go his way if you want all of the ActiveRecord's functionality but no table on the database. If you just want an ordinary Ruby class, maybe you'll find this solution better ;)
For the sake of completeness:
Rails now (at V5) has a handy module you can include:
include ActiveModel::Model
This allows you to initialise with a hash, and use validations amongst other things.
Full documentation is here.
There's a screencast about non-Active Record model, made up by Ryan Bates. A good place to start from.
Just in case you did not already watch it.
I have built a quick Mixin to handle this, as per John Topley's suggestion.
http://github.com/willrjmarshall/Tableless
What about marking the class as abstract?
class Car < ActiveRecord::Base
self.abstract = true
end
this will tell rails that the Car class has no corresponding table.
[edit]
this won't really help you if you'll need to do something like:
my_car = Car.new
Use the Validatable gem. As you say, there are AR-based solutions, but they tend to be brittle.
http://validatable.rubyforge.org/
Anybody has ever tried to include ActiveRecord::Validations and ActiveRecord::Validations::ClassMethods in a non-Active Record class and see what happens when trying to setup validators ?
I'm sure there are plenty of dependencies between the validation framework and ActiveRecord itself. But you may succeed in getting rid of those dependencies by forking your own validation framework from the AR validation framework.
Just an idea.
Update: oopps, this is more or less what's suggested in the post linked with your question. Sorry for the disturbance.
Do like Tiago Pinto said and just don't have your model inherit from ActiveRecord::Base. It'll just be a regular Ruby class that you stick in a file in your app/models/ directory. If none of your models have tables and you're not using a database or ActiveRecord at all in your app, be sure to modify your environment.rb file to have the following line:
config.frameworks -= [:active_record]
This should be within the Rails::Initializer.run do |config| block.
You ought to checkout the PassiveRecord plugin. It gives you an ActiveRecord-like interface for non-database models. It's simple, and less hassle than fighting ActiveRecord.
We're using PassiveRecord in combination with the Validatable gem to get the OP's desired behaviour.

Resources