I have problems with ActiveModelSerializer.
Model test.rb:
class Test < ActiveModel::Serializer
attr_accessor :id, :userName, :lista, :test2
def initialize params = {}
#lista = Array.new
#test2 = Array.new
params.each { |key, value| send "#{key}=", value }
end
end
Model test2.rb:
class Test2 < ActiveModel::Serializer
attr_accessor :id, :userName
def initialize params = {}
params.each { |key, value| send "#{key}=", value }
end
end
test_serializer.rb:
class TestSerializer < ActiveModel::Serializer
attributes :id, :userName, :lista
attributes :test2
public
def test2
Test2Serializer.new(object.test2).as_json
end
end
test2_serializer.rb:
class Test2Serializer < ActiveModel::Serializer
attributes :id, :userName
end
I get an error:
NoMethodError (undefined methodread_attribute_for_serialization’ for #Array:0x00007f1bb52da460):
After debuggin the code I found out that the Exception is from this line:
object.read_attribute_for_serialization(attr)
In variable object is an array. I think it should be object. So I tried to change
Test2Serializer.new(object.test2).as_json
oto
json: object.test2, each_serializer: Test2Serializer
or:
object.test2, each_serializer: Test2Serializer
That didn't work. Can anyone help me?
Related
module Api
module V1
class CalculatorsController < ApplicationController
def compute
#fields = Calculator.find_by(slug: params['id']).fields.result
render json: #fields, root: 'result', adapter: :json
#fields.each do |field|
cal_res = CalculatorResolver.call(field.calculator)
res = cal_res.each_with_object({}) do |(key, value), result|
result[key[:value]] = value.each_with_object({}) do |v, sel|
sel[v[:selector].downcase] = v[:value]
end
end
res.each do |key, value|
CalculationResolver.result(value, key)
end
end
end
end
end
end
class Field < ApplicationRecord
belongs_to :calculator
before_create :set_selector
enum kind: { form: 0, parameter: 1, result: 2 }
enum unit: { month: 0, date: 1, times: 2 }
validates :type, :label, :kind, presence: true
validates :unit, inclusion: { in: units.keys }
private
def set_selector
return if selector.present?
selected_rows_count = Field.where(kind: kind).count
if selected_rows_count.positive?
previous_number = Field.where(kind: kind).last.selector[1]
self.selector = kind[0].upcase + previous_number.next.to_s
else
self.selector = "#{kind[0].upcase}1"
end
end
def result
0
end
end
class FieldSerializer < ActiveModel::Serializer
attributes :name, :result
def name
object.name.parameterize.underscore
end
end
I have class Field,API/V1/compute and class Field Serializer
In API/V1/compute result is CalculationResolver.result(value, key).It's numeric.
I need to pass result in class Field method result
How can I do that?
I have ZoneSerializer
class Api::V1::ZoneSerializer < ActiveModel::Serializer
attributes :id, :name, :address, :description, :groupname, :key, :speed_limit
has_many :radius_user_groups
end
And I set transform_key
ActiveModel::Serializer.config.key_transform = :camel_lower
When I get response from server I get radiusUserGroups which is ok. But frontend sends also radiusUserGroups for includes, but there seems to be no autotransform feature for backend. Should I do this manually?
Add in your Application controller This method convert all params to snake_case :
before_action :deep_snake_case_params!
def deep_snake_case_params!(val = params)
case val
when Array
val.map {|v| deep_snake_case_params! v }
when Hash
val.keys.each do |k, v = val[k]|
val.delete k
val[k.underscore] = deep_snake_case_params!(v)
end
val
else
val
end
end
I have created a simple before_validation:
before_validation :strip_tabs
def strip_tabs
end
In my class I want to loop through all my attributes and remove tabs from each value. Most posts I found on SO are people who want to set 1 attribute. But I want to edit all my values.
Question:
How can I loop all self attributes of a model and edit them.
Friend suggested this, but content_column_names does not exist:
self.content_column_names.each {|n| self[n] = self[n].squish}
UPDATE 1: More code:
class PersonalInfo
include ActiveModel::Validations
include ActiveModel::Validations::Callbacks
extend ActiveModel::Translation
extend ActiveModel::Callbacks
include Sappable
require 'ext/string'
attr_accessor \
:first_name, :middle_name, :last_name,:birthdate,:sex,
:telephone,:street,:house_number,:city,:postal_code,:country,
:e_mail, :nationality, :salutation, :com_lang
validates :e_mail, :email => {:strict_mode => true}
validate :validate_telephone_number
validate :age_is_min_17?
before_validation :strip_tabs
def strip_tabs
binding.remote_pry
end
def age_is_min_17?
birthdate_visible = PersonalField.not_hidden.find_by_name "BIRTHDATE"
if birthdate_visible && birthdate && birthdate > (Date.current - 17.years)
#errors.add(:birthdate, I18n.t("apply.errors.birthdate"))
end
end
def validate_telephone_number
telephone_visible = PersonalField.not_hidden.find_by_name "TELEPHONE"
telephone_current = telephone.dup
if telephone_visible && telephone_current && !telephone_current.empty?
if telephone_current[0] == '+' || telephone_current[0] == '0'
telephone_current[0] = ''
#errors.add(:telephone, I18n.t("apply.errors.telephone")) if !telephone_current.is_number?
else
#errors.add(:telephone, I18n.t("apply.errors.telephone"))
end
end
end
def initialize(hash)
simple_attributes = [:first_name, :middle_name, :last_name,:birthdate,:sex,
:telephone,:street,:house_number,:city,:postal_code,:country,
:e_mail, :nationality, :salutation, :com_lang]
simple_attributes.each do |attr|
set_attr_from_json(attr, hash)
end
set_attr_from_json(:birthdate, hash) {|date| Date.parse(date) rescue nil}
end
end
Update 2: Rails Version:
I'm using Rails '3.2.17'
You can do as following:
before_validation :strip_tabs
def strip_tabs
self.attributes.map do |column, value|
self[column] = value.squish.presence
end
end
But I think that .squish will not work on created_at, updated_at, id, ... Because they are not String!
def strip_tabs
self.attributes.map do |column, value|
self[column] = value.kind_of?(String) ? value.squish.presence : value
end
end
Since your class is not a Rails model (ActiveRecord::Base), you can do as following:
def strip_tabs
self.instance_variables.map do |attr|
value = self.instance_variable_get(attr)
value = value.squish if value.kind_of?(String)
self.instance_variable_set(attr, value)
end
end
This should work
def strip_tabs
self.attributes.each do |attr_name, attr_value|
modified_value = ... # calculate your modified value here
self.write_attribute attr_name, modified_value
end
end
Because it's not an ActiveRecord model you won't have attributes or column_names, but you already have an array of your attribute names in your initialize function. I would suggest making that into a constant so you can access it throughout the model:
class PersonalInfo
include ActiveModel::Validations
include ActiveModel::Validations::Callbacks
extend ActiveModel::Translation
extend ActiveModel::Callbacks
include Sappable
require 'ext/string'
SIMPLE_ATTRIBUTES = [:first_name, :middle_name, :last_name,:birthdate,:sex,
:telephone,:street,:house_number,:city,:postal_code,:country,
:e_mail, :nationality, :salutation, :com_lang]
attr_accessor *SIMPLE_ATTRIBUTES
before_validation :strip_tabs
def strip_tabs
SIMPLE_ATTRIBUTES.each{ |attr| self[attr] = self[attr].squish }
end
...
def initialize(hash)
SIMPLE_ATTRIBUTES.each do |attr|
set_attr_from_json(attr, hash)
end
set_attr_from_json(:birthdate, hash) {|date| Date.parse(date) rescue nil}
end
end
In my rails app i defined a specific JSON-Format in my model:
def as_json(options={})
{
:id => self.id,
:name => self.name + ", " + self.forname
}
end
And in the controller i simply call:
format.json { render json: #patients}
So now im trying to define another JSON-Format for a different action but i dont know how?
How do i have to define another as_json or how can i pass variables to as_json? Thanks
A very ugly method but you can refactor it for better readability:
def as_json(options={})
if options.empty?
{ :id => self.id, :name => self.name + ", " + self.forname }
else
if options[:include_contact_name].present?
return { id: self.id, contact_name: self.contact.name }
end
end
end
Okay, I should give you a better piece of code, here it is:
def as_json(options = {})
if options.empty?
self.default_json
else
json = self.default_json
json.merge!({ contact: { name: contact.name } }) if options[:include_contact].present?
json.merge!({ admin: self.is_admin? }) if options[:display_if_admin].present?
json.merge!({ name: self.name, forname: self.forname }) if options[:split_name].present?
# etc etc etc.
return json
end
end
def default_json
{ :id => self.id, :name => "#{self.name}, #{self.forname}" }
end
Usage:
format.json { render json: #patients.as_json(include_contact: true) }
By defining hash structure by 'as_json' method, in respective model class i.e User model in (Example 1), it becomes the default hash stucture for active record(i.e., user) in json format. It cannot be overridden by any inline definitions as defined in Example: 2
Example 1:
class User < ActiveRecord::Base
.....
def as_json(options={})
super(only: [:id, :name, :email])
end
end
Example: 2
class UserController < ApplicationController
....
def create
user = User.new(params[:user])
user.save
render json: user.as_json( only: [:id, :name] )
end
end
Therefore, in this example when create action is executed 'user' is returned in ("only: [:id, :name, :email]") format not as ("only: [:id, :name]")
So, options = {} are passed to as_json method to specifiy different format for different methods.
Best Practice, is to define hash structure as constant and call it everwhere it is needed
For Example
Ex: models/user.rb
Here, constant is defined in model class
class User < ActiveRecord::Base
...
...
DEFAULT_USER_FORMAT = { only: [:id, :name, :email] }
CUSTOM_USER_FORMAT = { only: [:id, :name] }
end
Ex: controllers/user.rb
class UserController < ApplicationController
...
def create
...
render json: user.as_json(User::DEFAULT_USER_FORMAT)
end
def edit
...
render json: user.as_json(User::CUSTOM_USER_FORMAT)
end
end
Cool!
I am using Ruby on Rails 3 and I would like to inizialize an ActiveRecord Tableless Model.
In my model I have:
class Account < ActiveRecord::Base
# The following ActiveRecord Tableless Model statement is from http://codetunes.com/2008/07/20/tableless-models-in-rails/
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
attr_reader :id,
:firstname,
:lastname,
def initialize(attributes = {})
#id = attributes[:id]
#firstname = attributes[:firstname]
#lastname = attributes[:lastname]
end
end
If in a controller, for example in the application_controller.rb file, I do:
#new_account = Account.new({:id => "1", :firstname => "Test name", :lastname => "Test lastname"})
a debug\inspect output of the #new_account variable is
"#<Account >"
Why? How I should inizialize properly that ActiveRecord Tableless Model and make it to work?
According to that blog post it would have to look like this:
class Account < ActiveRecord::Base
class_inheritable_accessor :columns
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
column :id, :integer
column :firstname, :string
column :lastname, :string
end
And then:
#new_account = Account.new({:id => "1", :firstname => "Test name", :lastname => "Test lastname"})
Did you already try it like that?
I my view, you don't need to extend ActiveRecord::Base class.
You can write your own model class something like this
# models/letter.rb
class Letter
attr_reader :char
def self.all
('A'..'Z').map { |c| new(c) }
end
def self.find(param)
all.detect { |l| l.to_param == param } || raise(ActiveRecord::RecordNotFound)
end
def initialize(char)
#char = char
end
def to_param
#char.downcase
end
def products
Product.find(:all, :conditions => ["name LIKE ?", #char + '%'], :order => "name")
end
end
# letters_controller.rb
def index
#letters = Letter.all
end
def show
#letter = Letter.find(params[:id])
end
I hope it will help you.
Reference: http://railscasts.com/episodes/121-non-active-record-model