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"]
Related
This question is asked many times on SO. The main problem is nothing got fits into my situation.
Case is, I am not able to store typed content as array in database column.
text_field whose code is:
= text_field_tag 'product[keywords][]', #product.keywords, class: 'tab-input
product_keywords'
In controller strong parameters are:
params.require(:product).permit(:id, :name, :keywords => [])
Jquery code that is not not removing value upon deletion when typed wrong value but it add commas after each element as I want to take commas seperated value in one column.
$(document).on 'keyup', '.product_keywords', ->
keyword = #value.replace(/(\w)[\s,]+(\w?)/g, '$1, $2')
if keyword != #value
#value = keyword
return
model code:
serialize :keywords, Array
migration code:
class AddKeywordsToProducts < ActiveRecord::Migration[5.1]
def change
add_column :products, :keywords, :text
end
end
So, if someone writes, abc and hit space a comma is added in the end. after three typed words it will look like:
abc, dbx, she
now I want to store it as array in column but its not storing properly.
it stores as:
["abc, dbx, she"]
Also please can anybody tell me the best cases to handle these cases?
Plus best practices to deal with such cases using ruby so I will learn it for future?
You probably want a custom serializer as shown here. So instead of:
serialize :keywords, Array
You might do somewhat like:
serialize :keywords, KeywordSerializer
And somewhere in helpers:
class KeywordSerializer
def self.dump(what)
what.join(", ")
end
def self.load(what)
what.split(/\s*,\s*/)
end
end
Passing array elements using single form tag is not possible to pass as a array and passing array as a string, you need to process it near white-listing your params,
permitted_params = params.require(:product).permit(:id, :name, :keywords => [])
permitted_params[:keywords] = permitted_params[:keywords][0].split(/\s*,\s*/)
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
I am building a Rails gem for which I might need to know the currently available column types. So say for Postgres, I am looking for something like: ActiveRecord::Base.available_column_types. I looked through the source with no success so far.
I can't find an ActiveRecord method to get what you want. But I can show you two ways you can achieve this:
With any path you need to create an initializer and Monkey Patch ActiveRecord. For example: /config/initializers/active_record_extensions.rb. Then, the options:
OPTION 1: get data types based on your models
class ActiveRecord::Base
def self.available_column_types
types = []
ActiveRecord::Base.subclasses.collect{ |type| type.name }.each do |model_name|
types += eval("#{model_name}.columns.map(&:type)")
end
types.uniq
end
end
Then you can do rails console on your terminal and write:
irb(main):001:0> User.available_column_types
=> [:integer, :string, :text, :datetime, :boolean, :date, :hstore]
irb(main):002:0> ActiveRecord::Base.available_column_types
=> [:integer, :string, :text, :datetime, :boolean, :date, :hstore]
irb(main):003:0>
OPTION 2: get all posible data types based on you db adapter
class ActiveRecord::Base
if defined?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) and
ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
types = ActiveRecord::Base.connection.execute("select * from pg_type;")
return types.inject([]) { |result, record| result << record["typname"] }
# Too much info on pg_type table, you can get whatever you need.
end
if defined?(ActiveRecord::ConnectionAdapters::MysqlAdapter) and
ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters::MysqlAdapter)
# I don't know, it's just an example. Yo can add all adapters you want
return
end
# maybe raise an Exception with NO ADAPTER! message
end
end
Once again, on your console, you can do ActiveRecord::Base.available_column_types to see the result.
Note: you need to adapt this in order to make it work with your gem.
I am using Ruby on Rails v3.2.2 and I would like to handle an array of symbols so to pass its values to the attr_accessible method as well as it should be made. That is, I have:
attr_array = [:one, :two, ...]
If I use:
attr_accessible attr_array
I get the following:
self.accessible_attributes.inspect
# => #<ActiveModel::MassAssignmentSecurity::WhiteList: {"[:one, :two, ..."]}>
However, I would like to get:
# => #<ActiveModel::MassAssignmentSecurity::WhiteList: {"one", "two", "..."}>
as well as it should be made.
How can I make that?
Just like this :
attr_accessible *array
I've got a model, Entity.
class Entity
include Mongoid::Document
field :x
field :y
field :z, type => Hash, :default => {} # new field
end
I added a new field to it, a hash. When I try to use it, I get an error. My code is:
e = Entity.first
if e.z["a"] # if there is a key of this in it?
e.z["a"] = e.z["a"] + 1
else
e.z["a"] = 1
end
But, this error with an undefined method get for hash. If I try to create an initializer for it, to set the values in an existing document, it errors with the same error. What am I doing wrong?
Initializer looks like:
e = Entity.first
e.write_attribute(:z, {})
Thanks
Sorted it.
It seems the answer is to set in Mongoid 1.9.5 the hash to:
field :hash_field, :type => Hash, :default => Hash.new
and it can access and initialize it. Not quite understanding why, but happy to have the answer !