Run controller as Ruby script? - ruby

In my application I setup a news controller to handle news feeds using Nokogiri:
class NewsController < InheritedResources::Base
def new
require 'nokogiri'
require 'open-uri'
doc = Nokogiri::XML(open("http://www.themusicvoid.com/feed"))
#info = doc.xpath('//item').take(5).map do |i|
News.create(:title => i.xpath('title').inner_text,
:description => i.xpath('description').inner_text,
:link => i.xpath('link').inner_text,
:image => i.xpath('image').inner_text)
end
end
end
This functions by running <%= debug #info %> in the new.html.erb file. It searches the RSS feed and automatically saves to my database.
The problem is that it would do this whenever ANYBODY loaded new.html.erb (although I could add that file to my robots.txt so at least bots wouldn't trigger it.)
What I actually want is some sort of a Ruby script or Rake task that would do the exact same thing without loading a page. If I just run the code above as a script, it works, it would output the data if I wanted to echo it, but it does not save the information to the database. Any ideas on how I might do that?

You can create a rake task under lib/tasks directory:
rss_fetcher.rake
require 'nokogiri'
require 'open-uri'
namespace :rss do
desc "Fetch rss feed"
task :fetch => :environment do
doc = Nokogiri::XML(open("http://www.themusicvoid.com/feed"))
#info = doc.xpath('//item').take(5).map do |i|
News.create(:title => i.xpath('title').inner_text, :description => i.xpath('description').inner_text, :link => i.xpath('link').inner_text, :image => i.xpath('image').inner_text)
end
end
end
then somewhere call rake 'rss:fetch'
I highly recommend the whenever gem to do that kind of job
gem install whenever
config/schedule.rb
every 2.hours do
rake "rss:fetch"
end

Related

Acceptance testing of sinatra app using webrat fails

I am trying to test a ruby authentication app using minitest and webrat but get errors.
Tests like visit '/' fail with an error Status 200 expected but was 404.
Tests containing code like fill_in :email, :with => "first#company.com" fail with error Could not find field: :email.
I read several sinatra, testing and webrat documents and forums. Some of them were old and suggested stuff like Sinatra::Default, but github.com/brynary/webrat/wiki/sinatra, Building a Sinatra App Driven By Webrat Tests and Learning From the Masters: Sinatra Internals are new, yet they still fail.
Basically, I didn't like sentence-like syntax of rspec, cucumber etc but do want to do behaviour driven development. I really like the minitest syntax, both tests and output and that is why I choose webrat for BDD. If I'm wrong about expecting webrat to fulfill acceptance testing requirements, please simply tell me that I should use this framework or that one.
Apart from that, the first parts of the main file and test file are below. I hope someone can explain me, what I am missing?
test_file
require "test/unit"
require "minitest/autorun"
require "rack/test"
require 'webrat'
require_relative "../lib/kimsin.rb"
Webrat.configure do |config|
config.mode = :rack
end
ENV["RACK_ENV"] = "test"
class KimsinTests < Test::Unit::TestCase
include Rack::Test::Methods
include Webrat::Methods
include Webrat::Matchers
def app
Sinatra::Application.new
end
def test_create_user
visit "/user/new"
fill_in :username, :with => "first#company.com"
fill_in :password, :with => "abC123?*"
fill_in :confirm_password, :with => "abC123?*"
click_link "Register"
assert 201, last_response.status, "Status 201 expected but was #{last_response.status}.\n#{error}"
assert_contain /Logged in as first#company.com./, "No user created"
assert_contain /Logout/, "Logout link not present"
end
main_file
require "sinatra"
require "erb"
require_relative "../lib/kimsin/version"
require_relative "../lib/kimsin/user"
class Kimsin < Sinatra::Application
use Rack::Session::Pool, :expire_after => 2592000
set :session_secret, BCrypt::Engine.generate_salt
configure :development do
DataMapper.auto_migrate!
end
get "/" do
if session[:user_id]
user = User.get session[:user_id]
email = user.email
erb :index, :locals => { :email => email }
else
email = nil
erb :index, :locals => { :email => email }
end
end
Using Sinatra with Webrat should work fine. I think that the errors that you are seeing are caused by the following method (around line 18 in your test file):
def app
Sinatra::Application.new
end
This is setting up the Sinatra::Application base class to run your tests against when you really need to set up your own subclass Kimsin (because you are creating a modular style Sinatra app), i.e.
def app
Kimsin.new
end
The 404 errors and missing fields are happening because Sinatra::Application doesn't define any of the routes you are testing.
You might also like to take a look at Capybara if you are looking for similar alternatives to Webrat.

Why doesn't the sinatra-redirect-with-flash gem work with shotgun?

I want to show flash messages using sinatra-redirect-with-flash gem.
Here's my ruby code:
require 'sinatra'
require 'sinatra/base'
require 'sinatra/flash'
require 'sinatra/redirect_with_flash'
require 'data_mapper'
require 'haml'
require 'builder'
# ...
class App < Sinatra::Base
enable :sessions
register Sinatra::Flash
helpers Sinatra::RedirectWithFlash
use Rack::MethodOverride
get '/' do
#notes = Note.all :order => :id.desc
#title = 'All TODOs'
if #notes.empty?
flash.now[:error] = 'No TODOs found. Add your first below.'
end
haml :home
end
post '/' do
n = Note.new
n.content = params[:content]
n.created_at = Time.now
n.updated_at = Time.now
if n.save
redirect '/', :notice => 'TODO saved successfully.'
else
redirect '/', :error => 'Failed to save TODO.'
end
end
# ...
end
And views/layout.haml is:
!!! 5
%html{:lang => "en"}
%head
%meta{:charset => "utf8"}
%body
%header
%hgroup
%h1
%a{:href => "/"}= SITE_TITLE
%h2= SITE_DESCRIPTION
#main
=styled_flash
=yield
After adding a TODO successfully, I expected to see the flash message 'TODO saved successfully.' on the home page. But no flash messages are shown after redirection when I run my app using shotgun. Flash messages are shown well when I run ruby app.rb or rackup.
How can I solve this problem?
Another problem is also happening when I run the app using shotgun. In get '/' method, if I use flash[:error] instead of flash.now[:error], the flash message doesn't show up on the page.
I am shadowning this tutorial, but I made some differences:
erb -> haml
Classic Sinatra app -> Subclassing Sinatra::Base
rack-flash -> sinatra-flash
You can browse whole codes here.
Thanks for any answers/comments.
The shotgun gem reloads Sinatra after every request. The README says:
Each time a request is received, it forks, loads the application in
the child process, processes the request, and exits the child process. The
result is clean, application-wide reloading of all source files and templates on
each request.
As a result, you will need some sort of mechanism to preserve state between requests that doesn't rely on data stored in each child process.

Unable to render PDF to browser using Prawn PDF for Ruby on Rails

I am creating a pdf file in the latest version of the Prawn library (v1.0.1rc) in Rails (3.1.1) and when I run my code it generates the PDF into the root of the application.
I don't want this. I want it to render the output into user's browser window, without saving it locally to the server.
Please tell me how I can achieve this. Here are my files:
views/foo/show.pdf.erb:
<%=
require 'prawn'
pdf = Prawn::Document.new(:page_size => 'LETTER', :page_layout => :landscape, :margin => 50, :top_margin => 20, :bottom_margin => 50)
.....
render_file("foo.pdf")
%>
controllers/foo_controller:
class AuditsController < ApplicationController
before_filter :authenticate_user!
layout 'application'
can_edit_on_the_spot
respond_to :html, :xml, :js, :pdf
def index
#audits = Audit.all
respond_with #audits
end
def show
#audit = Audit.find(params[:id])
respond_with #audit do |format|
format.pdf { render :layour => false }
end
end
Gemfile
gem 'prawn'
/config/initializers/mime_types.rb
Mime::Type.register "application/pdf", :pdf
AuditsController
def show
#audit = Audit.find(params[:id])
respond_to do |format|
format.html
format.pdf do
pdf = Prawn::Document.new
pdf.text "This is an audit."
# Use whatever prawn methods you need on the pdf object to generate the PDF file right here.
send_data pdf.render, type: "application/pdf", disposition: "inline"
# send_data renders the pdf on the client side rather than saving it on the server filesystem.
# Inline disposition renders it in the browser rather than making it a file download.
end
end
end
I used to use the prawnto gem before Rails 3.1, but it doesn't work without a bit of hacking anymore. This is a much cleaner way to instantiate and display the PDF object in 3.1 by accessing Prawn directly.
I got this technique straight from one of Ryan Bates' Railscasts. Been using it ever since. You can view that specific episode here. He goes into much more detail about subclassing Prawn and moving the PDF generating code out of the controller. Also shows a lot of useful Prawn methods to get you started. Highly recommended.
A lot of the episodes are free, but that revised Prawn episode is one of those that are only available with a paid subscription. At $9/month though, a subscription quickly pays for itself.
I find the best way to send a pdf to the client's browser is to put the download into a link. Often you need to generate a pdf after form submission, but also need to redirect to another page.
You can't redirect and send the pdf simultaneously, but you can redirect and then provide a download link, like so:
First add gem 'prawn' to your gemfile. Bundle. Then do the following:
Link to your special printing action in your view
<%= link_to 'print ticket', print_ticket_path %>
route to special printing action in routes.rb
match 'print_ticket', to: 'tickets#print_ticket'
action that sends the outputted file (change per your needs):
def print_ticket
if session[:token]
#pdf = generate_pdf(session[:token])
send_data(#pdf, :filename => "output.pdf", :type => "application/pdf")
end
end
private
def generate_pdf(token)
Prawn::Document.new do
formatted_text [ { :text=>"xxx.org", :styles => [:bold], :size => 30 } ]
move_down 20
text "Please proceed to the following web address:"
move_down 20
text "http://xxx.org/finder"
move_down 20
text "and enter this code:"
move_down 20
formatted_text [ { :text=>token, :styles => [:bold], :size => 20 } ]
end.render
end

Receiving errors when saving Tweets to a database using Sinatra

I'm using Sinatra, EventMachine, DataMapper, SQLite3 and the Twitter Stream API to capture and save tweets. When I run the application from my command line, it seems to continually fail at tweet 50. If I'm not saving the tweets, it can run seemingly forever.
Below is the app code to capture tweets with 'oscar' in them, which provided a very quick stream. Just enter your twitter username and password and run at the command line.
require 'rubygems'
require 'sinatra'
require 'em-http'
require 'json'
require 'dm-core'
require 'dm-migrations'
USERNAME = '<your twitter username>'
PASSWORD = '<your secret password>'
STREAMING_URL = 'http://stream.twitter.com/1/statuses/filter.json'
DataMapper.setup(:default, ENV['DATABASE_URL'] || "sqlite3://#{Dir.pwd}/db/development.db")
class Tweet
include DataMapper::Resource
property :id, Serial
property :tweet_id, String
property :username, String
property :avatar_url, String
property :text, Text
end
DataMapper.auto_upgrade!
get '/' do
#tweets = Tweet.all
erb :index
end
def rip_tweet(line)
#count += 1
tweet = Tweet.new :tweet_id => line['id'],
:username => line['user']['screen_name'],
:avatar_url => line['user']['profile_image_url'],
:text => line['text']
if tweet.save
puts #count
else
puts "F"
end
end
EM.schedule do
#count = 0
http = EM::HttpRequest.new(STREAMING_URL).get({
:head => {
'Authorization' => [ USERNAME, PASSWORD]
},
:query => {
'track' => 'oscars'
}
})
buffer = ""
http.stream do |chunk|
buffer += chunk
while line = buffer.slice!(/.+\r?\n/)
rip_tweet JSON.parse(line)
end
end
end
helpers do
alias_method :h, :escape_html
end
I'm not sure you can safely mix EM and Sinatra in the same process. You might want to try splitting the Sinatra viewer and the EventMachine downloader into separate programs and processes.

Sinatra/CouchDB error?

I'm working on my first Sinatra/CouchDB project and I'm getting an error I can't explain.
Here's my rackup (config.ru) file:
require 'rubygems'
require 'couchrest'
require 'patina'
set :environment, :development
set :root, File.dirname(__FILE__)
set :run, false
FileUtils.mkdir_p 'log' unless File.exists?('log')
log = File.new("log/sinatra.log", "a")
$stdout.reopen(log)
$stderr.reopen(log)
set :db, CouchRest.database!("http://127.0.0.1:5984/test")
run Sinatra::Application
And here's the app file (patina.rb):
require 'rubygems'
require 'sinatra'
require 'couchrest'
require 'haml'
class Article < CouchRest::ExtendedDocument
use_database settings.db
property :title
timestamps!
view_by :title
end
get '/' do
#db = settings.db
haml :index
end
Without the class definition in patina.rb, the route returns a page that displays the #db property as I was expecting. However, when I add the class definition to patina.rb I get "Ruby (Rack) application could not be started" error message.
Obviously this has something to do with my class definition, but I can't figure out what the problem is and the error message doesn't seem that helpful to me.
Also, I'd actually prefer to have the class definition in a separate file (Article.rb), but I can't figure out how to do that in the context of my Sinatra app.
Any help would be greatly appreciated!
EDIT:
See my answer below.
After a lot of googling, I discovered that the 1.4 series of json.gem are known to cause a lot of problems. I uninstalled all the json gems I had and installed json-1.2.4.gem instead. I have everything working correctly now. Here's the setup I'm using:
config.ru (Rackup file):
require 'application'
set :environment, :production
set :root, File.dirname(__FILE__)
set :run, false
FileUtils.mkmdir_p 'log' unless File.exists?('log')
log = File.new('log/sinatra.log', 'a+')
$stdout.reopen(log)
$stderr.reopen(log)
run Sinatra::Application
environment.rb:
require 'rubygems'
require 'couchrest'
require 'haml'
require 'ostruct'
require 'sinatra' unless defined?(Sinatra)
configure do
SiteConfig = OpenStruct.new(
:title => 'Application Title',
:author => 'Your Name',
:url_base => 'Your URL',
:url_base_db => 'Your CouchDB Server',
:db_name => "Your DB Name"
)
# load models
$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/lib")
Dir.glob("#{File.dirname(__FILE__)}/lib/*.rb") { |lib| require File.basename(lib, '.*') }
end
lib/contact.rb (Model example, models auto-loaded in environment.rb):
class Contact < CouchRest::ExtendedDocument
include CouchRest::Validation
use_database CouchRest.database!((SiteConfig.url_base_db || '') + SiteConfig.db_name)
property :name
timestamps!
view_by :name
end
application.rb:
require 'rubygems'
require 'sinatra'
require 'environment'
configure do
set :views, "./views"
end
error do
e = request.env['sinatra.error']
Kernel.puts e.backtrace.join("\n")
'Application error'
end
helpers do
end
get '/new/?' do
haml :new
end
post '/save/?' do
#contact_name = params[:contact_name]
#contact = Contact.new
#contact.name = #contact_name
#contact.save
haml :save
end
get '/' do
haml :index
end
Hope this helps someone in the future!
try requiring 'patina' after setting :db. I think the class body of Article is executing the use_database method before the setting exists.
you should be able to put Article in article.rb (ruby naming convention is UpperCamel for classes, but under_scores for the files in which classes are defined) and then require 'article' in patina.rb.
thats the only thing that stood out for me, so let me know if that works.

Resources