I'm fairly new to Ruby on Rails and making my way through Michael Hartl's Rails Tutorial. I've hit a bug I can't seem to fix on Chapter 10 (specifically, 10.1.3 "User/Micropost associations"). The error I run into is when I execute the following rspec command:
$ bundle exec rspec spec/models
It raises the following error:
Failures:
1) Micropost accessible attributes should not allow access to user_id
←[31mFailure/Error:←[0m ←[31mexpect do←[0m
←[31mexpected ActiveModel::MassAssignmentSecurity::Error but nothing was
raised←[0m
←[36m # ./spec/models/micropost_spec.rb:28:in `block (3 levels) in <top (req
uired)>'←[0m
Finished in 5.79 seconds
←[31m27 examples, 1 failure←[0m
Failed examples:
←[31mrspec ./spec/models/micropost_spec.rb:27←[0m ←[36m# Micropost accessible at
tributes should not allow access to user_id←[0m
This is the code for micropost_spec.rb:
require 'spec_helper'
describe Micropost do
let(:user) { FactoryGirl.create(:user) }
before { #micropost = user.microposts.build(content: "Lorem ipsum") }
subject { #micropost }
it { should respond_to(:content) }
it { should respond_to(:user_id) }
it { should respond_to(:user) }
its(:user) { should == user }
it { should be_valid }
describe "when user_id is not present" do
before { #micropost.user_id = nil }
it { should_not be_valid }
end
describe "accessible attributes" do
it "should not allow access to user_id" do
expect do
Micropost.new(user_id: user.id)
end.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
end
end
end
application.rb:
require File.expand_path('../boot', __FILE__)
# Pick the frameworks you want:
require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "active_resource/railtie"
require "sprockets/railtie"
# require "rails/test_unit/railtie"
if defined?(Bundler)
# If you precompile assets before deploying to production, use this line
Bundler.require(*Rails.groups(:assets => %w(development test)))
# If you want your assets lazily compiled in production, use this line
# Bundler.require(:default, :assets, Rails.env)
end
module SampleApp
class Application < Rails::Application
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
config.active_record.whitelist_attributes = true
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
# Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named.
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
# Activate observers that should always be running.
# config.active_record.observers = :cacher, :garbage_collector, :forum_observer
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# config.i18n.default_locale = :de
# Configure the default encoding used in templates for Ruby 1.9.
config.encoding = "utf-8"
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters += [:password]
# Enable the asset pipeline
config.assets.enabled = true
# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0'
end
end
micropost.rb...
class Micropost < ActiveRecord::Base
attr_accessible :content
belongs_to :user
validates :user_id, presence: true
end
Thanks in advance for your help!
Thai
So expected ActiveModel::MassAssignmentSecurity::Error but nothing was
raised when Micropost.new(user_id: user.id) is called.
For it to work correctly:
The Rails application should be configured to explicitly whitelist or blacklist accessible parameters; in rails versions before 3.2.3 this was on by default; in previous versions, it has to be done by the following line in config/application.rb as detailed in Listing 10.6 of RailsTutorial book:
config.active_record.whitelist_attributes = true
There shouldn't be a attr_accessible :user_id in the micropost.rb model file.
Try adding
config.active_record.mass_assignment_sanitizer = :strict
To your application.rb file, what I found out is that if not strict, it would just throw a Warning but not an exception.
Like you, I tried all of the solutions above with no luck. I also notice that Hartl's more recent editions of the book for Rails 4 do not contain those tests, so I suspect this approach to testing accessibility does not work in later versions. Perhaps an expert (someone who isn't running through the Rails Tutorial) can chime in.
But.... I noticed this response in another StackOverflow thread recommending the "shoulda-matchers" Gem to test it, and that solved the problem for me (https://stackoverflow.com/a/11876425/3899955). In investigating that Gem, I find it is a nice way to produce readable tests for a number of common ActiveModel, ActiveRecord, and ActionController tests. I am happy to be adding that to my quiver, perhaps you will be too.
I don't take credit for that answer, just wanted to post this so if anyone else is running in to this frustration doing Chapter 10.5, or completing exercise 6 in Chapter 9.
Related
Every time I call validates_with NumberValidator. I get an uninitialized constant error. I have created a directory called validators under the "app" directory. This is what I have so far.
This is my model
class Worker < ActiveRecord::Base
include ActiveModel::Validations
validates_with NumberValidator
end
This is my validator file
class NumberValidator < ActiveModel::Validator
def validate(record, attribute, value)
puts record
puts attribute
puts value
end
end
require File.expand_path('../boot', __FILE__)
require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
require "active_job/railtie"
require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
require "sprockets/railtie"
# require "rails/test_unit/railtie"
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module ApplicationName
class Application < Rails::Application
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# config.i18n.default_locale = :de
# Do not swallow errors in after_commit/after_rollback callbacks.
config.active_record.raise_in_transactional_callbacks = true
config.autoload_paths += %W["#{config.root}/app/validators/"]
end
end
I have restarted my server and I'm just not sure what I am doing wrong
Rails will automatically load anything under app/ assuming you follow the expected structure. The way Rails does this is with Constant Autoloading, which maps a constant, such as MyValidator to a file name, such as my_validator.rb under a directory in the autoload paths.
In order for Rails to load your NumberValidator, you should name the file number_validator.rb and put it in a folder in the autoload path, such as app/validators/number_validator.rb. You will need to restart the server if you have added a directory under app because these are initialized at boot time (be sure to run spring stop if you are using spring so it restarts too!).
Notes:
You do not need to add anything under app to your app config, so you can remove the config.autoload_paths line.
ActiveRecord::Base already includes ActiveModel::Validations, so you can also remove that include from your Worker class.
For more information on how this process works, check out the Autoloading and Reloading Constants page in the Rails Guides.
This is just a curious irritant, but why does my app not include the expected line in config/application.rb, or anywhere else?
require 'rails/all'
This app was generated using Rails Composer in early 2014, if that makes a difference. Also, it is Rails 4.2.1.
The issue arose only because I am studying the Configuring Rails Applications and the The Rails Initialization Process guides as I have a need to modify my initialization process. Both state that the config/application.rb file is expected to contain that line, but mine does not. And, yes, the app runs just fine locally and on Heroku, so... Why?
My file is:
require File.expand_path('../boot', __FILE__)
# Pick the frameworks you want:
require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "sprockets/railtie"
# require "rails/test_unit/railtie"
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(:default, Rails.env)
module Theappname
class Application < Rails::Application
config.generators do |g|
# Enable Chrome Source Maps so CSS and JS can be debugged
#g.sass_options = { :debug_info => true }
# don't generate RSpec tests for views and helpers
g.test_framework :rspec, fixture: true
g.fixture_replacement :factory_girl, dir: 'spec/factories'
g.view_specs false
g.helper_specs false
end
# Rails 4 should include all helpers for controllers and views
config.action_controller.include_all_helpers = true
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
#config.time_zone = 'Eastern Time (US & Canada)'
config.time_zone = 'UTC' # Don't use local time or you won't notice time issues.
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# config.i18n.default_locale = :de
# Do not check for unavailable locales
I18n.enforce_available_locales = false
# not needed at 4.0
config.assets.initialize_on_precompile = false
# Load files in lib
config.autoload_paths += %W(#{config.root}/lib)
# Extend Rails classes in lib/core_ext/<classname>.rb... See above?
#config.autoload_paths += Dir[File.join(Rails.root, "lib", "core_ext", "*.rb")].each {|l| require l }
# 20150711 Default Date formats
#default_date_formats = { :default => '%d.%m.%Y' }
default_date_formats = { :default => '%Y.%m.%d' }
Time::DATE_FORMATS.merge!(default_date_formats)
Date::DATE_FORMATS.merge!(default_date_formats)
# 20150808 Adding Delayed_Job queueing for daily_report and such
config.active_job.queue_adapter = :delayed_job
end
end
You can require 'rails/all' if that suits your fancy, but the parts of rails necessary to run your application are required with these lines:
require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "sprockets/railtie"
If I were to guess the motivation for this, it is probably that you don't necessarily need or want all of the parts of rails in your application, so better to explicitly require the ones you want instead of everything. In this case if you were to require 'rails/all' you would end up with action_view, active_job, and rails/test_unit as well as the above. The files required can be found in railties.
I have been trying to validate that the first name does not include anything but letters, numbers, hyphens, or underscores.
In my users.rb model I am using this code:
validates :first_namename, :firstname_convention => true
Which goes to the FirstNameConvention class:
class FirstnameConventionValidator < ActiveModel::EachValidator
def validate_each(record, field, value)
unless value.blank?
record.errors[field] << "is not alphanumeric (letters, numbers, underscores or periods)" unless value =~ /^[[:alnum:]._-]+$/
record.errors[field] << "should start with a letter" unless value[0] =~ /[A-Za-z]/
record.errors[field] << "contains illegal characters" unless value.ascii_only?
end
end
end
And that file is stored in app/validators - a folder that I had to make.
I get this error:
ArgumentError in UsersController#index
Unknown validator: 'FirstnameConventionValidator'
I have tried placing:
config.autoload_paths += %W["#{config.root}/app/validators"]
In config/application.rb:
require File.expand_path('../boot', __FILE__)
require 'rails/all'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module ThorCinema
class Application < Rails::Application
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# config.i18n.default_locale = :de
# Do not swallow errors in after_commit/after_rollback callbacks.
config.active_record.raise_in_transactional_callbacks = true
config.autoload_paths << Rails.root.join('app', 'validators')
end
end
But this still doesn't work.
What am I doing wrong?
app/validators is a good place to store validators, all you need to do, is to tell Rails about this folder existence. Add this line to your autoload paths in config/application.rb (YourAppName::Application section):
config.autoload_paths << Rails.root.join('app', 'validators')
That's it
using devise 2.1.0
I had to override the devise registration controller. In the process I had to move my devise views directory to correspond with the new controller. I have this code in my config/applications.rb file
paths.app.views << "app/views/devise"
And it is throwing an error when I try and start my server with rails s:
...config/application.rb:65:in `<class:Application>': undefined method `app' for #<Rails::Paths::Root:0x00000103537740> (NoMethodError)
I am relatively new at rails. But I get that there is a root class somewhere that didn't define app. I got this advice to use this paths.app.views here at stack.
Here is the full applications.rb
require File.expand_path('../boot', __FILE__)
require 'rails/all'
if defined?(Bundler)
# If you precompile assets before deploying to production, use this line
Bundler.require(*Rails.groups(:assets => %w(development test)))
# If you want your assets lazily compiled in production, use this line
# Bundler.require(:default, :assets, Rails.env)
end
module Growle
class Application < Rails::Application
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
# Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named.
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
# Activate observers that should always be running.
# config.active_record.observers = :cacher, :garbage_collector, :forum_observer
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# config.i18n.default_locale = :de
# Configure the default encoding used in templates for Ruby 1.9.
config.encoding = "utf-8"
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters += [:password, :password_confirmation]
#don't generate RSpec tests for views and helper
config.generators do |g|
g.view_specs false
g.helper_specs false
end
# Use SQL instead of Active Record's schema dumper when creating the database.
# This is necessary if your schema can't be completely dumped by the schema dumper,
# like if you have constraints or database-specific column types
# config.active_record.schema_format = :sql
# Enforce whitelist mode for mass assignment.
# This will create an empty whitelist of attributes available for mass-assignment for all models
# in your app. As such, your models will need to explicitly whitelist or blacklist accessible
# parameters by using an attr_accessible or attr_protected declaration.
config.active_record.whitelist_attributes = true
# Enable the asset pipeline
config.assets.enabled = true
# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0'
paths.app.views << "app/views/devise"
end
end
By reading the last comment of yours, it sound that you want either view_paths= or prepend_view_path.
Those can be set at class level in your controller.
If that isn't what you are looking for, consider reading Override devise registrations controller for information.
The ActionMailer in my Rails 3.1 project has an odd behavior, the ActionMailer::Base.deliveries is empty in test while I can actually receive the email by running the code in rails console. Anybody can points out what wrong it is?
# spec/mailers/user_mailer_spec.rb
require "spec_helper"
describe UserMailer do
describe ".reminding_email" do
subject { UserMailer.reminding_email(user).deliver }
let(:user) { create :confirmed_user_with_mass_tasks }
it "should be delivered" do
ActionMailer::Base.deliveries.should_not be_empty
end
its(:to) { should == [user.email] }
its(:subject) { should == "Task Reminding" }
end
end
# app/mailers/user_mailer.rb
class UserMailer < ActionMailer::Base
layout "email"
default from: Rails.env.production? ? "<production>#gmail.com" : "<test>#gmail.com"
def reminding_email(user)
#tasks = user.tasks.reminding_required
#current = Time.current
mail(to: user.email, subject: "Task Reminding")
end
end
Failures:
1) UserMailer.reminding_email should be delivered
Failure/Error: ActionMailer::Base.deliveries.should_not be_empty
expected empty? to return false, got true
# ./spec/mailers/user_mailer_spec.rb:8:in `block (3 levels) in '
Finished in 11.81 seconds
42 examples, 1 failure, 2 pending
# config/environments/test.rb
RemindersForMe::Application.configure do
# Settings specified here will take precedence over those in config/application.rb
# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
# and recreated between test runs. Don't rely on the data there!
config.cache_classes = true
# Configure static asset server for tests with Cache-Control for performance
config.serve_static_assets = true
config.static_cache_control = "public, max-age=3600"
# Log error messages when you accidentally call methods on nil
config.whiny_nils = true
# Show full error reports and disable caching
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
# Raise exceptions instead of rendering exception templates
config.action_dispatch.show_exceptions = false
# Disable request forgery protection in test environment
config.action_controller.allow_forgery_protection = false
# Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
# Use SQL instead of Active Record's schema dumper when creating the test database.
# This is necessary if your schema can't be completely dumped by the schema dumper,
# like if you have constraints or database-specific column types
# config.active_record.schema_format = :sql
# Print deprecation notices to the stderr
config.active_support.deprecation = :stderr
# Setup default URL options as devise needed
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
end
Application:
# config/application.rb
require File.expand_path('../boot', __FILE__)
# Pick the frameworks you want:
require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "active_resource/railtie"
require "sprockets/railtie"
# require "rails/test_unit/railtie"
if defined?(Bundler)
# If you precompile assets before deploying to production, use this line
# Bundler.require(*Rails.groups(:assets => %w(development test)))
Bundler.require *Rails.groups(:assets) if defined?(Bundler)
# If you want your assets lazily compiled in production, use this line
# Bundler.require(:default, :assets, Rails.env)
end
module RemindersForMe
class Application < Rails::Application
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
# Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named.
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
# Activate observers that should always be running.
# config.active_record.observers = :cacher, :garbage_collector, :forum_observer
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# config.i18n.default_locale = :de
# Configure the default encoding used in templates for Ruby 1.9.
config.encoding = "utf-8"
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters += [:password]
# Enable the asset pipeline
config.assets.enabled = true
# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0'
# Set Rspec generator as default
config.generators do |g|
g.test_framework :rspec
end
# Set Mailer of Devise
config.to_prepare do
Devise::Mailer.layout "email"
end
end
end
Now change the spec as follow:
require "spec_helper"
describe UserMailer do
describe ".reminding_email" do
subject { UserMailer.reminding_email(user) }
let(:user) { create :confirmed_user_with_undue_tasks }
it { expect { subject.deliver }.to change { ActionMailer::Base.deliveries.length }.by(1) }
its(:to) { should == [user.email] }
its(:subject) { should == "Task Reminding" }
end
end
1) UserMailer.reminding_email
Failure/Error: it { expect { subject.deliver }.to change { ActionMailer::Base.deliveries.length }.by(1) }
result should have been changed by 1, but was changed by 2
# ./spec/mailers/user_mailer_spec.rb:7:in `block (3 levels) in '
I had a similar problem and it happened because I was testing Devise emails: Devise uses a separate mailer, so you'll need to access Devise.mailer.deliveries instead of ActionMailer::Base.deliveries.
You should test the separated properties of the mail:
describe UserMailer do
describe ".reminding_email" do
subject { UserMailer.reminding_email(user)}
let(:user) { create :confirmed_user_with_mass_tasks }
it{ expect{subject.deliver}.to change{ActionMailer::Base.deliveries.length}.by(1)}
its(:to) { should == [user.email] }
its(:subject) { should == "Task Reminding" }
end
end
Notice the change in the subject, because your mailer as it should returns the mail method result, which already initialized with to and subject.
By default Action Mailer does not send emails in the test environment. They are just added to the ActionMailer::Base.deliveries array. (See section 6 in http://guides.rubyonrails.org/action_mailer_basics.html)
What "by default" means is that you should not provide effective email_delivery settings for the test environment. If there are effective default email_delivery settings in "config/configuration.yml", you can override it with:
test:
email_delivery:
delivery_method: :test
How are you configuring ActionMailer? Is it within your config/application.rb file or within the specific environments/*.rb files?
Remember, anything you specify in the environments/*.rb files take precedence over the config/application.rb file.
So perhaps your problem is that when running in test mode the ActionMailer is using :smtp and not :test?