Resque with Rails 3 + Heroku + RedisToGo giving Postgresql error - ruby-on-rails-3

I'm writing an app which needs to send many emails and creates many user notifications because of these emails. This task produces a timeout in Heroku. To solve this, I decided to use Resque and RedistToGo.
What I did was to send the email (it's actually just one email because we use Sendgrid to handle this) and create the notifications using a Resque worker. The email is already created, so I send its id to the worker, along with all the recipients.
This works fine locally. In production, unless we restart our app in Heroku, it only works once. I will post some of my code and the error message:
#lib/tasks/resque.rake
require 'resque/tasks'
task "resque:setup" => :environment do
ENV['QUEUE'] = '*'
end
desc "Alias for resque:work (To run workers on Heroku)"
task "jobs:work" => "resque:work"
#config/initalizers/resque.rb
ENV["REDISTOGO_URL"] ||= "redis://redistogo:some_hash#some_url:some_number/"
uri = URI.parse(ENV["REDISTOGO_URL"])
Resque.redis = Redis.new(:host => uri.host, :port => uri.port, :password => uri.password)
Dir["#{Rails.root}/app/workers/*.rb"].each { |file| require file }
#app/workers/massive_email_sender.rb
class MassiveEmailSender
#queue = :massive_email_queue
def self.perform(email_id, recipients)
email = Email.find(email_id.to_i)
email.recipients = recipients
email.send_email
end
end
I've got an Email model which has an after_create that enqueues the worker:
class Email < ActiveRecord::Base
...
after_create :enqueue_email
def enqueue_email
Resque.enqueue(MassiveEmailSender, self.id, self.recipients)
end
...
end
This Email model also has the send_email method which does what I said before
I'm getting the following error message. I'm gonna post all the information Resque gives to me:
Worker
9dddd06a-2158-464a-b3d9-b2d16380afcf:1 on massive_email_queue at just now
Retry or Remove
Class
MassiveEmailSender
Arguments
21
["some_email_1#gmail.com", "some_email_2#gmail.com"]
Exception
ActiveRecord::StatementInvalid
Error
PG::Error: SSL error: decryption failed or bad record mac : SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull FROM pg_attribute a LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum WHERE a.attrelid = '"emails"'::regclass AND a.attnum > 0 AND NOT a.attisdropped ORDER BY a.attnum
/app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.2/lib/active_record/connection_adapters/postgresql_adapter.rb:1139:in `async_exec'
/app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.2/lib/active_record/connection_adapters/postgresql_adapter.rb:1139:in `exec_no_cache'
/app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.2/lib/active_record/connection_adapters/postgresql_adapter.rb:663:in `block in exec_query'
/app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.2/lib/active_record/connection_adapters/abstract_adapter.rb:280:in `block in log'
/app/vendor/bundle/ruby/1.9.1/gems/activesupport-3.2.2/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
/app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.2/lib/active_record/connection_adapters/abstract_adapter.rb:275:in `log'
/app/vendor/bundle/ruby/1.9.1/gems/newrelic_rpm-3.3.2/lib/new_relic/agent/instrumentation/active_record.rb:31:in `block in log_with_newrelic_instrumentation'
/app/vendor/bundle/ruby/1.9.1/gems/newrelic_rpm-3.3.2/lib/new_relic/agent/method_tracer.rb:242:in `trace_execution_scoped'
/app/vendor/bundle/ruby/1.9.1/gems/newrelic_rpm-3.3.2/lib/new_relic/agent/instrumentation/active_record.rb:28:in `log_with_newrelic_instrumentation'
/app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.2/lib/active_record/connection_adapters/postgresql_adapter.rb:662:in `exec_query'
/app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.2/lib/active_record/connection_adapters/postgresql_adapter.rb:1264:in `column_definitions'
/app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.2/lib/active_record/connection_adapters/postgresql_adapter.rb:858:in `columns'
/app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.2/lib/active_record/connection_adapters/schema_cache.rb:12:in `block in initialize'
/app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.2/lib/active_record/model_schema.rb:228:in `yield'
/app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.2/lib/active_record/model_schema.rb:228:in `default'
/app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.2/lib/active_record/model_schema.rb:228:in `columns'
/app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.2/lib/active_record/model_schema.rb:237:in `columns_hash'
/app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.2/lib/active_record/relation/delegation.rb:7:in `columns_hash'
/app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.2/lib/active_record/relation/finder_methods.rb:330:in `find_one'
/app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.2/lib/active_record/relation/finder_methods.rb:311:in `find_with_ids'
/app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.2/lib/active_record/relation/finder_methods.rb:107:in `find'
/app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.2/lib/active_record/querying.rb:5:in `find'
/app/app/workers/massive_email_sender.rb:5:in `perform'
According to this, the first argument is the email id, and the second one is the list of all recipients... exactly as it should be.
Can anyone help me? Thanks!

I've run into the same problem. Assuming you're using Active Record you have to call ActiveRecord::Base.establish_connection for each forked Resque worker to make sure it doesn't have a stale database connection. Try putting this in your lib/tasks/resque.rake
task "resque:setup" => :environment do
ENV['QUEUE'] = '*'
Resque.after_fork = Proc.new { ActiveRecord::Base.establish_connection }
end

Related

error running a scrape command ruby on rails

I am trying to re-setup my app on a new computer and run a scrape to build the DB. When I run my first rake scraper:scrape, this is the error I am getting. I am not sure why I am getting this error any help would make my day.. cheers!
Art West#ARTWESTIV ~/desktop/duckduckjeep-master
$ rake scraper:scrape --trace
** Invoke scraper:scrape (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute scraper:scrape
rake aborted!
NoMethodError: undefined method `value' for nil:NilClass
c:/Users/Art West/desktop/duckduckjeep-master/lib/tasks/scraper.rake:17:in `block (3 levels) in <top (required)>'
c:/Users/Art West/desktop/duckduckjeep-master/lib/tasks/scraper.rake:12:in `loop'
c:/Users/Art West/desktop/duckduckjeep-master/lib/tasks/scraper.rake:12:in `block (2 levels) in <top (required)>'
c:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/rake-10.4.2/lib/rake/task.rb:240:in `call'
c:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/rake-10.4.2/lib/rake/task.rb:240:in `block in execute'
c:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/rake-10.4.2/lib/rake/task.rb:235:in `each'
c:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/rake-10.4.2/lib/rake/task.rb:235:in `execute'
c:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/rake-10.4.2/lib/rake/task.rb:179:in `block in invoke_with_call_chain'
c:/RailsInstaller/Ruby2.1.0/lib/ruby/2.1.0/monitor.rb:211:in `mon_synchronize'
c:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/rake-10.4.2/lib/rake/task.rb:172:in `invoke_with_call_chain'
c:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/rake-10.4.2/lib/rake/task.rb:165:in `invoke'
c:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/rake-10.4.2/lib/rake/application.rb:150:in `invoke_task'
c:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/rake-10.4.2/lib/rake/application.rb:106:in `block (2 levels) in top_level'
c:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/rake-10.4.2/lib/rake/application.rb:106:in `each'
c:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/rake-10.4.2/lib/rake/application.rb:106:in `block in top_level'
c:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/rake-10.4.2/lib/rake/application.rb:115:in `run_with_threads'
c:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/rake-10.4.2/lib/rake/application.rb:100:in `top_level'
c:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/rake-10.4.2/lib/rake/application.rb:78:in `block in run'
c:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/rake-10.4.2/lib/rake/application.rb:176:in `standard_exception_handling'
c:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/rake-10.4.2/lib/rake/application.rb:75:in `run'
c:/RailsInstaller/Ruby2.1.0/lib/ruby/gems/2.1.0/gems/rake-10.4.2/bin/rake:33:in `<top (required)>'
c:/RailsInstaller/Ruby2.1.0/bin/rake:23:in `load'
c:/RailsInstaller/Ruby2.1.0/bin/rake:23:in `<main>'
Tasks: TOP => scraper:scrape
here is my scraper.rake
namespace :scraper do
desc "Fetch Craigslist posts from 3Taps"
task scrape: :environment do
require 'open-uri'
require 'json'
# Set API token and URL
auth_token = "b077632d17da8857e2fa92c053115e43"
polling_url = "http://polling.3taps.com/poll"
# Grab data until up-to-date
loop do
# Specify request parameters
params = {
auth_token: auth_token,
anchor: Anchor.first.value,
source:"CRAIG",
category_group: "VVVV",
category: "VAUT",
'location.country' => "USA",
retvals: "location,external_url,heading,body,timestamp,price,images,annotations"
}
# Prepare API request
uri = URI.parse(polling_url)
uri.query = URI.encode_www_form(params)
# Submit request
result = JSON.parse(open(uri).read)
# Display results to screen
#puts result["postings"].first["annotations"]["year"]
Anchor.first.update(value: result["anchor"])
puts Anchor.first.value
break if result["postings"].empty?
# #store results in Database
result["postings"].each do |posting|
#ADD HARD FILTER (IN PROGRESS....)
if posting["annotations"]["make"] == "Jeep"
#create new post
#post= Post.new
#post.heading = posting["heading"]
#post.body = posting["body"]
#post.price = posting["price"]
#post.neighborhood = posting["location"]["locality"]
#post.external_url = posting["external_url"]
#post.timestamp = posting["timestamp"]
#post.year = posting ["annotations"]["year"] if posting ["annotations"]["year"].present?
#post.make = posting ["annotations"]["make"] if posting ["annotations"]["make"].present?
#post.model = posting ["annotations"]["model"] if posting ["annotations"]["model"].present?
#post.title_status = posting ["annotations"]["title_status"] if posting ["annotations"]["title_status"].present?
#post.transmission = posting ["annotations"]["transmission"] if posting ["annotations"]["transmission"].present?
#post.mileage = posting ["annotations"]["mileage"] if posting ["annotations"]["mileage"].present?
#post.source_account = posting ["annotations"]["source_account"] if posting ["annotations"]["source_account"].present?
#post.phone = posting ["annotations"]["phone"] if posting ["annotations"]["phone"].present?
#post.lat = posting["location"]["lat"]
#post.lng = posting["location"]["long"]
#post.zipcode = posting["location"]["zipcode"]
#Save Post
#post.save
# Loop over images and save to Image database
posting["images"].each do |image|
#image = Image.new
#image.url = image["full"]
#image.post_id = #post.id
#image.save
end
end
end
end
end
desc "Destroy All Posting Data"
task destroy_all_posts: :environment do
Post.destroy_all
end
desc "Save neighborhood codes in a reference table"
task scrape_neighborhoods: :environment do
require 'open-uri'
require 'json'
# Set API token and URL
auth_token = "b077632d17da8857e2fa92c053115e43"
location_url = "http://reference.3taps.com/locations"
# Specify request parameters
params = {
auth_token: auth_token,
level: "locality",
country: "USA"
}
# Prepare API request
uri = URI.parse(location_url)
uri.query = URI.encode_www_form(params)
# Submit request
result = JSON.parse(open(uri).read)
# Display results to screen
# puts JSON.pretty_generate result
# Store results in database
result["locations"].each do |location|
#location = Location.new
#location.code = location["code"]
#location.name = location["short_name"]
#location.save
end
end
desc "Discard old data"
task discard_old_data: :environment do
Post.all.each do |post|
if post.created_at < 72.hours.ago
post.destoy
end
end
end
end
Your error message is saying it all:
NoMethodError: undefined method `value' for nil:NilClass
You are trying to call value on nil at some point. And, this is probably happening here:
# Specify request parameters
params = {
auth_token: auth_token,
anchor: Anchor.first.value, # this is the line that's creating problem
source:"CRAIG",
category_group: "VVVV",
category: "VAUT",
'location.country' => "USA",
retvals: "location,external_url,heading,body,timestamp,price,images,annotations"
}
When you call this: anchor: Anchor.first.value for the very first time, your Anchor.first is nil and so you are trying to call: nil.value and that's where your rake task fails with the specified error message.
Make sure you have populated the database table (anchors) so that when you call Anchor.first, you don't get nil.
Another approach to avoid this issue would be to use try:
anchor: Anchor.first.try(:value)
That way, your rake task won't fail even if Anchor.first returns nil.

Getting error trying to start rails server on Vagrant vm

below is the console error message I get when trying to run "rails s" on a vagrant VM. It exits with all this error message, first line being
/home/vagrant/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/net/http.rb:878:in `initialize': Connection refused - connect(2) (Errno::ECONNREFUSED)
Not sure what is going on here ...
Thank you so much
config\initializers\tire.rb
subdomain = ""
if Rails.env.alpha?
subdomain = ".alpha."
end
if Rails.env.development?
Tire.configure { logger 'log/elasticsearch_development.log', :level => 'debug' }
else
Tire.configure { logger "log/elasticsearch.#{Rails.env.to_s.downcase}.log" }
end
prefix = "org.#{Rails.application.class.parent_name.downcase}#{subdomain}_#{Rails.env.to_s.downcase}"
Tire::Model::Search.index_prefix(prefix)
def get_indices_for_env(prefix)
aliases = Tire::Configuration.client.get(Tire::Configuration.url + '/_aliases').body
global_indices = MultiJson.load(aliases).keys
all_indices = global_indices.select do |index|
index.start_with? prefix
end
all_indices.freeze
end
ALL_INDICES = get_indices_for_env(prefix)
error message
vagrant#precise32:/vagrant$ bundle exec rails s
=> Booting WEBrick
=> Rails 3.2.17 application starting in development on http://x.x.x.x:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server Exiting /home/vagrant/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/net/http.rb:878:in
initialize': Connection refused - connect(2) (Errno::ECONNREFUSED)
from /home/vagrant/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/net/http.rb:878:in
open'
from /home/vagrant/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/net/http.rb:878:in
block in connect'
from /home/vagrant/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/timeout.rb:52:in
timeout'
from /home/vagrant/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/net/http.rb:877:in
connect'
from /home/vagrant/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/net/http.rb:862:in
do_start'
from /home/vagrant/.rvm/rubies/ruby-2.0.0-p247/lib/ruby/2.0.0/net/http.rb:851:in
start'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/rest-client-1.6.7/lib/restclient/request.rb:172:in
transmit'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/rest-client-1.6.7/lib/restclient/request.rb:64:in
execute'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/tire-0.6.2/lib/tire/http/client.rb:11:in
get'
from /vagrant/config/initializers/tire.rb:20:in get_indices_for_env'
from /vagrant/config/initializers/tire.rb:29:in'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-3.2.17/lib/active_support/dependencies.rb:245:in
load'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-3.2.17/lib/active_support/dependencies.rb:245:in
block in load'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-3.2.17/lib/active_support/dependencies.rb:236:in
load_dependency'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-3.2.17/lib/active_support/dependencies.rb:245:in
load'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/railties-3.2.17/lib/rails/engine.rb:593:in
block (2 levels) in <class:Engine>'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/railties-3.2.17/lib/rails/engine.rb:592:in
each'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/railties-3.2.17/lib/rails/engine.rb:592:in
block in <class:Engine>'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/railties-3.2.17/lib/rails/initializable.rb:30:in
instance_exec'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/railties-3.2.17/lib/rails/initializable.rb:30:in
run'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/railties-3.2.17/lib/rails/initializable.rb:55:in
block in run_initializers'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/railties-3.2.17/lib/rails/initializable.rb:54:in
each'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/railties-3.2.17/lib/rails/initializable.rb:54:in
run_initializers'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/railties-3.2.17/lib/rails/application.rb:136:in
initialize!'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/railties-3.2.17/lib/rails/railtie/configurable.rb:30:in
method_missing'
from /vagrant/config/environment.rb:5:in <top (required)>'
from /vagrant/config.ru:3:inrequire'
from /vagrant/config.ru:3:in block in <main>'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/rack-1.4.5/lib/rack/builder.rb:51:in
instance_eval'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/rack-1.4.5/lib/rack/builder.rb:51:in
initialize'
from /vagrant/config.ru:innew'
from /vagrant/config.ru:in <main>'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/rack-1.4.5/lib/rack/builder.rb:40:in
eval'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/rack-1.4.5/lib/rack/builder.rb:40:in
parse_file'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/rack-1.4.5/lib/rack/server.rb:200:in
app'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/railties-3.2.17/lib/rails/commands/server.rb:46:in
app'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/rack-1.4.5/lib/rack/server.rb:304:in
wrapped_app'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/rack-1.4.5/lib/rack/server.rb:254:in
start'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/railties-3.2.17/lib/rails/commands/server.rb:70:in
start'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/railties-3.2.17/lib/rails/commands.rb:55:in
block in <top (required)>'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/railties-3.2.17/lib/rails/commands.rb:50:in
tap'
from /home/vagrant/.rvm/gems/ruby-2.0.0-p247/gems/railties-3.2.17/lib/rails/commands.rb:50:in
<top (required)>'
from script/rails:6:inrequire'
from script/rails:6:in `'
Like house9 commented above, I basically commented out the entire ruby file that was causing me the problems.
And then I had a problem starting the Rails server because it couldn't find a database. So it threw an ActiveRecord error:
ActiveRecord::StatementInvalid in PostsController#homepage
Mysql2::Error: Unknown column 'posts.status' in 'where clause': SELECT
posts.* FROM posts WHERE posts.status = 'PUBLISHED' AND
posts.post_type = 'About' AND (title LIKE 'About Ourgoods%') ORDER BY
created_at DESC LIMIT 1....
After connecting a .sql file that I had to the Rails app (following instructions from: How do I connect a mysql database file to a local ruby on rails application), and running rake db:migrate, I finally got rails s working!

Rspec testing API with rack protection

I am trying to test my API with an rspec integration(request) test.
I go to my api endpoint at 0.0.0.0:3000/api/regions in a browser and it returns my data, I get a session id and looks like everything is working.
I am using rack protection in my API:
module Api
class Root < Grape::API
use Rack::Protection, instrumenter: ActiveSupport::Notifications
use Rack::Protection::AuthenticityToken
prefix 'api'
helpers do
def warden
request.env['warden']
end
end
version 'v1', using: :header, vendor: 'vendor', strict: false do
mount Resources::V1::Search => '/'
mount Resources::V1::Regions => '/'
end
end
end
The api resource:
module Resources
module V1
class Regions < Grape::API
resource :regions do
get do
# Some code...
end
end
end
end
end
spec_helper.rb
[...]
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
config.include Rack::Test::Methods, type: :request
config.include Module.new { def app; Api::Root; end }, type: :request
config.include Warden::Test::Helpers
# ## Mock Framework
#
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
#
# config.mock_with :mocha
# config.mock_with :flexmock
# config.mock_with :rr
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of
# rspec-rails.
config.infer_base_class_for_anonymous_controllers = false
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = "random"
end
Here is a test:
describe Resources::V1::Regions do
describe 'GET /regions' do
it 'returns some data' do
get '/api/regions'
[... expectations - doesn't get here...]
end
end
end
Here is the error:
RuntimeError:
you need to set up a session middleware *before* Rack::Protection::RemoteToken
# ./spec/requests/api/region_spec.rb:6:in `block (3 levels) in <top (required)>'
Which comes from here: https://github.com/rkh/rack-protection/blob/master/lib/rack/protection/base.rb#L85
So I need to add rack session middleware, right?
I add use Rack::Session::Cookie, secret: '...' to my api which gets me to request.env['warden'] being nil (another question another time).
Now, however, when I load the endpoint with the browser I get:
undefined method `each' for #<ActionDispatch::Request::Session:0x7f7bf9e521e0 not yet loaded>
which raises over here: https://github.com/rack/rack/blob/master/lib/rack/session/abstract/id.rb#L158
I suspect I don't need to use Rack::Session::Cookie, as something else is doing it when loaded by the server, but I need to add something for the tests to work. No idea what that something is.
Please let me know if you need any other info.
Versions:
grape (0.6.1)
rails (4.0.2)
rack-protection (1.5.2)
rspec (2.14.1)
I was solving this by adding a 'rack.session' hash to the 3rd argument of my request i.e get '/api/regions', {}, {'rack.session' => {}}
But I found a better way: https://github.com/heavysixer/grape-on-rack/pull/1
which adds sessions and solves the warden issue at the same time.
RSpec.configure do |config|
[...]
rack_app = Module.new do
def app
#app ||= Rack::Builder.new do
use Rack::Session::Cookie
Warden::Manager.serialize_into_session { |user| user.id }
Warden::Manager.serialize_from_session { |id| User.get(id) }
use Warden::Manager do |manager|
manager.default_strategies :password, :basic
# manager.failure_app = Acme::BadAuthentication
end
run Api::Root
end
end
end
config.include rack_app, type: :request
end
Marking as answer unless anyone has anything better.

Rails 3 functional tests: Can't mass-assign protected attributes: controller, action

When running functional tests on my controller code in a Rails 3 project, I have a fatal error; the params variable contains controller and action, and ActiveModel is not happy about it:
ActiveModel::MassAssignmentSecurity::Error: Can't mass-assign protected attributes: controller, action
/Users/phooze/.rvm/gems/ruby-1.9.3-p0/gems/activemodel-3.2.1/lib/active_model/mass_assignment_security/sanitizer.rb:48:in `process_removed_attributes'
/Users/phooze/.rvm/gems/ruby-1.9.3-p0/gems/activemodel-3.2.1/lib/active_model/mass_assignment_security/sanitizer.rb:20:in `debug_protected_attribute_removal'
/Users/phooze/.rvm/gems/ruby-1.9.3-p0/gems/activemodel-3.2.1/lib/active_model/mass_assignment_security/sanitizer.rb:12:in `sanitize'
/Users/phooze/.rvm/gems/ruby-1.9.3-p0/gems/activemodel-3.2.1/lib/active_model/mass_assignment_security.rb:228:in `sanitize_for_mass_assignment'
/Users/phooze/.rvm/gems/ruby-1.9.3-p0/gems/activerecord-3.2.1/lib/active_record/attribute_assignment.rb:75:in `assign_attributes'
/Users/phooze/.rvm/gems/ruby-1.9.3-p0/gems/activerecord-3.2.1/lib/active_record/base.rb:495:in `initialize'
/Users/phooze/Documents/rails-app/app/controllers/credentials_controller.rb:40:in `new'
The application call is to the "new" method (where the error is occurring), the code is:
# Credential#create (POST)
def create
#credential = Credential.new(params)
# ... controller continues
end
Finally, my test case:
test "should create credential" do
assert_difference('Credential.count', 1) do
post :create, { :fid => "foobarbaz", :credentials_hash => "f00ba7f00ba7", :uid => "10023", :cid => "342" }
end
assert_response :created
end
Changing my controller code to a "separate" parameter hash containing ONLY the fid, credentials_hash, uid, and cid makes it work. I'm pretty sure Rails is trying to be "nice" and provide me with addtional values for testing, but it seems to be causing problems.
Any recommendations on how to solve this?
Looks like you have set config.active_record.mass_assignment_sanitizer = :strict
in your test environment only, but not in development or production, because params always contains controller and action, in any environment.
I think the best-practice recommendation here is to always use form_for, so that you'd have your credentials in params[:credential], or, indeed, do params.slice(:fid, :uid, etc).

Rails 3: How to render ERb template in rake task?

I am rendering a pretty huge sitemap HTML file with rake. Unfortunately, the code breaks when I migrate to rails 3. My current code looks like this:
#controller = ActionController::Base.new
#controller.request = ActionController::TestRequest.new
#controller.instance_eval do
#url = ActionController::UrlRewriter.new(request, {})
end
# collect data, open output file file
template = ERB.new(IO.read("#{RAILS_ROOT}/app/views/sitemap/index.html.erb"))
f.puts(template.result(binding))
This code worked in 2.3, but breaks in Rails 3 as url_for does not access #controller anymore, but controller. (I think that's why.)
undefined local variable or method `controller' for #<Object:0x3794c>
/opt/local/lib/ruby/gems/1.8/gems/actionpack-3.0.1/lib/action_view/helpers/url_helper.rb:31:in `url_options'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-3.0.1/lib/action_dispatch/routing/url_for.rb:132:in `url_for'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-3.0.1/lib/action_view/helpers/url_helper.rb:99:in `url_for'
(erb):5
/Users/me/Documents/Projects/zvg2/lib/tasks/zvg.rake:452
I also tried creating an ActionView to do it like that:
av = ActionView::Base.new(Rails::Application::Configuration.new(Rails.root).view_path, {
# my assigns
}, #controller)
av.class_eval do
include ApplicationHelper
end
f.puts(av.render(:template => "sitemap/index.html"))
But the problem seems to be the same, although ActionView::Base.new takes my controller.
undefined local variable or method `controller' for nil:NilClass
/opt/local/lib/ruby/gems/1.8/gems/activesupport-3.0.1/lib/active_support/whiny_nil.rb:48:in `method_missing'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-3.0.1/lib/action_view/helpers/url_helper.rb:31:in `url_options'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-3.0.1/lib/action_dispatch/routing/url_for.rb:132:in `url_for'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-3.0.1/lib/action_view/helpers/url_helper.rb:99:in `url_for'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-3.0.1/lib/action_dispatch/routing/url_for.rb:132:in `url_for'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-3.0.1/lib/action_view/helpers/url_helper.rb:99:in `url_for'
/Users/me/Documents/Projects/zvg2/app/views/sitemap/index.html.erb:5:in `_app_views_sitemap_index_html_erb__757224102_30745030_0'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-3.0.1/lib/action_view/template.rb:135:in `send'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-3.0.1/lib/action_view/template.rb:135:in `render'
/opt/local/lib/ruby/gems/1.8/gems/activesupport-3.0.1/lib/active_support/notifications.rb:54:in `instrument'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-3.0.1/lib/action_view/template.rb:127:in `render'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-3.0.1/lib/action_view/render/rendering.rb:59:in `_render_template'
/opt/local/lib/ruby/gems/1.8/gems/activesupport-3.0.1/lib/active_support/notifications.rb:52:in `instrument'
/opt/local/lib/ruby/gems/1.8/gems/activesupport-3.0.1/lib/active_support/notifications/instrumenter.rb:21:in `instrument'
/opt/local/lib/ruby/gems/1.8/gems/activesupport-3.0.1/lib/active_support/notifications.rb:52:in `instrument'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-3.0.1/lib/action_view/render/rendering.rb:56:in `_render_template'
/opt/local/lib/ruby/gems/1.8/gems/actionpack-3.0.1/lib/action_view/render/rendering.rb:26:in `render'
/Users/me/Documents/Projects/zvg2/lib/tasks/zvg.rake:450
What's the best practice for rendering a template with rake that uses url_for and link_to? I bet as Rails just got more modular, there should be an easy way for just this?
Thanks in advance! Jan
One of the approaches works when I include Rails.application.routes.url_helpers instead of ActionView::Helpers::UrlHelper. This works for now:
include Rails.application.routes.url_helpers # brings ActionDispatch::Routing::UrlFor
include ActionView::Helpers::TagHelper
av = ActionView::Base.new(Rails.root.join('app', 'views'))
av.assign({
:regions => #regions,
:courts_by_region => #courts_by_region,
:cities_by_region => #cities_by_region,
:districts_by_region => #districts_by_region
})
f = File.new(file_name, 'w')
f.puts(av.render(:template => "sitemap/index.html"))
f.close
Hope this helps others. If there is a better solution, I'd be interested.
Also, how do I automatically get a hash of assigns from binding?
I was successfull by doing something like this:
class Template < ActionView::Base
include Rails.application.routes.url_helpers
include ActionView::Helpers::TagHelper
def default_url_options
{host: 'yourhost.org'}
end
end
template = Template.new(Rails.root.join('app', 'views'))
template.assign(attendee: #attendee)
template.render(template: 'sitemap/index.html.erb')
This is what worked for me (Rails 3):
class TaskActionView < ActionView::Base
include Rails.application.routes.url_helpers
include ApplicationHelper
def default_url_options
{host: 'example.com'}
end
end
def action_view
controller = ActionController::Base.new
controller.request = ActionDispatch::TestRequest.new
TaskActionView.new(Rails.root.join('app', 'views'), {}, controller)
end
action_view.render(:template =>"path/to/template")
This successfully included my application helper, tag helpers, and url helpers. You'll want to put your application/environments host properly into the default_url_options hash.
This is what worked for me:
html = render_to_string(:index, layout: nil)