Mini John
Mini John

Reputation: 7941

Controller Best Practice

I have a Cards Controller where i need to set up categories. Because the views for this Controller would get pretty heavy to oversee i divided everything in folders.

routes.rb

  resources :cards do
    collection do
      get 'druid'
      get 'hunter'
      get 'mage'
      get 'paladin'
      get 'priest'
      get 'rogue'
      get 'shaman'
      get 'warlock'
      get 'warrior'
      get 'free'
      get 'common'
      get 'rare'
      get 'epic'
      get 'legendary'
      get 'spell'
      get 'minion'
      get 'weapon'
      get 'beast'
      get 'deamon'
      get 'dragon'
      get 'murloc'
      get 'pirate'
      get 'totem'
    end
  end

View Folders:

Views ->
    cards ->
      class ->
        druid.html.erb
        hunter.html.erb
        mage.html.erb
        paladin.html.erb
        priest.html.erb
        rogue.html.erb
        shaman.html.erb
        warlock.html.erb
        warrior.html.erb
      rarity ->
        free.html.erb
        common.html.erb
        rare.html.erb
        epic.html.erb
        legendary.html.erb
      type ->
        spell.html.erb
        minion.html.erb
        weapon.html.erb
      race ->
        beast.html.erb
        deamon.html.erb
        dragon.html.erb
        murloc.html.erb
        priate.html.erb
        totem.html.erb

Now i don't think this is such a good Idea, but as for now i don't know any better way of doing it..

My messy controller will look like this:

  def druid
    render 'cards/class/druid'
  end

  def hunter
    render 'cards/class/hunter'
  end

  def mage
    render 'cards/class/mage'
  end

  def paladin
    render 'cards/class/paladin'
  end

etc...

Now... This list will get pretty long...

Is there a better way of dealing with this ???

Upvotes: 3

Views: 763

Answers (2)

Midwire
Midwire

Reputation: 1100

UPDATE: 04/24/2014

Based on your absolute need to maintain separate views I would define the routes like this:

resources :cards do
  collection do
    get :cards, path: '/cards/:arg1'
  end
end

OR

resources :cards do
  collection do
    get :cards_by_class, path: '/cards/class/:class'
    get :cards_by_race, path: '/cards/race/:race'
    #etc...
  end
end

In your controller:

def index
  template, @cards = get_cards
  # you need to determine the path to the views based on params[:arg1]
  render template
end

private

# These constants could be defined in the Card model itself
CLASS_TYPES = %w(druid hunter mage paladin) # etc.
RARITIES = %w(free common rare epic legendary)
WEAPON_TYPES = %w(spell minion weapon)
RACE_TYPES = %w(beast demon dragon) # etc.

def get_cards
  path = ""
  cards = nil
  case params[:arg1]
    when CLASS_TYPES
      # Substitute 'classtype' with the proper column name
      cards = Card.where("classtype = '#{params[:arg1]}'")
      path = "/cards/class/#{params[:arg1]}"
    when RARITIES
      # Substitute 'rarity' with the proper column name
      cards = Card.where("rarity = '#{params[:arg1]}'")
      path = "/cards/rarity/#{params[:arg1]}"
    when WEAPON_TYPES
      # Substitute 'weapon_type' with the proper column name
      cards = Card.where("weapon_type = '#{params[:arg1]}'")
      path = "/cards/type/#{params[:arg1]}"
    when RACE_TYPES
      ... # you get the idea
  end
  return path, cards
end

To be more accurate, I would need to see your schema for the Card model.


Although Martin's solution would surely work just fine, I would probably go in this direction, and avoid hard-coding the different types, classes, etc:

# This sets up routes for :new, :create, :show, :update, :delete, :edit, :index
resources :cards

If you want to get a list of 'druid' cards in your spec/test:

get :index, {kind: 'druid'}

My controller method would look similar to this:

def index
    @cards = Card.where("kind = #{params[:kind]}")
    render "cards/class/#{params[:kind]}"
end

You could even make the view a single ERB or HAML template that may be generic enough to handle the different types of attributes. In that case you would get rid of, or change, the 'render' line above and let it default to 'index' or 'cards/class/index.html.erb' or whatever.

This is just off the top of my head, but I would tend to avoid concrete-coding the attributes, and instead treat them like properties of a 'card'. After all, if the 'card' is the resource, then all of the races, rarity, class, etc. are just attributes of a card.

Hopefully this all makes sense. Let me know if I can clarify further. The 'kind' attribute maybe something different in your DB schema model of the 'Card'.

Upvotes: 0

Martin
Martin

Reputation: 7723

A remark first: in your example (which I suppose is a simplified version of your application), your controller is just firing up the view. If this is correct, the pages could as well be totally static (pure HTML) and served statically.

Now, I think you should have more resources there: class, rarity, type and race could be resources by themselves, with the different "values" being the pages. After all (for what I can infer with my RPG knowledge), those are the elements of your domain model, so they should work great as resources.

The folders are already like that, so this could give something like: (warning, pseudo code out of my head)

resources :classes do # ClassesController
  collection do
    get 'druid'
    get 'mage'
  end
end

resources :rarity do # RarityController
  collection do
    get 'rare'
    get 'common'
end

Finally, never forget that controllers & the routing file are just ruby code. You can make loops there:

cards_list = ['rogue', 'druid', ...]

resources :cards do
    collection do
      cards_list.each do |card_name|
        get card_name
      end
    end
  end
end

This would work for the version by resource above too.

Some metaprogramming could achieve the same on your controller (if you have nothing different between the various action methods).

Upvotes: 3

Related Questions