Rails rabl set calculated field - ruby-on-rails

I need to render some json data. I use rabl for this purposes....
I have such code in index.rabl:
collection #banks, :root => "bank", :object_root => false
attributes :id, :central_office_address, :location_id, :name, :year_of_foundation
it's generates me json....
But now i need to put there also some calculated field for each entry of object.
For example new field (is not in model): :exch_count and do for it something like(pseudo): :exch_count #banks[i].exchangers.count * 3
But how can i do this in ruby on rails + rabls?

You can pass a block to node that uses the bank as a parameter:
node(:exch_count) {|bank| bank.exchangers.count * 3 }

Related

collection_radio_buttons from an array

How can I select a value from a pre-defined collection in a model, using collection_radio_buttons?
I tried
= f.collection_radio_buttons :lang_lvl, Language.language_levels, :language_level, :language_level #does not work
= f.select :lang_lvl, Language.language_levels, include_blank: true #works
The data source is defined in a model Language.rb:
class Languages < ActiveRecord::Base
LANGUAGE_LEVELS = [:"A1/Elem", :"A2/PreInt", :"B1/Int", :"B2/UpperInt", :"C1/Adv", :"C2/Prof", :"A1/Beg"]
def self.language_levels
LANGUAGE_LEVELS.map { |language_level| [language_level, language_level] }
end
end
The 3rd and 4th arguments of collection_radio_buttons specify the method used to extract value and text respectively. The function will try to call [language_level, language_level].language_level to convert the data because you're passing an array of arrays as a collection and :language_level to be called for each item in that sub-array.
If you don't want to change your model, you could try to change the code to this:
= f.collection_radio_buttons :lang_lvl, Language.language_levels, :first, :last

JsonB and attr_json gem: Get the defined attributes programmatically

