AlexNikolaev94
AlexNikolaev94

Reputation: 1209

Make a generator of models inheriting from a pre-built model

I'm writing a little Ruby on Rails CMS, and I want to make a generator that would create models inheriting from the pre-built CMS data model. For example, I have a Entry model in my CMS, and I want to create a Post model that would be child of this model. This is the source code:

db/migrations/*create_mycms_entries.rb:

class CreateMyCmsEntries < ActiveRecord::Migration[5.0]
  def change
    create_table :my_cms_entries do |t|
      t.string :type, index: true
      t.string :title
      t.string :slug, index: true
      t.json :payload
      t.integer :user_id, index: true
      t.string :author_name

      t.datetime :published_at
      t.timestamps null: false
    end
  end
end

entry.rb:

module MyCms
  class Entry < ActiveRecord::Base
    scope :published, -> { where('published_at <= ?', Time.zone.now) }

    def self.content_attr(attr_name, attr_type = :string)
      content_attributes[attr_name] = attr_type

      define_method(attr_name) do
        self.payload ||= {}
        self.payload[attr_name.to_s]
      end

      define_method('#{attr_name}='.to_sym) do |value|
        self.payload ||= {}
        self.payload[attr_name.to_s] = value
      end
    end

    def self.content_attributes
      @content_attributes ||= {}
    end
  end
end

and, at my blog side, post.rb:

class Post < MyCms::Entry
  content_attrs :body, :text

  # Rest of the stuff...
end

I want the final command look something like this:

$ rails generate entry Post body:text

But I'm not quite sure I know how to implement it.

Upvotes: 0

Views: 164

Answers (2)

Mirza Memic
Mirza Memic

Reputation: 872

If you want to create custom model generator then you can use Thor (https://github.com/erikhuda/thor) which rails team has documented here

http://guides.rubyonrails.org/generators.html

First start by typing this command:

bin/rails g generator entry

So you are triggering generator to generate generator :). You will get something like this after running your command

create  lib/generators/entry
create  lib/generators/entry/entry_generator.rb
create  lib/generators/entry/USAGE
create  lib/generators/entry/templates

Then what you can do is just copy the code that ActiveRecord has for creating models. Inside your templates you will have this file model.rb like here

https://github.com/rails/rails/blob/master/activerecord/lib/rails/generators/active_record/model/templates/model.rb

You will see that it has something like this:

class <%= class_name %> < <%= parent_class_name.classify %>

class_name is a variable that you pass to your generator, like in your example Post. And you can define it like this inside your entry_generator

argument :class_name, type: :string, default: "Post"

I would definitely recommend that you look at this file since the rails generator has everything you need...you would just customize it for your specific need:

https://github.com/rails/rails/blob/master/activerecord/lib/rails/generators/active_record/model/model_generator.rb

In generator you have a class that invokes the template. Here is another video from Ryan Bytes that explains creating generator on custom layout which you can find it useful for your CMS.

http://railscasts.com/episodes/218-making-generators-in-rails-3?view=asciicast

Good luck

Upvotes: 1

Nic Nilov
Nic Nilov

Reputation: 5155

What you are trying to do is single-table inheritance (STI) and Rails has the support for that. Basically you need column type in your entries table which would store the class name within your hierarchy. You don't need to use the parent attribute as it seems to be in your example. See more in Rails docs or blogs, e.g. this one.

Upvotes: 1

Related Questions