user3538310
user3538310

Reputation: 11

User-Generated Models in Rails

I'm trying to make a database that keeps track of items for a user.

For example, a user might want to keep track of their cars and the color, make and year of it. They will first create a cars "model", and they can now add as many new cars as they want. When they make a car, they will be prompted to input the color, make and year as well as generic item information.

The user might also want to keep track of their cats. But the cat "model" has different fields than the car "model." Maybe they need to keep track of the weight and breed of their cats. When they want to add an item of type cat to the database, I will prompt them to input the weight and breed NOT color, make and year.

I'm not very experienced with rails or SQL, but I was thinking when a user tells me they want to keep track of cars, I could create a cars table with color make and year. When they input a car, I would store the car as a generic item and also keep a reference to the cars table where I would store the color make and year. Not quite sure how I would implement though, so I'm open to any new ideas. Thanks in advance for your help.

Upvotes: 1

Views: 71

Answers (2)

Richard Peck
Richard Peck

Reputation: 76774

I've been playing with something called Meta models - whereby you have app/models/meta.rb which allows you to invoke models the user defines in the db.

I've had this running, but it's not super efficient.

I'll tell you about it; maybe it will give you some ideas.............

--

Each model can be stripped down to set of 2 common attributes - title & value:

#app/models/node.rb
class Node < ActiveRecord::Base
   # columns id | type | title | value | created_at | updated_at
end

This is the very base of the models you wish to use (car / cat etc).

For example, you could make a node have the following stats:

# id | type      | title | value            | created_at | updated_at
# 1  | Meta::Cat | Greg  | A new cat we got | 15/12/2015 | 15/12/2015
# 2  | Meta::Cat | Pip   | A little kitten  | 16/12/2015 | 16/12/2015
# 3  | Meta::Car | F480  | The red Ferrari  | 10/12/2015 | 10/12/2015

This will give you a base set of data which you can then append different associations to.

And yes, the new associations can be stored in the node datatable too.

So in fact, you could have the following:

#app/models/meta.rb
class Meta
    classes = %w(cat car)
    if classes.any?
        classes.each do |klass| #-> "class" is reserved
            self.const_set klass.titleize, Class.new(Node) do
                belongs_to :user
            end
        end
    end
end

This will programmatically create the Cat and Car classes, which can be called with Meta::Cat etc as required.

I guess this is how you could get your different "base" models.

If you then wanted to add extra associations to that, you'd have to have extra data to make sure that you know which associations each model has.

I've not done this yet, but as you can see above, you can invoke a block which basically allows you to define different things in the pseudo-class:

--

So if you had another meta class called Option, you could use it to set the associations for each other class:

# 2  | Meta::Option | association | has_many :x   | 16/12/2015 | 16/12/2015
# 3  | Meta::Option | association | belongs_to :y | 10/12/2015 | 10/12/2015

I still need to think about how it will match the association, but in short, you'll be able to do the following:

#app/models/meta.rb
class Meta
    classes = %w(cat car)
    if classes.any?
        classes.each do |klass| #-> "class" is reserved
            self.const_set klass.titleize, Class.new(Node) do
                #-> Node.where(name: "association").pluck(:value)
            end
        end
    end
end

This is known as metaprogramming (or it might be pseudoprogramming) and is something which could be done; whether it's efficient is another question entirely.

Upvotes: 0

JonSayer
JonSayer

Reputation: 196

You are quite right that you will need to create a table for both a car and cat, each with their own columns. Rails comes with Active Record to facilitate the creation of databases. Active record is really easy to use and has some great documentation. For example, to create a table for a car, you will have to create a car model first. To get Active Record to create a model, you just use the following command in terminal:

$ rails g model Car color:string make:string year:integer

This model is stored in app/models. Don't forget to migrate your database after you create the model. In order for a user to enter the properties of the instance, you will need to use form_for. Documentation for a form_for can be found here. To use a form_for you need to declare the instance for the model that you want to change and the method you are going to use once the form has been submitted. You then follow this up with some input fields which are all associated with a property of the instance. The example in the documentation is a pretty good one.

<%= form_for @article, url: {action: "create"}, html: {class: "nifty_form"} do |f| %>
    <%= f.text_field :title %>
    <%= f.text_area :body, size: "60x12" %>
    <%= f.submit "Create" %>
<% end %>

Upvotes: 1

Related Questions