Ruby Rails Cucumber Troubleshooting - ruby

I am trying to build an app with Cucumber for the first time, using Rails 5.2.6, Rspec, Capybara and Factory bot.
I successfully got through my first feature with scenarios for authentication with devise.
UPDATE
Through a series of troubleshooting steps, detailed below, the problem is that the controller collection #cocktails somehow isn't passing to the view ONLY in the cucumber test.
With the rails server, it passes with no problem.
I checked and #cocktails only appears on the controller and the view. So its not getting overwritten or erased, at least directly.
Its working in the unit RSpec test and also rails server.
How it is not getting passed in the cucumber test? Can anyone see why it wouldn't pass from the controller to the test?
But I hit a snag in my second feature file for CRUD functionality. The very first given step uses FactoryBot to create 2 items, log in the user and go straight to the index for just those two items. But the page shows the view but not the 2 created items as confirmed by using:
puts page.body
in the cucumber file
When I create them on the actual application, it functions correctly, goes straight to the index and displays them.
So I'm trying to figure out how to troubleshoot this. My first thought is to find a way to confirm that FactoryBot created the 2 items. My second thought is to confirm they are actually set to the user. I have tried to use puts to display the two created objects or the user, but I haven't figured out how to call upon them within cucumber.
This is the step file:
Given('I have populated a cocktail list for this User') do
FactoryBot.create(:cocktail,
:user => #registered_user,
:name => "Frank Wallbanger",
:ingredients => "Lots of Booze, a pinch of lime")
FactoryBot.create(:cocktail,
:user => #registered_user,
:name => "Fuzzy Naval Orange",
:ingredients => "Lots of Tequila, a pinch of orange")
end
When('I visit the website and log in') do
expect(page).to have_content("Signed in successfully")
end
Then('I will see the cocktail list') do
puts page.body
expect(page).to have_content("Frank Wallbanger")
expect(page).to have_content("Fuzzy Naval Orange")
end
This is my RSpec unit test file and its green
require "rails_helper"
RSpec.describe CocktailsController do
let(:user) { instance_double(User) }
before { log_in(user) }
describe "GET #index" do
let(:cocktails) { [
instance_double(Cocktail),
instance_double(Cocktail)
] }
before do
allow(user).to receive(:cocktails).and_return(cocktails)
get :index
end
it "looks up all cocktails that belong to the current user" do
expect(assigns(:cocktails)).to eq(cocktails)
end
end
end
This is rails_helper.rb
# This file is copied to spec/ when you run 'rails generate rspec:install'
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../config/environment', __dir__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'
# Add additional requires below this line. Rails is not loaded until this point!
# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
# run as spec files by default. This means that files in spec/support that end
# in _spec.rb will both be required and run as specs, causing the specs to be
# run twice. It is recommended that you do not name files matching this glob to
# end with _spec.rb. You can configure this pattern with the --pattern
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
#
# The following line is provided for convenience purposes. It has the downside
# of increasing the boot-up time by auto-requiring all files in the support
# directory. Alternatively, in the individual `*_spec.rb` files, manually
# require only the support files necessary.
#
# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }
# Checks for pending migrations and applies them before tests are run.
# If you are not using ActiveRecord, you can remove these lines.
begin
ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
puts e.to_s.strip
exit 1
end
RSpec.configure do |config|
# 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
# You can uncomment this line to turn off ActiveRecord support entirely.
# config.use_active_record = false
# RSpec Rails can automatically mix in different behaviours to your tests
# based on their file location, for example enabling you to call `get` and
# `post` in specs under `spec/controllers`.
#
# You can disable this behaviour by removing the line below, and instead
# explicitly tag your specs with their type, e.g.:
#
# RSpec.describe UsersController, type: :controller do
# # ...
# end
#
# The different available types are documented in the features, such as in
# https://relishapp.com/rspec/rspec-rails/docs
config.infer_spec_type_from_file_location!
# Filter lines from Rails gems in backtraces.
config.filter_rails_from_backtrace!
# arbitrary gems may also be filtered via:
# config.filter_gems_from_backtrace("gem name")
end
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :active_record
with.library :active_model
with.library :action_controller
with.library :rails
end
end
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
config.around(:each) do |example|
DatabaseCleaner.cleaning do
example.run
end
end
end
require "support/controller_helpers"
RSpec.configure do |config|
config.include Warden::Test::Helpers
config.include Devise::Test::ControllerHelpers, :type => :controller
config.include ControllerHelpers, :type => :controller
end
This is the controller I'm testing:
class CocktailsController < ApplicationController
before_action :set_cocktail, only: %i[ show edit update destroy ]
# GET /cocktails or /cocktails.json
def index
#cocktails = current_user.cocktails
end
# GET /cocktails/1 or /cocktails/1.json
def show
end
# GET /cocktails/new
def new
#cocktail = Cocktail.new
render :new
end
# GET /cocktails/1/edit
def edit
end
# POST /cocktails or /cocktails.json
def create
#cocktail = Cocktail.new cocktail_params.merge(user: current_user)
if #cocktail.save
redirect_to cocktails_path
else
render :new
end
end
# PATCH/PUT /cocktails/1 or /cocktails/1.json
def update
respond_to do |format|
if #cocktail.update(cocktail_params)
format.html { redirect_to #cocktail, notice: "Cocktail was successfully updated." }
format.json { render :show, status: :ok, location: #cocktail }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #cocktail.errors, status: :unprocessable_entity }
end
end
end
# DELETE /cocktails/1 or /cocktails/1.json
def destroy
#cocktail.destroy
respond_to do |format|
format.html { redirect_to cocktails_url, notice: "Cocktail was successfully destroyed." }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_cocktail
#cocktail = Cocktail.find(params[:id])
end
# Only allow a list of trusted parameters through.
def cocktail_params
params.require(:cocktail).permit(:name, :ingredients, :user)
end
end
I'm totally new to this so if there are other files you need to see let me know or if I put a block in a bad area.
At the minimum, I'm just looking for how to display the values. The user login runs with the previous steps file where #registered_user was created and it seems to persist with the second step that confirms "Signed in successfully". So I'm down to the items not created or not assigned to the user as my most likely suspects.
Thanks
Here is the index.html.erb view
<h1>Cocktails</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Ingredients</th>
<th>User</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #cocktails.each do |cocktail| %>
<tr>
<td><%= cocktail.name %></td>
<td><%= cocktail.ingredients %></td>
<td><%= cocktail.user_id %></td>
<td><%= link_to 'Show', cocktail %></td>
<td><%= link_to 'Edit', edit_cocktail_path(cocktail) %></td>
<td><%= link_to 'Destroy', cocktail, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Cocktail', new_cocktail_path %>
<%= button_to 'Log out', destroy_user_session_path, :method => :delete %>
So, after Lam's suggestion below, I tried this attempt to verify.
I went rails console, and tried to run that first FactoryBot.create from the step file. That produced an error that the user #registered_user doesn't exist. Which makes sense in the console. So I can't verify that. BUT, if I created #registered_user from an existing user, that snippet successfully reacted the Frank Wallbanger cocktail. So it seems the FactoryBot is functional. So perhaps is the #registered_user that is the problem?
So I added "puts #registered_user" to the steps file and it did print an active record to the cucumber output.
When I run the rails server and display that view, I see the cocktails listed out. But its still blank in that area of the cucumber output from the "puts page.body" line in the steps file.
UPDATE
Ok, I think I zeroed down to the cause. But not the fix.
In the controller index
#cocktails = current_user.cocktails
Is working on the rails server, but it is NOT populated in the cucumber test.
I added this code to the view
<% if #cocktails.any? %>
<%= #cocktails.first.name %>
<% else %>
<p> none </p>
<% end %>
So why would it not be passing? I tried changing the controller to:
#cocktails = Cocktail.all
And that also doesn't work.

Ok, I finally got it.
I needed to put add visit root_path on the Then step of the step file. And it finally all cleared up.
Then('I will see the cocktail list') do
visit root_path
expect(page).to have_content("Frank Wallbanger")
expect(page).to have_content("Fuzzy Naval Orange")
end
I need a beer.....

You should avoid using Factory Bot when cuking. A better pattern to use with Rails follows.
Have the create and update actions of your controller call a service rather than call Active Record directly.
Your services take new model objects and save or update them. They return a Result hash which contains the following keys [success, model]
If your service succeeds {success: true, model: model}
If your service fails {success: false, model: model(with errrors),... }
Your services create a place for you to deal with business logic that occurs when you create an event e.g. send emails, audit the event, background long running processes etc. etc.
Now you can create things quickly in your Cuking by
a) Initialising a suitable model object with params
b) Calling the service and passing in the model
Ideally you would do this in helper methods that are called in your step defs.
This replaces FactoryBot and at the same time ensures that all the objects you create are created correctly with the same business logic that your application uses. Its very fast (a bit faster than factory bot and way faster than using the UI). It avoids you having to duplicate business logic by configuring FactoryBot to match your applications business logic. And finally it provides far more flexibility in applying business logic than relying solely on ActiveModel callbacks.
This gives you a pattern for CRUD. When you start with something new you will have
Scenario: Create a foo
Given ...
When I create a new foo
Then I should have a new foo
This will drive the development of the model, controller, controller#new, view#new, form, controller#create, controller#show view#show
Once you have these and have followed the pattern above you can then use foo in your givens e.g
Given there is a foo
When I bar with my foo
...
and you can create the foo with no user interaction and no need for Factories.
e.g
module FooSH
def create_foo(foo: )
Result = CreateFooService.new(foo: foo).call
Result.model
end
def create_default_foo
Result = CreateFooService.new(foo: Foo.new(default_foo_params).call
Result.model
end
def default_foo_params
{
}
end
end
World FooSH
and then your step
Given 'there is a foo' do
#foo = create_default_foo
end
You can apply your own styles and patterns to this and expand on object creation in a number of ways. You can see an example of this in this rather old repo https://github.com/diabolo/cuke_up

Related

rails buttons click counter

This is another I'm-totally-new-to-Ruby-please-have-mercy situation.
So i'm trying to figure out how to make a database of all my buttons to save the click count each time they're clicked. I started a new rails to try it out and generated a model Buttonand a controller buttons index
route.rbs
Rails.application.routes.draw do
resources :buttons
root 'buttons#index'
end
migration
class CreateButtons < ActiveRecord::Migration[5.0]
def change
create_table :buttons do |t|
t.integer :clicks
t.timestamps
end
end
end
buttons_controller
class ButtonsController < ApplicationController
def index
#button = Button.find(1)
end
def doit
#button = Button.find(1)
#newcount = #button.clicks + 1
Button.find(1).update_attributes(:clicks => #newcount)
end
end
Now.. i need to trigger the doit method.. is it possible to trigger a non CRUD operation ?
i tried this but it doesn't seem to work
index.html.erb
<h1>Hello, This is button and my click are :</h1>
<h1><%= #button.clicks %></h1>
<%= link_to 'click me', method: :doit %>
I know there's something I'm not getting here...
Ruby have been doing so much magic that I can't do a simple ruby method.. it have been really hard for me getting the part were methods are taking place without calling them by name..
Specially when I trigger a delete method and the destroy method is triggered by that.. I really need to get used to this too-much-magic coding
Several things to improve, I think. Please get back to me if something is not working (I did not run the code)
Make your index action list all the buttons
Controller:
def index
#buttons = Button.all
end
View:
<h1>These are all my buttons</h1>
<% #buttons.each do |button| %>
<%= link_to("Button #{button.id}", button_votes_path(button), method: :post) %>
<% end %>
It's common to have index show a list of resources.
Only create the routes you need, make increment a separate action
I'd chose to call it "vote". You could also call it "clicks" or "presses" or whatever.
resources :buttons, only: [:index] do
resources :votes, only: [:create]
end
Add the votes controller
class VotesController < ApplicationController
def create
button = Button.find(params[:id])
button.clicks += 1
button.save
redirect_to buttons_path
end
end
No error handling here. So this is just to get you started.
For the next steps I suggest you follow a tutorial or start with simpler stuff.

How to map routes to controllers in Sinatra?

I'd like to create a simple experimental MVC framework using Sinatra.
I'd like to define resources by name "pages" for example should resolve to:
/pages (index)
/pages/new
/pages/:id/show (show)
as WELL as map to app/controllers/PagesController.rb with corresponding get('/') to be responsible for the index, post('/pages/create') be responsible for creation, etc.
Trouble is even after reading the official documentation I'm terribly confused. I imagine I need to use non-classic Sinatra model for this, but could anyone point me in the right direction?
Thank you
If you want what I think you're wanting, I do this all the time. Initially for this scheme I used the travis-api source as a reference, but essentially what you want to do is extend Sinatra::Base in a "controller" class and then mount up your individual Sinatra "controllers" in rack, something like this:
module Endpoint
def self.included(base)
base.class_eval do
set(:prefix) { "/" << name[/[^:]+$/].downcase }
end
end
end
class Users < Sinatra::Base
include Endpoint
get '/' do
#logic here
end
get '/:id' do
#logic here
end
post '/' do
#logic here
end
patch '/:id' do
#logic here
end
end
class Posts < Sinatra::Base
include Endpoint
post '/' do
#logic here
end
end
and then something like this:
class App
require "lib/endpoints/users"
require "lib/endpoints/posts"
attr_reader :app
def initialize
#app = Rack::Builder.app do
[Users, Posts].each do |e|
map(e.prefix) { run(e.new) }
end
end
end
def call(env)
app.call(env)
end
end
You can adjust this to whatever you need, but the idea is the same, you separate your app into composable Sinatra applications that each have a prefix that they are mounted under using Rack. This particular example will give you routes for:
get '/users'
get '/users/:id'
post '/users'
patch '/users/:id'
get '/posts'
I'll give you a very simple example here:
Create a file controller.rb
get '/pages' do
#pages = Pages.all
erb :pages
end
Next create a views directory in the same folder as teh controller, and create a file named pages.html.erb
This is the corresponding view to your previously created controller action.
Here, you can type something like:
<% #pages.each do |p| %>
<%= p.title %>
<% end %>
Restart your server, visit localhost:PORT/pages and you will see a list of all your page titles.
You can check out this link for a simple sinatra tutorial - http://code.tutsplus.com/tutorials/singing-with-sinatra--net-18965
You can make this as complicated or as simple as you need. For example:
Rails makes a lot of magic happen under the hood, whereas Sinatra is more flexible at the cost of requiring you to implement some of this stuff yourself.
controller_map = {
'pages' => PagesController
}
post '/:controller/new' do
c = params[:controller]
module = controller_map[c]
module.create_new()
...
end
get '/:controller/:id/show' do
c = params[:controller]
id = params[:id]
module = controller_map[c]
module.get(id)
...
end

How can I access Sorcery in my RSpec tests?

Sorcery authentication gem: https://github.com/NoamB/sorcery
Sorcery's creator provides an example Rails app with Sorcery test helpers included in its Test::Unit functional tests: https://github.com/NoamB/sorcery-example-app/blob/master/test/functional/users_controller_test.rb
# Test::Unit functional test example
require 'test_helper'
class UsersControllerTest < ActionController::TestCase
setup do
#user = users(:noam)
end
test "should show user" do
login_user
get :show, :id => #user.to_param
assert_response :success
end
But I can't figure out how to get login_user to work in my RSpec controller specs.
/gems/sorcery-0.7.5/lib/sorcery/test_helpers/rails.rb:7:in `login_user':
undefined method `auto_login' for nil:NilClass (NoMethodError)
Here's the relevant code in the Sorcery gem regarding the above error:
https://github.com/NoamB/sorcery/blob/master/lib/sorcery/test_helpers/rails.rb
module Sorcery
module TestHelpers
module Rails
# logins a user and calls all callbacks
def login_user(user = nil)
user ||= #user
#controller.send(:auto_login,user)
#controller.send(:after_login!,user,[user.send(user.sorcery_config.username_attribute_names.first),'secret'])
end
def logout_user
#controller.send(:logout)
end
end
end
end
UPDATE:
As per Sorcery's documentation "Testing in Rails 3", I have indeed added include Sorcery::TestHelpers::Rails to my spec_helper.rb.
The Sorcery test helper login_user acts on #controller, but I'm getting the error because #controller is nil in my controller spec. Here's my spec:
#spec/controllers/forums_controller_spec.rb
require 'spec_helper'
describe ForumsController do
render_views
describe 'GET new' do
describe 'when guest' do
it 'should deny and redirect' do
get :new
response.should redirect_to(root_path)
end
end
describe 'when admin' do
p #controller #=> nil
#user = User.create!(username: "Test", password: "secret", email: "test#test.com")
login_user # <--------------- where the error occurs
it 'should resolve' do
get :new
response.should render_template(:new)
end
end
end
end
FWIW, I spent a lot of time looking for an answer to this problem. I am using Capybara and RSpec. As it turns out, you need to login manually to using Sorcery to get the login to work.
I've created a Gist on creating integration tests with Sorcery/Rspec/Capybara here:
https://gist.github.com/2359120/9989c14af19a48ba726240d030c414b882b96a8a
You need to include the Sorcery test helpers in your spec_helper
include Sorcery::TestHelpers::Rails
See the sorcery wiki : https://github.com/NoamB/sorcery/wiki/Testing-rails-3
In the example rails app, this is done at https://github.com/NoamB/sorcery-example-app/blob/master/test/test_helper.rb#L13
Updated
Do you have any other Controller specs in the same folder which pass successfully ?
RSpec usually mixes in the required stuff for controller testing for the specs in "spec/controllers" folder.
You could try explicitly marking this as a controller spec by writing
describe ForumsController, :type => :controller do
You need to put your user creation and login into a before(:each) block as follows:
describe 'when admin' do
before(:each) do
#user = User.create!(username: "Test", password: "secret", email: "test#test.com")
login_user
end
it 'should resolve' do
get :new
response.should render_template(:new)
end
end
I've just experienced this dilemma myself and drawing from the input from danneu, diwalak and Birdlevitator (in this thead: rail3/rspec/devise: rspec controller test fails unless I add a dummy=subject.current_user.inspect) I think I can see a solution.
I've been working with a standard rails 3 rspec generated resource from the 'rails generate scaffold' command. Here's the controller rspec file after I modified it to work with a sorcery login:
require 'spec_helper'
# This spec was generated by rspec-rails when you ran the scaffold generator.
# It demonstrates how one might use RSpec to specify the controller code that
# was generated by Rails when you ran the scaffold generator.
#
# It assumes that the implementation code is generated by the rails scaffold
# generator. If you are using any extension libraries to generate different
# controller code, this generated spec may or may not pass.
#
# It only uses APIs available in rails and/or rspec-rails. There are a number
# of tools you can use to make these specs even more expressive, but we're
# sticking to rails and rspec-rails APIs to keep things simple and stable.
#
# Compared to earlier versions of this generator, there is very limited use of
# stubs and message expectations in this spec. Stubs are only used when there
# is no simpler way to get a handle on the object needed for the example.
# Message expectations are only used when there is no simpler way to specify
# that an instance is receiving a specific message.
describe RecordsController do
before(:each) do
#user = User.create!(forename: "Billy", surname: "Bob", username: "Test", password: "secret!1", email: "test#test.com")
login_user
end
# This should return the minimal set of attributes required to create a valid
# Record. As you add validations to Record, be sure to
# update the return value of this method accordingly.
def valid_attributes
{ :owner => 'Mr Blobby', :catagory => 'Index'}
end
# This should return the minimal set of values that should be in the session
# in order to pass any filters (e.g. authentication) defined in
# RecordsController. Be sure to keep this updated too.
def valid_session
{"warden.user.user.key" => session["warden.user.user.key"]}
end
describe "GET index" do
it "assigns all records as #records" do
record = Record.create! valid_attributes
get :index, {}, valid_session
assigns(:records).should eq([record])
end
end
describe "GET show" do
it "assigns the requested record as #record" do
record = Record.create! valid_attributes
get :show, {:id => record.to_param}, valid_session
assigns(:record).should eq(record)
end
end
describe "GET new" do
it "assigns a new record as #record" do
get :new, {}, valid_session
assigns(:record).should be_a_new(Record)
end
end
describe "GET edit" do
it "assigns the requested record as #record" do
record = Record.create! valid_attributes
get :edit, {:id => record.to_param}, valid_session
assigns(:record).should eq(record)
end
end
describe "POST create" do
describe "with valid params" do
it "creates a new Record" do
expect {
post :create, {:record => valid_attributes}, valid_session
}.to change(Record, :count).by(1)
end
it "assigns a newly created record as #record" do
post :create, {:record => valid_attributes}, valid_session
assigns(:record).should be_a(Record)
assigns(:record).should be_persisted
end
it "redirects to the created record" do
post :create, {:record => valid_attributes}, valid_session
response.should redirect_to(Record.last)
end
end
describe "with invalid params" do
it "assigns a newly created but unsaved record as #record" do
# Trigger the behavior that occurs when invalid params are submitted
Record.any_instance.stub(:save).and_return(false)
post :create, {:record => {}}, valid_session
assigns(:record).should be_a_new(Record)
end
it "re-renders the 'new' template" do
# Trigger the behavior that occurs when invalid params are submitted
Record.any_instance.stub(:save).and_return(false)
post :create, {:record => {}}, valid_session
response.should render_template("new")
end
end
end
describe "PUT update" do
describe "with valid params" do
it "updates the requested record" do
record = Record.create! valid_attributes
# Assuming there are no other records in the database, this
# specifies that the Record created on the previous line
# receives the :update_attributes message with whatever params are
# submitted in the request.
Record.any_instance.should_receive(:update_attributes).with({'these' => 'params'})
put :update, {:id => record.to_param, :record => {'these' => 'params'}}, valid_session
end
it "assigns the requested record as #record" do
record = Record.create! valid_attributes
put :update, {:id => record.to_param, :record => valid_attributes}, valid_session
assigns(:record).should eq(record)
end
it "redirects to the record" do
record = Record.create! valid_attributes
put :update, {:id => record.to_param, :record => valid_attributes}, valid_session
response.should redirect_to(record)
end
end
describe "with invalid params" do
it "assigns the record as #record" do
record = Record.create! valid_attributes
# Trigger the behavior that occurs when invalid params are submitted
Record.any_instance.stub(:save).and_return(false)
put :update, {:id => record.to_param, :record => {}}, valid_session
assigns(:record).should eq(record)
end
it "re-renders the 'edit' template" do
record = Record.create! valid_attributes
# Trigger the behavior that occurs when invalid params are submitted
Record.any_instance.stub(:save).and_return(false)
put :update, {:id => record.to_param, :record => {}}, valid_session
response.should render_template("edit")
end
end
end
describe "DELETE destroy" do
it "destroys the requested record" do
record = Record.create! valid_attributes
expect {
delete :destroy, {:id => record.to_param}, valid_session
}.to change(Record, :count).by(-1)
end
it "redirects to the records list" do
record = Record.create! valid_attributes
delete :destroy, {:id => record.to_param}, valid_session
response.should redirect_to(records_url)
end
end
end
And a run down of the important bits:
This bit does the programmatic login (ignore the forename and surname attributes, they're specific to the solution I'm building):
before(:each) do
#user = User.create!(forename: "Billy", surname: "Bob", username: "Test", password: "secret!1", email: "test#test.com")
login_user
end
This bit holds session info/key data:
def valid_session
{"warden.user.user.key" => session["warden.user.user.key"]}
end
As diwalak writes, we need to add this to the spec_help.rb file:
include Sorcery::TestHelpers::Rails
And that's it - worked for me anyhow :)

How to upload a file temporarily in Rails 3?

I'm creating CSV-upload functionality for a site of mine.
I'm looking to upload a file, parse it, and then dispose of it.
I know I can upload and save a file using Paperclip, but that seems a bit like overkill.
All I need to do is parse the uploaded file and never save it.
How would I go about doing this in Rails 3?
Note: I'd prefer to do the uploading manually without using an external gem so I can learn how to process works, but any suggestions are welcome.
Thanks!
Use the file_field helper in your form, then in your controller you can use File.Write and File.read to save the file.
E.g. View
<%= form_for #ticket do |f| %>
<%= f.file_field :uploaded_file %>
<% end %>
Controller
def upload
uploaded = params[:ticket][:uploaded_file]
File.open(<insert_filename_here>, 'w') do |file|
file.write(uploaded.read)
end
end
Edit: Just saw #klochner's comment, that link says pretty much what I have said so follow that: RubyOnRails Guides: Uploading Files.
Paste this in your model
def parse_file
File.open(uploaded/file/path, 'w') do |f| # Feed path that user gives in some way
## Parse here
end
end
this in view
<%=form_for #page, :multipart => true do |f|%>
<ul><li><%= f.label :file%></li>
<li><%= f.file_field :uploaded_file%></li></ul>
<%end%>
Let me know if this works. If it fails figure out a way to feed path of uploaded_file in parse_file method (the definite way which will work is storing file location in db and picking up from there, but it is not the right way to do this thing). Otherwise, I guess it should work.
Complete Example
Take, for example, uploading an import file containing contacts. You don't need to store this import file, just process it and discard it.
Routes
routes.rb
resources :contacts do
collection do
get 'import/new', to: :new_import # import_new_contacts_path
post :import, on: :collection # import_contacts_path
end
end
Form
views/contacts/new_import.html.erb
<%= form_for #contacts, url: import_contacts_path, html: { multipart: true } do |f| %>
<%= f.file_field :import_file %>
<% end %>
Controller
controllers/contacts_controller.rb
def new_import
end
def import
begin
Contact.import( params[:contacts][:import_file] )
flash[:success] = "<strong>Contacts Imported!</strong>"
redirect_to contacts_path
rescue => exception
flash[:error] = "There was a problem importing that contacts file.<br>
<strong>#{exception.message}</strong><br>"
redirect_to import_new_contacts_path
end
end
Contact Model
models/contact.rb
def import import_file
File.foreach( import_file.path ).with_index do |line, index|
# Process each line.
# For any errors just raise an error with a message like this:
# raise "There is a duplicate in row #{index + 1}."
# And your controller will redirect the user and show a flash message.
end
end

Error generating test routes for Rails 3 plugin?

I'm trying to develop tests for plugin "foobar" that modifies some of the standard Rails helpers. In vendor/plugins/foobar/test/foobar_test.rb, I have the following:
# create the test model
class Thing < ActiveRecord::Base
end
# create the test controller, which renders the included index template
class ThingsController < ActionController::Base
def index
#things = Thing.all
format.html { render(:file => 'index') }
end
def destroy
#thing = Thing.find(params[:id])
#thing.destroy
format.html { render(:file => 'index') }
end
end
# confirm that the test environment is working correctly
class ThingsTest < ActiveSupport::TestCase
test "model is loaded correctly" do
assert_kind_of Thing, Thing.new
end
end
# confirm that the controller and routes are working correctly
class ThingsControllerTest < ActionController::TestCase
test "should load index" do
with_routing do |set|
set.draw do
resources :things, :only => [:index, :destroy]
end
get :index
assert_response :success
end
end
end
When I run rake, I get the following output:
test_should_load_index(ThingsControllerTest):
NameError: undefined local variable or method `_routes' for ThingsController:Class
Any idea what I'm doing wrong that is preventing me from accessing the routes? I've been at this for days and scoured much documentation, to no avail. Thanks for the help!
I got things to work on Rails 3.1.0.rc1 with Ruby 1.9.2, by following "The Basics of Creating Rails Plugins" Rails Guide and correcting the code whenever it exploded due to an incompatibility.
I started by running:
Code$ rails new tester
Code$ cd tester
tester$ rails generate plugin foobar --with-generator
Then modifying the generated code to obtain these files in addition to the default ones:
# vendor/plugins/foobar/init.rb
require 'foobar'
# vendor/plugins/foobar/lib/foobar.rb
%w{ models controllers helpers }.each do |dir|
path = File.join(File.dirname(__FILE__), 'app', dir)
$LOAD_PATH << path
ActiveSupport::Dependencies.autoload_paths << path
ActiveSupport::Dependencies.autoload_once_paths.delete(path)
end
# I'm not entirely sure this is the best way to add our plugin's views
# to the view search path, but it works
ActionController::Base.view_paths =
ActionController::Base.view_paths +
[ File.join(File.dirname(__FILE__), 'app', 'views') ]
<!-- vendor/plugins/foobar/lib/app/views/things/index.html.erb -->
<h1>Listing things</h1>
<table>
<tr>
<th>Name</th>
<th></th>
</tr>
<% #things.each do |thing| %>
<tr>
<td><%= thing.name %></td>
<td><%= link_to 'Destroy', thing, confirm: 'Are you sure?', method: :delete %></td>
</tr>
<% end %>
</table>
# vendor/plugins/foobar/test/database.yml
sqlite:
:adapter: sqlite
:dbfile: vendor/plugins/foobar/test/foobar_plugin.sqlite.db
sqlite3:
:adapter: sqlite3
:database: vendor/plugins/foobar/test/foobar_plugin.sqlite3.db
postgresql:
:adapter: postgresql
:username: postgres
:password: postgres
:database: foobar_plugin_test
:min_messages: ERROR
mysql:
:adapter: mysql
:host: localhost
:username: root
:password: password
:database: foobar_plugin_test
# vendor/plugins/foobar/test/schema.rb
ActiveRecord::Schema.define(:version => 0) do
create_table :things, :force => true do |t|
t.string :name
t.datetime :created_at
t.datetime :updated_at
end
end
# vendor/plugins/foobar/test/test_helper.rb
ENV['RAILS_ENV'] = 'test'
ENV['RAILS_ROOT'] ||= File.dirname(__FILE__) + '/../../../..'
require 'test/unit'
require File.expand_path(File.join(ENV['RAILS_ROOT'], 'config/environment.rb'))
def load_schema
config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
db_adapter = ENV['DB']
# no db passed, try one of these fine config-free DBs before bombing.
db_adapter ||=
begin
require 'rubygems'
require 'sqlite'
'sqlite'
rescue MissingSourceFile
begin
require 'sqlite3'
'sqlite3'
rescue MissingSourceFile
end
end
if db_adapter.nil?
raise "No DB Adapter selected. Pass the DB= option to pick one, or install Sqlite or Sqlite3."
end
ActiveRecord::Base.establish_connection(config[db_adapter])
load(File.dirname(__FILE__) + "/schema.rb")
require File.dirname(__FILE__) + '/../init'
end
load_schema
# vendor/plugins/foobar/test/foobar_test.rb
require File.dirname(__FILE__) + '/test_helper'
# create the test model
class Thing < ActiveRecord::Base
end
# create the test controller, which renders the included index template
class ThingsController < ActionController::Base
def index
#things = Thing.all
respond_to do |format|
format.html # index.html.erb
end
end
def destroy
#thing = Thing.find(params[:id])
#thing.destroy
respond_to do |format|
format.html { redirect_to things_url }
end
end
end
# confirm that the test environment is working correctly
class ThingsTest < ActiveSupport::TestCase
test "schema has loaded correctly" do
assert_equal [], Thing.all
end
test "model is loaded correctly" do
assert_kind_of Thing, Thing.new
end
end
# confirm that the controller and routes are working correctly
class ThingsControllerTest < ActionController::TestCase
test "should load index" do
with_routing do |set|
set.draw do
resources :things, :only => [:index, :destroy]
end
get :index
assert_response :success
end
end
end
And finally, our test passes:
tester$ cd vendor/plugins/foobar/
foobar$ rake
-- create_table(:things, {:force=>true})
-> 0.0059s
-- initialize_schema_migrations_table()
-> 0.0002s
-- assume_migrated_upto_version(0, ["db/migrate"])
-> 0.0003s
Loaded suite /Users/nick/.rvm/gems/ruby-1.9.2-p0/gems/rake-0.9.2/lib/rake/rake_test_loader
Started
...
Finished in 0.091642 seconds.
3 tests, 3 assertions, 0 failures, 0 errors, 0 skips
Your best bet for debugging this is to do
rake <task> --trace
This will give you a much better idea (i.e. a line number) on what is causing the _routes error.

Resources