Rake task to access models - ruby-on-rails

I'm trying to access a model called Book from a rake task like so
task :create_epubs => :environment do
include Rails.application.routes.url_helpers # brings ActionDispatch::Routing::UrlFor
include ActionView::Helpers::TagHelper
av = ActionView::Base.new(Rails.root.join('app', 'views'))
books = Book.all
av.render("books/", :books => books)
end
but i get the following warning
rake aborted!
undefined method `to_sym' for nil:NilClass
Tasks: TOP => create_epubs
(See full trace by running task with --trace)
I'm trying to load environment like the following accessing rails models from rake task but maybe it's slightly off for rails 3.1
*edit Book.all returns something when I do puts Book.all.to_yaml so the to_sym error is probably something else in av.render
I've figured out what the problem is. I was referring to instance variable from my view.
Can anyone tell me how to keep using instance variables by setting that variable?
This is the working version when I change the instance variables to the :params variables
task :create_epubs => [:environment] do
av = ActionView::Base.new(Rails.root.join('app', 'views'), :assigns => self)
av.view_paths = ActionController::Base.view_paths
av.extend ApplicationHelper #or any other helpers your template may need
book = Book.first
puts av.render(:template => "books/epub-show", :locals => {:book => book}, :layout => false) # this isn't passing #book to the view correctly, i get undefined method for nil:nilClass
end

you should probably use instance variables.
#book = Book.first
and in your render
:locals => { :book => #book }
also, i think you want
:layout => nil

Related

Use # symbol in a rails method name

Is there any way for me to use the '#' symbol in a rails method name? e.g.
def #method_name
end
Rails doesn't seem to like it.
I want to do it to adhere to some external conventions (external to rails).
It can be done like this:
define_method('#test') do
'test'
end
this method then has to be called with:
model.send('#test')
I would not recommend it, since it will be ugly and complicated and that is against the philosophy of ruby. But it can be done.
Kind of like this video: https://www.youtube.com/watch?v=eVpVHGiELf8
What I ended up using:
In my controller:
#job = Job.first
render :json #job.as_json( methods: :#id )
In Job.rb
define_method('#id') do
return url_for( :controller => 'jobs', :action => 'create', :id => id )
end
Now when I render my json i get:
"#id":"http://localhost:3000/jobs?id=1"
Note: #id is to comply with the syntax of json-ld

Heroku Scheduler.rake set up

Following the Railscasts am trying to set up a scheduled rake task to send an email to an administrator user:
desc 'Testing rake task to generate email'
task :overtime_report => :environment do
hospital_bookings = HospitalBooking.scoped
hospital_booking = hospital_bookings
user = User.where(:roles => :administrator)
if params[:format] == 'pdf'
hospital_bookings = hospital_bookings.where(:day => Date.today.beginning_of_month..Date.today.end_of_month)
end
respond_to do |format|
format.html
format.pdf do
render :pdf => "#{Date.today.strftime('%B')} Overtime Report",
:header => {:html => {:template => 'layouts/pdf.html.erb'}}
OvertimeMailer.overtime_pdf(user, hospital_booking).deliver
end
end
end
Controller
class HospitalBookingsController < ApplicationController
#load_and_authorize_resource
before_filter :admin_user, :only => [:index]
def index
#hospital_bookings = HospitalBooking.scoped
system "rake overtime_report Mail_ID=#{params[:id]} &"
flash[:notice] = 'Delivering Overtime Report'
end
Just need a bit of guidance if I am heading in the right direction.
Not much of your code makes sense I'm afraid:
A rake task is not an action in a controller so your use of params and respond_to will not work at all.
If it's a scheduled task to be run at a certain interval then you don't want a controller at all.
Your controller contains a huge security vulnerability by passing user input directly to the command line in the form of Mail_ID=#{params[:id]}.
Passing the Mail_ID option above into the rake task doesn't make sense as it's never used.
You should never call system commands from a controller.
hospital_booking = hospital_bookings makes no sense as it does nothing.
render isn't going to do anything either, that's the responsibility of the ActionMailer object you create.
You probably want something like:
desc 'Send hospital bookings overtime report'
task :overtime_report => :environment do
bookings = HospitalBooking.where(:day => Date.today.beginning_of_month..Date.today.end_of_month).all
user = User.where(:roles => :administrator).first
OvertimeMailer.overtime_pdf(user, hospital_bookings).deliver
end

Functional Testing with API Versioning?

I followed the RailsCast #350 REST API Versioning and #352 Securing an API which creates an API under app/controllers/api/v1 so you can type in localhost:3000/api/v1/restaurants and you'll get the JSON for restaurants index.
I want to be able to do some functional testing. I've been trying to figure this out but am not sure how to do this.
Here's what I've done so far:
I created api_v1_restaurants_controller_test.rb in my test/functional folder and did something like this:
require 'test_helper'
include Devise::TestHelpers
class ApiV1RestaurantsControllerTest < ActionController::TestCase
fixtures :users, :roles, :restaurants, :menus, :ingredients, :dishes, :drinks
setup do
#restaurant = restaurants(:applebees)
#user = users(:admin)
#api = api_keys(:one)
end
test "should get restaurant index json data" do
assert_routing(
'api/v1/restaurants',
{:controller => 'api/v1/restaurants', :action => 'index', :format => 'json', :api_key => #api},
{},
{:api_key => #api}
)
end
The should get restaurant index json data test seems to work but I want to be able to test to see whether api/v1/restaurants/1?api_key=123456789 generates JSON, which it should.
Here what I've tried to write up:
test "should get restaurant id json data" do
get 'api/v1/restaurants', :format => :json, :api_key => #api
# get :get, :format => :json, :api_key => #api
json = JSON.parse(#response.body)
assert_equal Restaurant.first.id, json.first["id"]
end
But I get the following error on my console after running rake test:functionals:
test_should_get_restaurant_id_json_data(ApiV1RestaurantsControllerTest):
RuntimeError: #controller is nil: make sure you set it in your test's setup method.
Update 1: Defined #controller
So I listened to the error message and defined #controller under my setup do... as #controller = Api::V1 but now get the following error message on the console:
test_should_get_restaurant_id_json_data(ApiV1RestaurantsControllerTest):
NoMethodError: undefined method `response_body=' for Api::V1:Module
I was able to finally figure it all out.
You need to define your #controller like this:
setup do
...
#api = api_keys(:one)
#restaurant = restaurants(:applebees)
#controller = Api::V1::RestaurantsController.new
end
And then you should be able to create a test like this to test your JSON:
test "json should be valid" do
get :show, :format => :json, :api_key => #api.access_token, :id => #restaurant.id
json = JSON.parse(#response.body)
assert_equal #restaurant.id, json["id"]
...
end
If you ever need to see what JSON is being produced, you can simply write (inside your test block):
puts json # This will output the entire JSON to your console from JSON.parse(#response.body)
I hope this helps other people as well.

undefined method 'link_to'

I'm writing a ruby-on-rails library module:
module Facets
class Facet
attr_accessor :name, :display_name, :category, :group, :special
...
URI = {:controller => 'wiki', :action => 'plants'}
SEARCH = {:status => WikiLink::CURRENT}
#Parameters is an hash of {:field => "1"} values
def render_for_search(parameters)
result = link_to(display_name, URI.merge(parameters).merge({name => "1"}))
count = WikiPlant.count(:conditions => (SEARCH.merge(parameters.merge({name => "1"}))))
result << "(#{count})"
end
end
...
end
when I call render_for_search I get the error
undefined method 'link_to'
I've tried requiring url_helper directly but can't figure out what's going wrong.
Try this:
ActionController::Base.helpers.link_to
This is because, ActionView urlhelpers are only available to the Views, not in your lib directory.
the link_to method is found in the ActionView::Helpers::UrlHelper module, plus you wou
so try this.
class Facet
include ActionView::Helpers::UrlHelper
...
end
Simply including the helper doesn't get you much further. The helpers assume that they are in the context of a request, so that they can read out the domain name and so on.
Do it the other way around; include your modules in the application helper, or something like that.
# lib/my_custom_helper.rb
module MyCustomHelper
def do_stuff
# use link_to and so on
end
end
# app/helpers/application_helper.rb
module ApplicationHelper
include MyCustomHelper
end

render_to_string from a rake task

I want to use a Rake task to cache my sitemap so that requests for sitemap.xml won't take forever. Here's what I have so far:
#posts = Post.all
sitemap = render_to_string :template => 'sitemap/sitemap', :locals => {:posts => #posts}, :layout => false
Rails.cache.write('sitemap', sitemap)
But when I try to run this, I get an error:
undefined local variable or method `headers' for #<Object:0x100177298>
How can I render a template to a string from within Rake?
Here's how I did it:
av = ActionView::Base.new(Rails::Configuration.new.view_path)
av.class_eval do
include ApplicationHelper
end
include ActionController::UrlWriter
default_url_options[:host] = 'mysite.com'
posts = Post.all
sitemap = av.render 'sitemap/sitemap', :posts => posts
Rails.cache.write('sitemap', sitemap)
Note that I converted my template to a partial to make this work
There is a post about how to be able to access ActionView::Base methods and context from rake task.
However, this is a monkeypatch. Why not use the rails' cache mechanism to accomplish caching? :)
Later edit:
The render_to_string function is defined in ActionController::Base context.
Below is a solution on how to make it work from rake tasks, taken from omninerd.
# In a rake task:
av = ActionView::Base.new(Rails::Configuration.new.view_path)
Rails.cache.write(
"cache_var",
av.render(
:partial => "view_folder/some_partial",
:locals => {:a_var => #some_var}
)
)
Recently I wanted to take a rake task defined like Horace Loeb mentioned and translate it into a self contained background job, but it didn't easily translate.
Here is my implementation for Rails 2.3.x because the Rails 3 implementation I found wouldn't work.
# Public: Template to render views outside the context of a controller.
#
# Useful for rendering views in rake tasks or background jobs when a
# controller is unavailable.
#
# Examples
#
# template = OfflineTemplate.new(:users)
# template.render("users/index", :layout => false, :locals => { :users => users })
#
# template = OfflineTemplate.new(ProjectsHelper, PermissionsHelper)
# template.render("projects/recent", :projects => recent_projects)
#
class OfflineTemplate
include ActionController::UrlWriter
include ActionController::Helpers::ClassMethods
# Public: Returns the ActionView::Base internal view.
attr_reader :view
# Public: Convenience method to
delegate :render, :to => :view
# Public: Initialize an offline template for the current Rails environment.
#
# helpers - The Rails helpers to include (listed as symbols or modules).
def initialize(*helpers)
helper(helpers + [ApplicationHelper])
#view = ActionView::Base.new(Rails.configuration.view_path, {}, self)
#view.class.send(:include, master_helper_module)
end
private
# Internal: Required to use ActionConroller::Helpers.
#
# Returns a Module to collect helper methods.
def master_helper_module
#master_helper_module ||= Module.new
end
end
This is available as a gist: https://gist.github.com/1386052.
Then you can use the class above to create an OfflineTemplate to render your views in a rake task:
task :recent_projects => :environment do
template = OfflineTemplate.new(ProjectsHelper, PermissionsHelper)
puts template.render("projects/recent", :projects => recent_projects)
end

Resources