1

I am a newbie to ruby and rails. In my Rails app, I am trying to use Wicked Wizard gem and need some help in Understanding about the cattr_accessor which I have come across in the code

  create_table "pets", force: :cascade do |t|
            t.string   "name"
            t.string   "colour"
            t.string   "owner_name"
            t.text     "identifying_characteristics"
            t.text     "special_instructions"
            t.datetime "created_at"
            t.datetime "updated_at"
            t.string   "email"
            t.string   "password"
          end

model

 class Pet < ActiveRecord::Base
  has_many :pet_photos
  cattr_accessor :form_steps do
    %w(identity characteristics instructions)
  end

  attr_accessor :form_step
  validates :email, presence: true
  validates :name, :owner_name, presence: true, if: -> { required_for_step?(:identity) }
  validates :identifying_characteristics, :colour, presence: true, if: -> { required_for_step?(:characteristics) }
  validates :special_instructions, presence: true, if: -> { required_for_step?(:instructions) }

  def required_for_step?(step)
    return true if form_step.nil?
    return true if self.form_steps.index(step.to_s) <= self.form_steps.index(form_step)
  end



end

and

      class Pet::StepsController < ApplicationController
        include Wicked::Wizard
        steps *Pet.form_steps

        def show
          @pet = Pet.find(params[:pet_id])
          render_wizard
        end

        def update
          @pet = Pet.find(params[:pet_id])
          @pet.update(pet_params(step))

            if params[:images]

                params[:images].each do |image|
                @pet.pet_photos.create(image: image)
              end
            end

          render_wizard @pet
        end

        private

        def pet_params(step)
          permitted_attributes = case step
                                 when "identity"
                                   [:name, :owner_name]
                                 when "characteristics"
                                   [:colour, :identifying_characteristics]
                                 when "instructions"
                                   [:special_instructions]
                                 end

          params.require(:pet).permit(permitted_attributes).merge(form_step: step)
        end

      end

routes

  PetThing::Application.routes.draw do
          resources :pets, only: [:new, :create, :index, :destroy] do
            resources :steps, only: [:show, :update], controller: 'pet/steps'
          end

          root to: 'pets#index'

Now my Questions are

1) what is the difference between the cattr_accessor and attr_accessor?

       cattr_accessor :form_steps do
       %w(identity characteristics instructions)
       end
       attr_accessor :form_step

2) why two different symbols(:form_steps , :form_step) are used as method parameters for cattr_accessor and attr_accessor methods respectively?

3) why is a block passed as a parameter to cattr_accessor method?

Any Help is highly Appreciated.Thank you

current_user
  • 1,172
  • 1
  • 16
  • 28
  • 1
    Possible duplicate of [cattr\_accessor in Rails?](https://stackoverflow.com/questions/3411403/cattr-accessor-in-rails) – jvillian Dec 05 '17 at 16:21

1 Answers1

4

First of all, this method is deprecated or moved. Which version do you use, Rails 4 or Rails ?

cattr_accessor > replaced for mattr_accessor(*syms, &blk) for Module which is the superclass of the class Class. I recomend to use attr_accessor, which is just a method to set attributes for the class, it works like a getter or a setter for the class but only works for an instance (in memory), the attribute is saved anywhere.

cattr_accessor is like the attr_* methods, but for the class level. One thing you wouldn't expect is because it uses a backing @@form_steps, the value shared between the class and all instances.

Api docs:

Defines both class and instance accessors for class attributes.

module HairColors
  mattr_accessor :hair_colors
end

class Person
  include HairColors
end

Person.hair_colors = [:brown, :black, :blonde, :red]
Person.hair_colors     # => [:brown, :black, :blonde, :red]
Person.new.hair_colors # => [:brown, :black, :blonde, :red]

If a subclass changes the value then that would also change the value for parent class. Similarly if parent class changes the value then that would change the value of subclasses too.

class Male < Person
end

Male.hair_colors << :blue
Person.hair_colors # => [:brown, :black, :blonde, :red, :blue]

To opt out of the instance writer method, pass instance_writer: false. To opt out of the instance reader method, pass instance_reader: false.

module HairColors
  mattr_accessor :hair_colors, instance_writer: false, instance_reader:             false
end

class Person
  include HairColors
end

Person.new.hair_colors = [:brown]  # => NoMethodError
Person.new.hair_colors             # => NoMethodError

Or pass instance_accessor: false, to opt out both instance methods.

module HairColors
  mattr_accessor :hair_colors, instance_accessor: false
end

class Person
  include HairColors
end

Person.new.hair_colors = [:brown]  # => NoMethodError
Person.new.hair_colors             # => NoMethodError

Also you can pass a block to set up the attribute with a default value.

module HairColors
  mattr_accessor :hair_colors do
[:brown, :black, :blonde, :red]
  end
end

class Person
  include HairColors
end

Person.class_variable_get("@@hair_colors") #=> [:brown, :black, :blonde, :red]

1)

  • attr_accessor: is a ruby method that makes a getter and a setter
  • cattr_accessor:is a ruby method that makes a getter and a setter for the class (quite strange).

2) Class variable have a tendency to wander from class to class. @@form_steps class variable can be exposed through inheritance tree.

3) To set which attributes will have this class. form_steps

p = Pet.new
p.form_steps = "var"

and use the form_steps attribute inside your current instance Pet (like you do in the method required_for_step?).

maguri
  • 386
  • 2
  • 10