M.M.
M.M.

Reputation: 77

Ruby on Rails - Creating and Using a Custom Method

I am rather new to Rails, and would greatly appreciate any bit of help. I have created the following method:

  def name_fix
    name = self.split
    mod_name = []
    name.each do |n|
      n.split("")
      if n[0]
        n.upcase
      else
        n.downcase
      end
      mod_name.push(n)
    end
    mod_name.join
  end

I would like to use this method in my Controller as such:

def create
  @patient = Patient.new(params[:patient])
  @patient.name = params[:params][:name].name_fix
  if @patient.save
    redirect_to patients_path
  else
    render :new
  end
end

How can I go about accomplishing this? Will this method reside within my Model or Controller? Previously, I've run into an undefined method error.

Note: I'm sure that there is a way to better write my code. I am grateful for help with that as well.

Upvotes: 5

Views: 3650

Answers (4)

Richard Peck
Richard Peck

Reputation: 76774

#app/models/patient.rb
class Patient < ActiveRecord::Base

   protected

   def name=(value)
      mod_name = []
      value.split.each do |n|
         n.split("")
         type = n[0] ? "up" : "down"
         n.send("#{type}case")
         mod_name.push(n)
      end
      @name = mod_name.join
   end
end

#app/controllers/patients_controller.rb
class PatientsController < ApplicationController
   def create
      @patient = Patient.new patient_params
      @patient.save ? redirect_to(patients_path) : render(:new)
  end

  private

  def patient_params
     params.require(:patient).permit(:name)
  end
end

What you're doing is trying to override the setter method, which can be done using the above code. Much more efficient and out of the way.


I have created the following method

Since you're new, let me explain something else.

It is important to note where you're using this method.

You've currently put it in the model, which means you'll have to call it to manipulate some attribute / functionality of any object created with said model.

--

Models - in Rails - build the objects which populate your app. Ruby is an object orientated language, which means that every element of your program should revolve around data objects in some degree.

enter image description here

As you can see above, the method of building objects in your system is really about invoking classes. These classes contain methods which can be called, either at class level (IE invoking the class through the method), or at instance level (IE calling a method on an already invoked object).

This is where you get "class" methods (Model.method) and "instance" methods (@model.method) from:

#app/models/patient.rb
class Patient < ActiveRecord::Base
   def explode
      #this is an instance method
      puts "Instance Explode"
   end

   def self.explode
      #this is a class method
      puts "Exploded"
   end
end

Thus you can call the following:

@patient = Patient.find params[:id]
@patient.explode #-> "Instance explode"

Patient.explode #-> "Exploded"

--

This is important because it gives you a strict framework of where you should, and shouldn't use methods in your models.

It explains why you have controllers & helpers, and allows you to formulate the best way to structure your application as to get the most out of the least code.

For example...

Your use of @patient.name = params[:params][:name].name_fix is incorrect.

It's wrong because you're calling the instance method .name_fix on a piece of data totally unrelated to your model. If you wanted to use .name_fix in a general sense like this, you'd probably use a helper:

#app/helpers/patients_helper.rb
class PatientsHelper
   def name_fix value
      # stuff here
   end
end

#app/controllers/patients_controller.rb
class PatientsController < ApplicationController
   def create
      @patient.name = name_fix params[:patient][:name]
   end
end

Since you're using the method to populate the .name attribute of your model, it makes sense to override the name= setter. This will not only provide added functionality, but is much smoother and efficient than any other way.

Upvotes: 2

Arvind Kumar
Arvind Kumar

Reputation: 247

update your create method as below

  def create
    @patient = Patient.new(params[:patient])
    @patient.name = params[:params][:name]
    @patient = @patient.name_fix
     if @patient.save
        redirect_to patients_path
     else
       render :new
     end
   end

It should work.

Upvotes: 0

etdev
etdev

Reputation: 534

Assuming your goal with the name_fix method is just to capitalize the first letter of each name, you could just pass name as an argument and store it as a private method on the Controller:

# app/controllers/patient_controller.rb
private
def name_fix(name)
  name.split.map(&:capitalize).join(" ")
end

Then you could do

@patient.name = name_fix(params[:params][:name])

in the create method.

OR, you could store this method in the model:

# app/models/patient.rb
def self.name_fix(name)
  name.split.map(&:capitalize).join(" ")
end

Then you could do this instead, in the controller:

@patient.name = Patient.name_fix(params[:params][:name])

I would also suggest renaming your name_fix method to something like capitalize_name.

Upvotes: 0

user2490003
user2490003

Reputation: 11890

Methods that are called directly are best put in the Controller (or in ApplicationController if you think more than one controller might want to use it).

These are methods like

# app/controllers/my_controller.rb

def foo(bar)
  # do something here
end

def create
  id = params[:id]
  value = foo(id)
end

If you want a chained method that acts as a property method of whatever you're calling it on. Those are characteristic of how Models work - you have your main model and you call attributes or methods on the instance of that model.

# app/models/my_model.rb
def full_name
 first_name + " " + last_name
end

# app/controller/my_controller.rb
def create
  id = params[:id]
  model = MyModel.find(id)
  full_name = model.full_name
end

In your case, you want to call name_fix ON whatever is returned by params[:params][:name], which is (I'm guessing) a String.

You have two options

  1. Modify the String class to define a method named name_fix. I highly recommend against this. It's call "monkeypatching" and shouldn't be done without good reason. Just letting you know you can do it in some cases.

  2. Use a direct method in your controller or ApplicationController like the first example above.

    @patient.name = name_fix(params[:params][:name])

Edit: As for your request about a better way to write your code... that's difficult to teach or convey in one answer. I'd say read some open source projects out there to see how people write Ruby and some common idioms used to clean up the code. To get you started, here's how I'd re-write your code

def create
  @patient = Patient.new(params[:patient])

  # 1. Be descriptive with your method names. `name_fix` is vague
  # 2. Why is `:name` nested under another `[:params]` hash?
  @patient.name = capitalize_name(params[:name])

  if @patient.save
    # 1. I think `patient_path` has to be singular
    # 2. It needs a `Patient` object to know how to construct the URL
    #     e.g. `/patients/:id`
    redirect_to patient_path(@patient)
  else
    render :new
  end
end


def capitalize_name(full_name)
  # Example: julio jones
  # 
  # 1. `split` produces an array => ["julio", "jones"]
  # 2. `map` applies a function (`capitalize`) to each element
  #       => ["Julio", "Jones"]
  # 3. `join(" ")` rejoins it => "Julio Jones"
  full_name.split.map(&:capitalize).join(" ")
end

Upvotes: 0

Related Questions