rails 6.6.1.1 serializer attributes with regex fails - ruby-on-rails

In my model I have two attributes which store regular expressions
label_regex, and text_regex
class RegularExpression < ApplicationRecord
serialize label_regex
serialize text_regex
end
But When i try to access them i get an error
regex = RegularExpression.find(16)
regex[:label_regex]
Psych::DisallowedClass Exception: Tried to load unspecified class: Regexp
grateful for any ideas

Even though most of the answers posted relate to yaml, its also true for activerecord
so adding
config.active_record.yaml_column_permitted_classes = [Regexp]
in config/application.rb
fixed the issue

Related

How to understand the colon operator usage in a Ruby class

I am learning both Ruby (2.3.x) & Rails (4.x). I was going through the Ruby On Rails Tutorial and I encountered this syntax and am having trouble reading it:
class User < ApplicationRecord
validates :name, presence: true
validates :email, presence: true
end
Does this class define validates as a method which takes in a :name symbol and a hash presence:true? The same thing applies to line 3.
Or it is something entirely else? All attempts to run it resulted in:
uninitialized constant ApplicationRecord.
I looked at the source(maybe?) but am still not clear.
This is a special DSL introduced by ApplicationRecord. What you are actually doing is calling those methods inside class during declaration. It adds those validations to your class, so whenever you try to save a record it will fail if you don't have email or name
Try this
user = User.new
user.save
user.valid? # false
And try to do the same without validates.
If it will make things more clear for you, you can try write this class like this
class User < ApplicationRecord
validates(:name, presence: true)
validates(:email, presence: true)
end
validates is implemented as a class method in ActiveModel::Validations.
The ActiveModel::Validations module is included in ApplicationRecord, therefore you are able to call that method when your User class is loaded.
validates accepted an array and treats the last element of that array as an options hash (if the last element is an hash).
validates is a pre-defined helper that Active Record offers to use in Rails to make the validation work easier, this way you can with some single lines of code manage several validations of several attributes.
As it's a helper within Rails it's also a method defined in the ActiveModel module, in the core of the framework, see: active_model/validations.rb
The most common is the presence attribute which you're facing the trouble with, that specifies that the attribute used isn't empty, doing it in the Ruby way through the blank? method to check if the value passed isn't blank nor nil.

Active Model Serializers error on creating url attributes with v0.9.3

I'm trying to do something super simple in AMS where I generate the url attribute for an object like below:
class DeckSerializer < ActiveModel::Serializer
attributes :id, :title, :description, :url
has_one :user
has_many :cards
def url
deck_url(object)
end
end
However, I get the following error:
ArgumentError: Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true
I'm currently using Rails 4.2.0 and AMS 0.9.3
Anyone know what's going on?
This is a DIY way to solve this problem. Create a field on the model to store the url. then after the object has been saved, you update it with a manually generated url like this.
here is how i solved it.
if #questionsolution.save
generatedurl = 'http://localhost:3000/questionsolutions/' + #questionsolution.id.to_s
#questionsolution.update(solutionurl: generatedurl)
end
then you can get the url from the resource direclty without depending on active model serializers to do it for you.
Turns out it's a known bug mentioned here:
https://github.com/rails-api/active_model_serializers/issues/573
I switched the AMS version to 0.8.3 and everything worked. While not a complete solution, it works for the time being. Interested in hearing thoughts from others
You have to put Rails.application.routes.default_url_options[:host] = 'localhost:3000' in config/environments/development.rb

How can I make an ActiveModel::Serializer attribute optional at runtime?

I am trying to allow an API request to specify what fields to return on an object. I can retrieve the object with only the fields specified, but when it is serialized, it throws an error:
ActiveModel::MissingAttributeError (missing attribute: x)
How can I achieve this functionality with ActiveModel::Serializer and is it possible?
I've found this question while searching for a good alternative to remove optional fields from the json response.
The gem active_model_serializers does have a solution for this. You just need to pass a conditional to the attribute method in the serializer declaration.
class MySelectiveSerializer < ActiveModel::Serializer
attributes :id, :anything
attribute :something, if: -> { object.something.present? }
end
Perhaps 3 years ago a solution like this didn't exist, but it is available now. :)
Cheers.
This happens because the Serializer.attributes method call each field using the ActiveModel.read_attribute method. This method will apply some validations, like validates_presence_of at the model's definition, that will raise the exception. To avoid it I give three bad solutions and after a better and simple one:
Change the model definition, but you will miss your validation.
Overwrite the method ActiveModel.read_attribute to handle this behavior, you will get new challenges.
Overwrite the Serializer.attributes and instead of call super, call object.attributes.
But the best option will be create a new serialize class, to avoid besides effects, with the only fields that you want. Then specify this at the controller class:
render json: People.all.reduced, each_serializer: SimplePersonSerializer
Edit 1
The right answer should be the one from MaurĂ­cio Linhares.
render json: result.to_json( only: array_of_fields )
You can remove attributes from serializer, but they should exist.
class SomeSerializer < ActiveModel::Serializer
attributes :something
def attributes
super.except(:something) if something
end
end
You can customize attributes by implementing filter method in your serializer. Note, that I describe latest stable (for the time of the writing this post) 0.9.x branch.
class PostSerializer < ActiveModel::Serializer
attributes :id, :title, :body, :author
def filter(keys)
if scope.admin?
keys
else
keys - [:author]
end
end
end

How do I force a rails ActiveRecord string column to only accept strings?

I have a Post class with a title:string field
post = Post.create!
#=> Post created
post.title = "foo"
post.save!
# success
post.title = 1
post.save!
# succeeds, but I want it to throw a type mismatch exception
How do I get the last save! to throw a type mismatch?
This really runs against the grain of what Ruby is all about. If it's a string column, the database layer will take care of rendering that value as a string and saving it.
The concept of a type is different in Ruby than in other languages. Generally if an object supports to_s then it's considered to be string-enough to use, the basic tenet of Duck Typing.
If you pass in something that can't be rendered as a string you will get an exception. For example:
post.title = Post.find(5)
If you're looking to ensure your fields are not numerical, you should add a validation that enforces a particular format.
A new gem has been created to help validate types in rails and an explanatory blog post exists to answer more of the "why" it was created in the first place.
With this library your code would simple be:
class Post < ActiveRecord::Base
validates_type :title, :string
end
This will throw an exception when an integer is assigned to title instead of quietly casting the title to a string and saving it.
Ruby is a duck-typed language and doesn't have type mismatches. If an object responds to to_s, Rails will call to_s on it and put it into a string field.
It sounds like what you really want is to validate the format of the value you're putting into the field (is the string "2" a valid value?), which is what validates_format_of is for. For example, if you want to ensure that the value has at least one non-digit character, you could do this:
class Post < ActiveRecord::Base
validates_format_of :title, with: /\D/
end
Of course, you very well may want to customize that regex a bit more.
If you really can't stand duck-typing and want to make sure nothing but a true String goes into that column, you can override title=:
class Post < ActiveRecord::Base
def title=(value)
throw "`title' must be a String" unless value.is_a?(String)
super
end
end
Note that this won't be invoked when you do e.g. post[:title] = ... or post.write_attribute(:title, ...) but it should cover 95% of situations.
To answer the exact question, if you want to make sure that only objects of class String are saved to the field, you should add the following custom validation.
validate :must_be_string
def must_be_string
errors.add(:base, 'The field must be String') unless title.class == String
end
You can define a custom validator.
validates :title, type: String
and then:
class TypeValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors.add attribute, (options[:message] || "is not of class #{options[:with]}") unless
value.class == options[:with]
end
end

Rails Postgresql array of hstore

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.

Resources