8

Is there any gem/plugin for ruby on rails which gives the ability to define custom fields in a model at runtime with no need to change the model itself for every different field.

I'm looking for something like Redmine acts_as_customizable plugin which is packaged as a gem usable in the rails way, i.e.

gem 'gemname'
rails g something
rails db:migrate

class Model < ActiveRecord::Base
  acts_as_something
end

Here are the CustomField and the CustomValue classes used in Redmine.

Edit:

Since my question is not clear I add a brief use case which explains my need better:

I want users to be able to design their own forms, and collect data submitted on those forms. An important decision is the design of how these custom dynamic records are stored and accessed.

Taken from here, in this article approach the problem with different ideas, but they all have drawbacks. For this reason I'm asking if the issue has been approached in some gem with no need to rethink the whole problem.

Community
  • 1
  • 1
Fabio
  • 18,856
  • 9
  • 82
  • 114
  • @KandadaBoggu In my case is MySQL but usually AR plugins does not depends on particular RDBMS's. – Fabio Apr 19 '12 at 11:17
  • 3
    Postgres database supports a datatype called `hstore`. Watch this screencast: (http://hstoredemo.herokuapp.com/) – Harish Shetty Apr 19 '12 at 15:59
  • Is this multi-tenant or multi-instance application? In other words do you plan to have one db for all clients or each client will have its own db instance? – Greg Dan Apr 23 '12 at 15:01
  • @GregDan I took the use case from the linked article since it explains with a concrete case what I'm trying to do. In my case there will be just one database. – Fabio Apr 23 '12 at 15:16
  • @Fabio In case of many dbs, I would suggest to add new columns for custom fields. Rails doesn't reload AR classes when DB schema changes but this should be possible to fix. Solutions with serialized hashes smell like http://en.wikipedia.org/wiki/Inner-platform_effect – Greg Dan Apr 25 '12 at 09:36

4 Answers4

5

I'm not aware of a gem that does this, but serialize works quite well and it's a built-in. You get a NoSQL-ish document store backed by JSON/YAML.

If you allow user to create a custom form, you can pass nested arrays et cetera directly into the attribute. However, if you need to validate the structure, you're on your own.

Prathan Thananart
  • 4,007
  • 3
  • 19
  • 18
3

Since rails 3.2 you can use store method. Just include following in your model:

store :properties, accessors: [:property1, :property2, :property3...]

You only need to change your model once (to add properties field to db table). You can add more properties later without altering the schema.

The way this works is by serializing properties hash into YAML and saving it into database. It it suitable for most cases, but not if you'd like to use these values in db queries later.

Milovan Zogovic
  • 1,541
  • 2
  • 16
  • 24
  • AR store is not good for my needs, I want application end users to be able to define additional fields for a given model. I've updated the question with more details. – Fabio Apr 18 '12 at 09:56
  • 1
    Oh, didn't know you wanted *users* to be able to define custom attributes. I don't know of a gem, but I would use REDIS to store such data. It wont require more than a few lines of code to wire all up get the behavior you want. – Milovan Zogovic Apr 18 '12 at 12:26
3

I'm afraid it could be tricky and complicated to do it in ActiveRecoand (generally in standard relational database). Take a look at http://mongoid.org/docs/documents/dynamic.html - this mechanism is using nosql feature.

You can also may try the following trick:

1/ Serialize a hash with your custom fields in the database column, for example { :foo => 'bar', :fiz => 'biz' }

2/ After load a record from database do some metaprogramming and define corresponding methods on the record's singleton class, for instance (assume that custom fields are stored and serialized in custom_fields column):

after_initialize :define_custom_methods
# ..or other the most convinient callback

def define_custom_methods
  # this trick will open record's singleton class
  singleton_class = (class << self; self; end)
  # iterate through custom values and define dynamic methods
  custom_fields.each_with_key do |key, value|
    singleton_class.send(:define_method, key) do
      value
    end
  end
end
luacassus
  • 6,540
  • 2
  • 40
  • 58
  • 2
    I'm start thinking that a NoSQL db is the only solution for this kind of issues. I never worked with mongoid, does it support ActiveRecord/ActiveModel stuff (validators, naming, etc) for dynamic fields as well? P.S. Your AR approach is good, but in this way I don't have validators, and other AR goodies. – Fabio Apr 18 '12 at 12:42
  • 1
    Mongoid has pretty nice support for validations, see http://mongoid.org/docs/validation.html I'm not sure whether you can add validation for dynamic fields. – luacassus Apr 18 '12 at 13:23
  • 1
    You may find some clues here: https://groups.google.com/forum/?fromgroups#!searchin/mongoid/dynamic$20field$20validation/mongoid/b0pamrGyElg/G-U3Vqaf2RkJ – luacassus Apr 18 '12 at 13:28
  • if you're using Postgres, you can use hstore for this (http://railscasts.com/episodes/345-hstore). That is kinda "in-column schema-less thingy" – Milovan Zogovic Apr 30 '12 at 13:32
0

I don't know a gem, but this can be accomplished be creating a table called custom_fields with a name column and possibly a datatype column if you wanted to restrict fields by datatype. Then you create a join table for a custom field to your desired table and a value and do whatever validations you want.

cpuguy83
  • 5,794
  • 4
  • 18
  • 24
  • Yeah, I know that, but I'd prefer something already built which is already tested and known. Redmine acts_as_customizable could be an option but it can't be used as is since it's not extracted as a gem but built into Redmine itself. – Fabio Apr 17 '12 at 14:30
  • I was also just thinking you could use a column in your table as a key/value store, it just won't be searchable with the normal finders. – cpuguy83 Apr 17 '12 at 22:52
  • 1
    I know that too, but it won't allow to make changes at runtime, which is a requirement. In order to add a field I need to edit model code, so it's not ok for my needs. – Fabio Apr 18 '12 at 00:18