How to Handle Serialized Edit Fields in an Active Admin Resource

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 model

serialize :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



Leave a reply



Submit