Error generating test routes for Rails 3 plugin? - ruby

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.

Related

Ruby Rails Cucumber Troubleshooting

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

Rack::Session::Pool Sessions in Sinatra

I can't seem to get these sessions to continue into other pages.
app.rb:
class MyApp < Sinatra::Base
use Rack::Session::Pool, :expire_after => 60 * 1
get "/" do
#foo = "one two three"
erb :index
end
get "/first" do
session[:foo] = Time.now
session[:message] = "ALPHA"
session[:message1] = "CHARLIE"
erb :first
end
get "/second" do
session[:message2] = "BRAVO2"
erb :second
end
end
Inside /first and /second:
Sess: <%= session.inspect %><br>
The session doesn't want to carry across pages. On /first I'm displaying this:
Sess: {"message"=>"ALPHA", "message1"=>"CHARLIE", "foo"=>2015-12-01 17:05:31 -0500}
On /second I'm displaying this:
Sess: {"message2"=>"BRAVO2"}
Just needed a restart. Figure that.

Issue with Shoes setup

When i am running shoes.rb file, which contains code to install gem, it throws error.
Undefined method setup for Shoes:Class
Code:
Shoes.setup do
gem 'activerecord' # install AR if not found
require 'active_record'
require 'fileutils'
ActiveRecord::Base.establish_connection(
:adapter => 'postgresql',
:dbfile => 'shoes_app'
)
# create the db if not found
unless File.exist?("shoes_app.sqlite3")
ActiveRecord::Schema.define do
create_table :notes do |t|
t.column :message, :string
end
end
end
end
class ShoesApp < Shoes
require 'note'
url '/', :index
def index
para 'Say something...'
flow do
#note = edit_line
button 'OK' do
Note.new(:message => #note.text).save
#note.text = ''
#result.replace get_notes
end
end
#result = para get_notes
end
def get_notes
messages = []
notes = Note.find(:all, :select => 'message')
notes.each do |foo|
messages << foo.message
end
out = messages.join("n")
end
end
Shoes.app :title => 'Notes', :width => 260, :height => 350
The problem was using Shoes4, where the setup method was unimplemented.
Shoes4 now implements Shoes.setup for backwards compatibility reasons but you don't really need it, so it doesn't do anything except for printing a warning that you should rather do gem install gem_name instead of using Shoes.setup.

#users variable empty. forget to pass the collection object for will_paginate?

Problem localhost:3000/users/ won't display
I enter humbly as I am trying to make it through the rails tutorial for the first time. I am in chapter 10 and I have been trouble shooting this for 5 hours. When I attempt to visit localhost:3000/users/ I get an error (I believe this has something to do with factory_girl) that explain that the #users variable is empty and that I forgot to pass a collection object for will_paginate.
I'm currently at chapter 10, section 10.23 and each time I run:
$ bundle exec rake db:reset $ bundle exec rake db:populate
$ bundle exec rake db:test:prepare
I get an error explaining that
rake aborted!
Factory already registered: micropost
This is my second time trying this chapter as I encountered problems the first time and started from chapter 9. Please help and be clear and detailed when providing directions. I am happy to post whatever files that will be helpful.
Here is my index.html.erb - I save these as HTML, should they be saved as ruby files instead?
<% provide(:title, 'All users') %>
<h1>All users</h1>
<%= will_paginate %>
<ul class="users">
<%= render #users %>
</ul>
<%= will_paginate %>
Here is my users controller
class UsersController < ApplicationController
before_filter :signed_in_user, only: [:index, :edit, :update, :destroy]
before_filter :correct_user, only: [:edit, :update]
before_filter :admin_user, only: :destroy
def show
#user = User.find(params[:id])
#microposts = #user.microposts.paginate(page: params[:page])
end
end
def new
#user = User.new
end
def index
#title = "All users"
#users = User.paginate(:page => params[:page])
end
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:success] = "Do more of the things you love!"
redirect_to #user
else
render 'new'
end
end
def edit
end
def update
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated"
sign_in #user
redirect_to #user
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User destroyed."
redirect_to users_url
end
private
def signed_in_user
unless signed_in?
store_location
redirect_to signin_url, notice: "Please sign in."
end
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user?(#user)
end
def admin_user
redirect_to(root_path) unless current_user.admin?
end
In your Users controller, make sure you have #users and if you are using will_paginate, make sure you call .paginate(page: params[:page], per_page: 20] and in your view, have <%= will_paginate #users %>.
/users should point to UsersController#index. Make sure you are assigning the collection #users.
For instance it could look like this at the most basic level:
def index
#users = User.all # not paginated
#users = User.paginate(page: params[:page]) # paginated
end
As far as the test database error, I'm guessing that's because you define a :micropost factory more than once.
Instead of #user = User.find(params[:id]) this you should use
#users = User.paginate(page: params[:page])

Blocks in pure ERB / Erubis

I have the following Ruby script:
require 'erubis'
def listing(title, attributes={})
"output" + yield + "more output"
end
example = %Q{<% listing "db/migrate/[date]_create_purchases.rb", :id => "ch01_292" do %>
<![CDATA[class CreatePurchases < ActiveRecord::Migration
def change
create_table :purchases do |t|
t.string :name
t.float :cost
t.timestamps
end
end
end]]>
<% end %>}
chapter = Erubis::Eruby.new(example)
p chapter.result(binding)
I am attempting to use a block here and get it to output "output", then the content in the block and then "more output", but I can't seem to get it to work.
I know that ERB used to work this way in Rails 2.3 and now works with <%= in Rails 3... but I'm not using Rails at all. This is just pure ERB.
How can I get it to output all the content?
Jeremy McAnally linked me to this perfect description of how to do it.
Basically, you need to tell ERB to store the output buffer in a variable.
The script ends up looking like this:
require 'erb'
def listing(title, attributes={})
concat %Q{
<example id='#{attributes[:id]}'>
<programlisting>
<title>#{title}</title>}
yield
concat %Q{
</programlisting>
</example>
}
end
def concat(string)
#output.concat(string)
end
example = %Q{<% listing "db/migrate/[date]_create_purchases.rb", :id => "ch01_292" do %>
<![CDATA[class CreatePurchases < ActiveRecord::Migration
def change
create_table :purchases do |t|
t.string :name
t.float :cost
t.timestamps
end
end
end]]>
<% end %>}
chapter = ERB.new(example, nil, nil, "#output")
p chapter.result(binding)
Great. I remember seeing that a while ago. Playing a bit I was getting this:
require 'erubis'
def listing(title, attributes={})
%Q{<%= "output #{yield} more output" %>}
end
example = listing "some title", :id => 50 do
def say_something
"success?"
end
say_something
end
c = Erubis::Eruby.new(example)
p c.evaluate
# => "output success? more output"

Resources