I'm using a jsonb field in my Rails application and have installed the gem attr_json. Is there a way to receive the defined json_attributes programmatically? With a "normal" rails attribute, I would just do #instance.attribute_names. But with attr_json is there any way how to have the json_attributes returned?
class Vehicle < Item
include AttrJson::Record
attr_json :licence_plate, :string, container_attribute: "custom_attributes_indexed"
attr_json :brand, :string, container_attribute: "custom_attributes_indexed"
attr_json :serial_number, :string, container_attribute: "custom_attributes_indexed"
attr_json :inventory_number, :string, container_attribute: "custom_attributes_indexed"
end
For this code I would like to do something like #vehicle.json_attribute_names and have the following returned
["licence_plate", "brand", "serial_number", "inventory_number"]
You can retrieve the defined json_attributes via: Vehicle.attr_json_registry.attribute_names
But even simpler to retrieve the attributes via the rails method attribute names is to add rails_attribute: true to your attr_json definitions. This will return all the "normal" attributes and the JSON attributes as an array.
[...]
attr_json :licence_plate, :string, container_attribute: "custom_attributes_indexed", rails_attribute: true
[...]
I have a Rails app that uses a JSONB column in Postgres and without any other gems I can call the following to get the keys...
Given a model Inspections with a JSONB column called "results" I can do:
#some_inspection = Inspection.first
#some_inspection.results
#=> {"assigned_to" => "John Smith", "inspection_date" => "2020_01_02", "passed" => "true"}
#some_inspection.results.keys
#=> ["assigned_to", "inspection_date", "passed"]
I was under the impression that the great thing about having a JSONB column is that it seamlessly translates for me. I don't have any attr_json nor any other specialized code in my model. Like so much Ruby and Rails "it just works". I pass it a hash and it stores that as JSON and when I ask for it, I get a hash. I can then do any hash methods like .values or .keys on it. If I need JSON back I can do #some_inspection.results.to_json.
If you pass it actual JSON then it will just store the JSON text as a string in the column. You can then get the JSON back just by calling the column name like:
#some_inspection.results
#=> "{\"assigned_to\":\"John Smith\",\"inspection_date\":\"2020_01_02\",\"passed\":\"true\"}"
But if you want to do something like .keys you have to parse it since it is a string:
JSON.parse(#some_inspection.results).keys
#=> ["assigned_to", "inspection_date", "passed"]

Custom wrap JSON to Virtus Model?

I have a JSON object that looks like the following:
{
"id":"10103",
"key":"PROD",
"name":"Product",
"projectCategory":{
"id":"10000",
"name":"design",
"description":""
}
}
and a Virtus model that looks like the following:
class Project
include Virtus.model
attribute :id, Integer
attribute :key, String
attribute :name, String
attribute :category, String #should be the value of json["projectCategory"]["name"]
end
Everything lines up fine other than trying to map Project.category to json["projectCategory"]["name"].
So in total the end Virtus object I'm look for should look like:
"id" => "10103",
"key" => "PROD",
"name" => "Product",
"category" => "design"
Right now I'm creating a model instance with Project.new(JSON.parse(response)) or basically a hash of the json response. How can I custom map Virtus some attributes to my json response?
So I ended up figuring out you can override the self.new method allowing you to get to nested values in the hash you pass your Virtus model.
I ended up doing the following which worked fine:
class Project
include Virtus.model
attribute :id, Integer
attribute :name, String
attribute :key, String
attribute :category, String
def self.new(attributes)
new_attributes = attributes.dup
# Map nested obj "projectCategory.name" to Project.category
if attributes.key?("projectCategory") and attributes["projectCategory"].key?("name")
new_attributes[:'category'] = attributes["projectCategory"]["name"]
end
super(new_attributes)
end
end

How do you set up dynamic enumerables on Rails 5?

I'm trying to create a select input that takes specific User models and displays them as a string while saving them as an integer. Typically I would set the enumerable up the same way as below but as a static hash.
While attempting to create a new Product, I keep receiving the following error: "undefined local variable or method `user_business_hash'"
I've tried moving the 'user_business_hash' method to application/products controller, application/products helper with no luck.
Model
enum user_id: user_business_hash
validates :user_id, inclusion: user_ids.keys
def user_business_hash
# output: {User.business_name => User.id }
# ex: {"Business Name A"=>2, "Business Name B"=>1, "Business Name C"=>5}
array = User.where(account_type: 'Business').map{|x| [x.business_name, x.id] }
hash = array.inject({}) do |memo, (key, value)|
memo[key] = value
memo
end
return hash
end
Form
<%= form.select :user_id, Product.user_ids.keys, prompt: 'Select', id: :product_user_id %>
I think that what you actually want is:
on your controller
#options = User.where(account_type: 'Business')
on your view
options_from_collection_for_select(#options, "id", "business_name")

Rails inclusion validations

I'm struggling to get a inclusion validation to work on a model so maybe some one could tell me what I'm missing here.
This is the model I have:
class Order < ActiveRecord::Base
ORDER_TYPES = %w{ Verkooporder Retourorder }
ORDER_TYPES.each_with_index do |meth, index|
define_method("#{meth}?") { type == index }
end
validates_inclusion_of :order_type, :in => %w{ Verkooporder Retourorder }
...
I also created a form that creates a dropdownbox using the constant array like this:
(I'm
= f.input :order_type, as: :select, collection: Order::ORDER_TYPES, label: 'Order type', include_blank: false
And I save it to my model like this:
#order.order_type = params[:order][:order_type]
So when I save my order model it always fails on validating the order_type.
Is there anybody who can point me out what I'm doing wrong?
PS: The order_type is an integer value field in my model.
The problem is that you are defining methods Verkooporder? and Retourorder?, but they're not being called from your validation because the :in is interpreting %w{ Verkooporder Retourorder} as an array of strings, i.e. [ "Verkooporder", "Retourorder"].
What you actually want to validate is that order_type is a number between 0 and the size of the ORDER_TYPES array, i.e. a string with a value between 0 and 1:
validates_inclusion_of :order_type, :in => %w{ 0 1 }
In this case you don't really need to define the boolean Verkooporder? and Retourorder? methods, unless you need them elsewhere.
UPDATE:
I realize now that your form will return order_type as a string in Order::ORDER_TYPES, which won't work with the validation above since the validation above is validating on integer-valued strings.
The way I've done this in the past is not to use an integer for order_type but a string. In that case, you can just validate with validates_inclusion_of :order_type, :in => ORDER_TYPES, and the select drop-down doesn't have to change. Is there any particular reason you're using an integer value field for order_type? Alternatively you could have the select return integer values for each order type.

Resources