0

I've been trying to wrap my head around this single table inheritance example. I understand the concept behind it, but am not sure from reading other posts around the web & on this site how to make it work for my example. I'm using STI because i've got two types of projects - public and private. The logic behind them is the same and the data they store in the database will be identical whether public or private. I just intend to implement some authorization & roles for users in the future that differ slightly based on whether the project is public or private. So my question is, what steps are still needed to make this work...so far I've:

1) added a type_column to projects via a migration...my project model is

PROJECT.RB

class Project < ActiveRecord::Base
  has_many :users
  has_many :versions, dependent: :destroy
  validates :title, presence: true, length: { maximum: 100 }
  validates :background, presence: true
  validates :user_id, presence: true

  default_scope -> { order('created_at DESC') }
end

2) I created separate classes for PublicProjects & PrivateProjects...those models are:

PUBLIC_PROJECT.RB

class PublicProject < Project
end

PRIVATE_PROJECT.RB

class PrivateProject < Project
end

So now i'm wondering what needs to be re-factored in my controller (i'd like to keep the single Projects controller)...and then also what needs changing in my 'new' form view. Step by step help on the code that would make this work would be much appreciated. The controller/view files are:

PROJECTS_CONTROLLER.RB

class ProjectsController < ApplicationController
  before_filter :signed_in_user, only: [:create, :new, :edit, :update]

  def new
    @project = Project.new
  end

  def show
     @project = Project.find(params[:id])
     @user = User.where(:id => @project.user_id).first
  end

  def index
    @projects = Project.paginate(page: params[:page])
  end

  def create
    @project = current_user.projects.build(project_params)
    if @project.save
      flash[:success] = "Welcome to your new project."
      redirect_to @project
    else
      render 'new'
    end
  end

  def edit

  end

  def update
    @project = Project.find(params[:id])
    if @project.update_attributes(params[:project])
      flash[:success] = "Project Created"
      redirect_to @project
    else
      render 'edit'
    end
  end

  def destroy
    User.find(params[:id]).destroy
    flash[:success] = "Project destroyed"
    redirect_to users_path
  end

  private

    def project_params
      params.require(:project).permit(:title, :background)
    end

end

NEW.HTML.ERB (NEW PROJECTS VIEW W/ FORM)

<% provide(:title, 'New Project') %>
<h1>Create a new project</h1>

<div class="row-fluid">
  <div class="col-md-5 no-pad offset3">

    <%= bootstrap_form_for @project do |f| %>

      <%= render 'shared/error_messages', object: f.object %>

      <%= f.text_field :title %>

      <%= f.text_area :background %>

      <div class="row-fluid">
        <div class="no-pad col-md-6">
          <h5>Is this project public or private?</h5>
          <div class="btn-group">
            <button type="button" class="btn btn-default"><%= image_tag "globe.png" %> Public</button>
            <button type="button" class="btn btn-default"><%= image_tag "lock.png" %> Private</button>
          </div>
          <script>
          $(".btn-group > .btn.btn-default").click(function(){
              $(".btn-group > .btn.btn-default").removeClass("active");
              $(this).addClass("active");
          });
          </script>
        </div>
        <div class="col-md-6">
          Some static graphics
        </div>
      </div>

      <br clear="all"><br/>
      <%= f.submit "Create your project", class: "btn btn-lg btn-primary" %>
    <% end %>
  </div>

</div>

Lastly, i've seen some STI examples that are getting into the routes file (although I've seen that this isn't recommended as often). but just in case:

ROUTES.RB

ProductionApp::Application.routes.draw do
  resources :users
  resources :sessions, only: [:new, :create, :destroy]

  resources :projects do
    resources :versions
  end

  # get "static_pages/home"
  # get "static_pages/help"
  # get "static_pages/about"
  #The original routes above map to...
  root  'static_pages#home'
  match '/signup',  to: 'users#new',            via: 'get'
  match '/signin',  to: 'sessions#new',         via: 'get'
  match '/signout', to: 'sessions#destroy',     via: 'delete'
  match '/help',    to: 'static_pages#help',    via: 'get'
  match '/about',   to: 'static_pages#about',   via: 'get'
  match '/contact', to: 'static_pages#contact', via: 'get'
end

Thanks a lot for any help,

BB500
  • 549
  • 2
  • 6
  • 24

1 Answers1

1

I am not sure about the authentication, but instead of using additional classes for public and private, you could just create a type field that tells you whether or not the project is public or private something like is_public.

The other option is to use a enum release in rails 4 - really simple and straight forward. This is my preferred option, but you need to be working on the latest version of rails.

Ryan-Neal Mes
  • 6,003
  • 7
  • 52
  • 77
  • Hi Ryan, won't an 'is_public' type field leave a lot of null values in the database? I was warned to avoid this & do it using Single table inheritance... I'm not familiar with enums...I'm using rails 4.0.4. but it looks interesting...what would be the benefits of using something like this over STI (& are there any drawbacks...from all my research its seemed like STI is the way to go on this, but I realize enums are very new). thanks for your response – BB500 Apr 22 '14 at 15:26
  • I would guess if you are planning on adding specific functionality to private/public projects then STI is the way to go so you can extend the functionality, otherwise I would go for simplicity. – Ryan-Neal Mes Apr 22 '14 at 15:31
  • yea the only difference will be around roles & permissions down the road (all users can be collaborators on public projects...only invited users can be collaborators on private projects, etc). you're not too sure about how to do it through STI though huh? – BB500 Apr 22 '14 at 15:43
  • After much deliberation...I think you were probably correct. If the only differences between public & private projects will be the user access & various role authorizations...it is best to just treat it as a flag. I simply used an is_private? type field as you suggested...even though it doesn't really correspond to the original question about STI, i'll mark your answer as correct because its a better way to go. thx, – BB500 Apr 24 '14 at 14:40