ZK Zhao
ZK Zhao

Reputation: 21513

Rails: How to define a function in a rake task

In my task file post.rake, I want to reuse a function

  def save_post(title, href, source)
    post = Post.new(title: title, url: href, source: source)
    if post.save
      puts title + 'saved'
    else
      puts title + 'not saved'
    end
  end

However, when I define it in this file and re-use it, it returns

NoMethodError: undefined method `save_post' for main:Object

The post.rake looks like this:

task :fetch_post => :environment do
  require 'nokogiri'
  require 'open-uri'

  url = 'http://example.com'
  doc = Nokogiri::HTML(open(url) )
  puts doc.css("title").text
  doc.css(".a").each do |item_info|
    title = item_info.text
    href = item_info['href']
    save_post(title, href)
  end

  def save_post(title, href)
    post = Post.new(title: title, url: href)
    if post.save
      puts title + 'saved'
    else
      puts title + 'not saved'
    end
  end
end

The content-scraping part works. I just move the post-saving code out, wanting to abstract the method out.

Where should I put the def method?

Upvotes: 4

Views: 5199

Answers (3)

scottxu
scottxu

Reputation: 933

OOOH~~~,function postion is wrong, like this, it works:

task :fetch_post => :environment do
  require 'nokogiri'
  require 'open-uri'

  def save_post(title, href)
    post = Post.new(title: title, url: href)
    if post.save
      puts title + 'saved'
    else
      puts title + 'not saved'
    end
  end

  url = 'http://example.com'
  doc = Nokogiri::HTML(open(url) )
  puts doc.css("title").text
  doc.css(".a").each do |item_info|
    title = item_info.text
    href = item_info['href']
    save_post(title, href)
  end
end

Upvotes: 4

Roman Kiselenko
Roman Kiselenko

Reputation: 44360

You should define method before and outside of task:

task :fetch_post => :environment do
  require 'nokogiri'
  require 'open-uri'

  url = 'http://example.com'
  doc = Nokogiri::HTML(open(url) )
  puts doc.css("title").text
  doc.css(".a").each do |item_info|
    title = item_info.text
    href = item_info['href']
    save_post(title, href)
  end
end

def save_post(title, href)
  post = Post.new(title: title, url: href)
  if post.save
    puts title + 'saved'
  else
    puts title + 'not saved'
  end
end

But i think this logic should be in model.

#app/models/post.rb

class Post < ActiveRecord::Base

  def self.save_post(title, href)
    post = Post.new(title: title, url: href)
    if post.save
      puts title + 'saved'
    else
      puts title + 'not saved'
    end
   end
 end

Upvotes: 1

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230286

If you define methods in a rake task, they become accessible globally, which may have undesired side-effects. A cleaner approach is to use an inline lambda (or move the method to some class in the app)

task :fetch_post => :environment do
  require 'nokogiri'
  require 'open-uri'

  save_post = ->(title, href) {
    post = Post.new(title: title, url: href)
    if post.save
      puts title + 'saved'
    else
      puts title + 'not saved'
    end
  }

  url = 'http://example.com'
  doc = Nokogiri::HTML(open(url) )
  puts doc.css("title").text
  doc.css(".a").each do |item_info|
    title = item_info.text
    href = item_info['href']
    save_post.call(title, href)
  end

end

Upvotes: 8

Related Questions