How do you handle serialized edit fields in an Active Admin resource?
Here is a summary of how I handled this situation. I added an accessor to the model which can turn the Array into a string joined by a linefeed and split it back to an Array.
# app/models/domain.rb
class Domain < ActiveRecord::Base
serialize :names, Array
attr_accessor :names_raw
def names_raw
self.names.join("\n") unless self.names.nil?
end
def names_raw=(values)
self.names = []
self.names=values.split("\n")
end
end
then, in my admin resource for domain, instead of using the :names
field, I used the :names_raw
field. setting this value would save the names Array with the new values.# app/admin/domains.rb
form do |f|
f.inputs "Domain" do
f.input :names_raw, :as => :text
end
f.buttons
end
How to edit a serialized hash column in form textarea
A late answer to my own question:
class ConfigSerializer
def self.load(i)
if i.blank?
{}
else
YAML.load(i)
end
end
def self.dump(i)
i = {} if i.blank?
if i.is_a?(String) # Allow assigning an YAML string as input
i
else
YAML.dump(i)
end
end
end
and in the modelserialize :names, ConfigSerializer
This way I can assign a YAML string and it will be stored into the database as is. Only when it is loaded from database it is converted to hash object.In view I set textarea to have the raw YAML string, so user can edit it.
Activeadmin Resource Names
As mentioned in this issue : https://github.com/gregbell/active_admin/issues/434
Your initializer should look like this:
config/initializers/i18n.rb
I18n.load_path += Dir[Rails.root.join('config', 'locales', '*.{rb,yml}').to_s]
I18n.locale = :es
I18n.default_locale = :es
I18n.reload!
Active Admin's support for i18n is not very complete nor stable at present (v0.3.4) but it should get better in the next few releases. activeadmin and dynamic store accessors fails on new resource
SOLUTION:
I traced what AA was doing to figure out the minimum number of commands needed. It was necessary to add code to build_new_resource to ensure that any new resource AA built had the correct :category field, and once doing so, make the call to dynamically add the store_accessor keys to the newly built instance.
Now users can create their own original schemas and records that use them, without any further programming! I hope others find this useful, I certainly will.
There are a couple ugly solutions here, one is that adding the parameters to the active admin new route call is not expected by AA, but it still works. I guess this parameter could be passed in some other way, but quick and dirty does the job. The other is that I had to have the form generate a session variable to store what kind of schema was used, in order for the post-form-submission build to know, since pressing the "Create Move" button clears the params from the url.
The operations are as follows: for a model called Move with field :data that should be dynamically serialized into fields according to the json schema tables, both admin/moves/new?category="cleave"
and admin/moves/#/edit
find the "cleave" schema from the schema table, and correctly create and populate a form with the serialized parameters. And, direct writes to the db
m=Move.new(category: "cleave") ==> true
m.update(name: "t2", quality: "fine") ==> true
work as expected. The schema table is defined as: require "json-schema"
class SampleActionSchema < ApplicationRecord
validates :category, uniqueness: { case_sensitive: false }, allow_nil: false, allow_blank: true
validate :schema_is_json_schema
def self.schema_keys(categ)
sas=SampleActionSchema.find_by(category: categ)
schema_keys= sas.nil? ? [] : sas[:schema]["properties"].keys.map{|k| k.to_sym}
end
private
def schema_is_json_schema
metaschema = JSON::Validator.validator_for_name("draft4").metaschema
unless JSON::Validator.validate(metaschema, schema)
errors.add :schema, 'not a compliant json schema'
end
end
end
The Move table that employs this schema is:class Move < ApplicationRecord
after_initialize :add_field_accessors
def add_field_accessors
if category!=""
keys=SampleActionSchema.schema_keys(category)
keys.each {|k| singleton_class.class_eval{store_accessor :data, k}}
end
end
end
Finally, the working controller: ActiveAdmin.register Move do
permit_params do
#choice 1 is for new records, choice 2 is for editing existing
categ = @_params[:category] || (@_params[:move][:category] if @_params[:move]) || ""
keys=SampleActionSchema.schema_keys(categ)
prms = [:name, :data] + keys
end
form do |f|
new=f.object.new_record?
f.object.category=params[:category] if new
if new
session[:current_category]=params[:category]
f.object.add_field_accessors
else
session[:current_category] = ""
end
keys=SampleActionSchema.schema_keys(f.object.category)
f.inputs do
f.input :name
f.input :category
keys.each {|k| f.input k}
end
f.actions
end
controller do
def build_new_resource
r=super
r.assign_attributes(category: session[:current_category])
r.add_field_accessors
r
end
end
end
Filter on multi-select on Active Admin (Rails 3.2/postgreSQL/active admin 1.0)
You can create a multiple select in the same way you do in your form, with multiple: true
:
filter :deal_goal, as: :select, collection: DEAL_GOALS, multiple: true
UPDATE
Querying PostgreSQL arrays from Active Admin / Ransack can be done, it just takes a bit more work. First off, you should add the postgres_ext gem to your Gemfile. It gives you a nice set of query methods that you can use like this:
User.where.contains roles: ['foo']
# or:
User.where User.arel_table[:roles].contains ['foo']
The next step is to tell Ransack that these query methods (or "predicates") exist. Put this in an initializer:# https://github.com/activerecord-hackery/ransack/issues/321
Ransack.configure do |config|
{ array_contained_within: :contained_within,
array_contained_within_or_equals: :contained_within_or_equals,
array_contains: :contains,
array_contains_or_equals: :contains_or_equals,
array_overlap: :overlap
}.each do |rp, ap|
config.add_predicate rp, arel_predicate: ap, wants_array: true
end
end
You can confirm that Ransack is picking up on the added methods if this:User.search(roles_contains: [3,4]).result.to_sql
Generates this SQL:SELECT "users".* FROM "users" WHERE ("users"."roles" @> '{"3","4"}')
Now to get these new methods working with Active Admin.ActiveAdmin.register User do
filter :roles_array_contains, as: :select, multiple: true, collection: ['foo', 'bar']
end
Of course, be sure to change User
and roles
to the model and attribute that fits your situation.One final note. I noticed you're manually setting up serialize :attr, Array
in your model. For your array to be a PostgreSQL array you have to specify array: true
in the database migration. Assuming you've already done that, then this serialize
call is unnecessary. In fact, it seems to break this approach for some reason, so be sure to remove it.
Related Topics
Warning While Installing The Rails Plugin
I Am Getting This Gem Install Error for Kgio Gem When I Do a Bundle Install
How to Get All Message History from Hipchat for a Room via The API
Rails Redirecting Invalid Route to Root
After_Save Callback to Set The Updated_By Column to The Current_User
Encoding Problems in Rails on Ruby 1.9.1
Using Google Search Rest API in Ruby
Camelcase Instead of Snake_Case in Rails Db
Using Facebook Connect with Authlogic
Why Am I Getting "Unable to Autoload Constant" with Rails and Grape
Automatically Adding Proxy to All Http Connections in Ruby
Why Use Gemspec + Gemfile When Checking for Dependencies
Sinatra on Nginx Configuration - What's Wrong
Adding Bootstrap Icon to Button in Ruby on Rails
Fastest Way to Skip Lines While Parsing Files in Ruby