Add new field to index.json in Rails 4 - ruby-on-rails

I would like to add a modified variable to my index.json.
views/posts/index.json.builder
json.array!(#posts) do |post|
post[:image_th] = post[:image].reverse.split('.', 2).join(".s").reverse
#this is the new line above
json.extract! post, :id, :title, :datum, :body, :image
#json.url post_url(post, format: :json)
end
So itt adds an 's' before ".jpg" in the string.
This gives error:
can't write unknown attribute `image_th'
How to add a field to index.json without creating a migration and accessing it from the database?

Try:
class Post < ActiveRecord::Base
# ...
def image_th
image.reverse.split('.', 2).join(".s").reverse
end
end
And then:
json.extract! post, :id, :title, :datum, :body, :image_th
EDIT:
Also the method would be probably slightly cleaner with:
def image_th
image.sub(/\.[^\.]+$/, 's\0')
end

If you don't need to persist it, you can use an attr_accessor :image_th in your Post model:
class Post < ActiveRecord::base
attr_accessor :image_th
end
Anyway that claims more to be a method than a field in your model

Related

Serializing a custom attribute

I am using the Active Model Serializer gem for my application. Now I have this situation where a user can have an avatar which is just the ID of a medium.
I have the avatar info saved into Redis. So currently what I do to show the avatar at all in the serialized JSON, is this:
class UserSerializer < ActiveModel::Serializer
include Avatar
attributes :id,
:name,
:email,
:role,
:avatar
def avatar
Medium.find(self.current_avatar)[0]
end
#has_one :avatar, serializer: AvatarSerializer
has_many :media, :comments
url :user
end
I query Redis to know what medium to look for in the database, and then use the result in the :avatar key.
Down in the code there is also a line commented out, that was the only way I found on the https://github.com/rails-api/active_model_serializers/ page about using a custom serializer for something inside of serializer.
So to get to my problem. Right now the :avatar comes just like it is in the database but I want it to be serialized before I serve it as JSON.
How can I do that in this case?
You need to serialize Avatar Class:
class Avatar
def active_model_serializer
AvatarSerializer
end
end
Then you just use this way:
class UserSerializer < ActiveModel::Serializer
include Avatar
attributes :id,
:name,
:email,
:role,
:avatar
def avatar
# Strange you query another object
avatar = Medium.find(object.current_avatar).first
avatar.active_model_serializer.new(avatar, {}).to_json
end
has_many :media, :comments
url :user
end
According to the docs if you want a custom serializer you can just add:
render json: avatar, serializer: AvatarSerializer
or whatever your serializer name could be, here are the docs:
https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/general/serializers.md#scope

Hashing value before output

I am looking to MD5 hash a email address prior to outputting from a Rails Model. Currently my modal looks like so:
class Comment < ActiveRecord::Base
belongs_to :post
attr_accessible :body, :name, :reply, :email
validates_presence_of :body, :name
def gravator
require 'digest/md5'
email_address = self.email.downcase
# create the md5 hash
hash = Digest::MD5.hexdigest(email_address)
# compile URL which can be used in <img src="RIGHT_HERE"...
self.email = "http://www.gravatar.com/avatar/#{hash}"
end
end
I'm wondering what the best way to convert the email field to the outputted URL from the gravator method.
Thanks for any help!
Rather than hashing it every time, create a new field in the database that contains the hashed value.
Create a gravator field and assign it on before_save. This will speed things up and won't call the database each time it is displayed.
class Comment < ActiveRecord::Base
belongs_to :post
attr_accessible :body, :name, :reply, :email
validates_presence_of :body, :name
before_save :set_gravator
def set_gravator
require 'digest/md5'
email_address = self.email.downcase
# create the md5 hash
hash = Digest::MD5.hexdigest(email_address)
# compile URL which can be used in <img src="RIGHT_HERE"...
self.gravator = "http://www.gravatar.com/avatar/#{hash}"
end
end

How to override enumeration data in rail_admin gem

In my models, BookHeader has many Category
So, when edit or create new BookHeader, the form show like this
Enum fix?
I wanna change the "category #{id}" to category name by define a category_enum method but it still don't work. Please help!
Code for BookHeader model
class BookHeader < ActiveRecord::Base
attr_accessible :autho, :category_id, :description, :title, :book_type, :year,:publisher_id,:detail
has_many :books
belongs_to :category
belongs_to :publisher
TYPE = {:ebook=>"Ebook",:paper_book=> "PaperBook",:magazine=> "Magazine",:media=> "Media"}
DEFAULT_TAB = :paper_book
BOOKS_PER_PAGE = 1 # books to show in a pages (pagination)
extend FriendlyId
def book_type_enum #it worked here
TYPE.map{|key, val| [val]}
end
def category_enum #but dont' work here
["a","b"]
end
Code for edit form
edit do
field :title
field :description, :text do
ckeditor do true end
end
field :autho
field :book_type
field :category
end
See the Division attribute in this link
alias_attribute :name, :you_field_you_want_to_display
I think it's more flexible way, there is no need to rename something and everything will work properly
Yeah, I just found the answer, rename a column in your model to "name", it seem to be very magical, but it worked!

Rails 3 form error: "undefined method `quoted_table_name'"

I have a Rails 3 form (simple_form, really) that has a set of nested attributes:
<%= simple_form_for(#user, :url => the_path(#user)) do |f| %>
...
<%= f.simple_fields_for :credit_card do |c| %>
<%= c.input :number, :label => 'Credit card number' %>
...
<% end %>
...
<% end %>
The problem is that the :credit_card attributes belong to the class CreditCard, which is not a model since I'm not storing any of the credit card data in the database. I have that Class defined in /app/models/credit_card.rb like this (per this RailsCast):
class CreditCard
include ActiveModel::Validations
include ActiveModel::Conversion
include ActiveModel::Naming
attr_accessor :number, :expiration_month, :expiration_year, :cvv
validates_presence_of :number, :expiration_month, :expiration_year, :cvv
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end
In user.rb, I have this:
has_one :credit_card
accepts_nested_attributes_for :credit_card
When I access the page, I get this error:
undefined method `quoted_table_name' for CreditCard:Class
Googling that error didn't yield any suggestions. I'm able to create CreditCard objects from Rails Console, but for some reason the Rails form generator isn't seeing the class.
I already tried swapping out simple_form_for with form_for (and the related changes), so I don't think it's a simple_form problem.
This seems error in the association with user
This seems to be a problem with having a model without an associated table in the database. I am having the same situation where I don't want to store a nested model in application database table but rather fetch and save information to and from an external service.
class Order < ActiveRecord::Base
attr_accessor :identifier, :amount
has_one :credit_card_transaction
end
class CreditCardTransaction
attr_accessor :number, :expiration_date
belongs_to :order
end
I don't want to save credit card transaction object in local database. And getting the same problem as you are. Please keep posted for any updates.
It may be a case of the presence of another class that has the same name "CreditCard" which in this case the class is not an ActiveRecord and thus does not have quoted_table_name method.

Rails ActiveRecord: validate single attribute

Is there any way I can validate a single attribute in ActiveRecord?
Something like:
ac_object.valid?(attribute_name)
You can implement your own method in your model. Something like this
def valid_attribute?(attribute_name)
self.valid?
self.errors[attribute_name].blank?
end
Or add it to ActiveRecord::Base
Sometimes there are validations that are quite expensive (e.g. validations that need to perform database queries). In that case you need to avoid using valid? because it simply does a lot more than you need.
There is an alternative solution. You can use the validators_on method of ActiveModel::Validations.
validators_on(*attributes) public
List all validators that are being used to validate a specific
attribute.
according to which you can manually validate for the attributes you want
e.g. we only want to validate the title of Post:
class Post < ActiveRecord::Base
validates :body, caps_off: true
validates :body, no_swearing: true
validates :body, spell_check_ok: true
validates presence_of: :title
validates length_of: :title, minimum: 30
end
Where no_swearing and spell_check_ok are complex methods that are extremely expensive.
We can do the following:
def validate_title(a_title)
Post.validators_on(:title).each do |validator|
validator.validate_each(self, :title, a_title)
end
end
which will validate only the title attribute without invoking any other validations.
p = Post.new
p.validate_title("")
p.errors.messages
#=> {:title => ["title can not be empty"]
note
I am not completely confident that we are supposed to use validators_on safely so I would consider handling an exception in a sane way in validates_title.
I wound up building on #xlembouras's answer and added this method to my ApplicationRecord:
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
def valid_attributes?(*attributes)
attributes.each do |attribute|
self.class.validators_on(attribute).each do |validator|
validator.validate_each(self, attribute, send(attribute))
end
end
errors.none?
end
end
Then I can do stuff like this in a controller:
if #post.valid_attributes?(:title, :date)
render :post_preview
else
render :new
end
Building on #coreyward's answer, I also added a validate_attributes! method:
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
def valid_attributes?(*attributes)
attributes.each do |attribute|
self.class.validators_on(attribute).each do |validator|
validator.validate_each(self, attribute, send(attribute))
end
end
errors.none?
end
def validate_attributes!(*attributes)
valid_attributes?(*attributes) || raise(ActiveModel::ValidationError.new(self))
end
end

Resources