Defining my Custom devise authentication strategy throws 'load_missing_constant' error - ruby-on-rails-3

In my app, I am trying to define a custom strategy but it throws some errors... Here's what I am doing:
# myrailsapp/lib/devise/strategies
require 'devise/strategies/database_authenticatable'
module Devise
module Strategies
class CustomAuth < Devise::Strategies::DatabaseAuthenticatable
def valid?
Rails.logger.info("\n\nIn CustomAuth.valid\n\n")
true
end
def authenticate!
Rails.logger.info("\n\nCalled My Authenticate!\n\n")
super
end
end
end
end
Warden::Strategies.add(:custom_auth, Devise::Strategies::CustomAuth)
# config/initializer.rb
config.warden do |manager|
# manager.strategies.add(:custom_auth, Devise::Strategies::CustomAuth)
manager.default_strategies(:scope => :user).unshift :custom_auth
end
When I try to run this, I get this error:
/usr/lib/ruby/gems/1.8/gems/activesupport-3.1.1/lib/active_support/dependencies.rb:490:in load_missing_constant': Expected app/helpers/custom_auth.rb to define CustomAuth (LoadError)
from /usr/lib/ruby/gems/1.8/gems/activesupport-3.1.1/lib/active_support/dependencies.rb:181:inconst_missing'
from /usr/lib/ruby/gems/1.8/gems/activesupport-3.1.1/lib/active_support/dependencies.rb:179:in each'
from /usr/lib/ruby/gems/1.8/gems/activesupport-3.1.1/lib/active_support/dependencies.rb:179:inconst_missing'
from /usr/lib/ruby/gems/1.8/gems/activesupport-3.1.1/lib/active_support/dependencies.rb:501:in load_missing_constant'
from /usr/lib/ruby/gems/1.8/gems/activesupport-3.1.1/lib/active_support/dependencies.rb:181:inconst_missing'
from /usr/lib/ruby/gems/1.8/gems/activesupport-3.1.1/lib/active_support/dependencies.rb:179:in each'
from /usr/lib/ruby/gems/1.8/gems/activesupport-3.1.1/lib/active_support/dependencies.rb:179:inconst_missing'
from /usr/lib/ruby/gems/1.8/gems/activesupport-3.1.1/lib/active_support/dependencies.rb:501:in load_missing_constant'
from /usr/lib/ruby/gems/1.8/gems/activesupport-3.1.1/lib/active_support/dependencies.rb:181:inconst_missing'
from /usr/lib/ruby/gems/1.8/gems/activesupport-3.1.1/lib/active_support/dependencies.rb:179:in each'
from /usr/lib/ruby/gems/1.8/gems/activesupport-3.1.1/lib/active_support/dependencies.rb:179:inconst_missing'
from /home/rjain/src/qbol/tapp/config/initializers/devise.rb:212
Now, when I move the same file to my app/helpers directory, I get the same error, but if I remove the module Devise and module Strategies, things start to work. Two questions:
How to ensure that I can create the file in my lib/devise/strategies folder?
How to make it work so that I can use it within Devise::Strategies module as I described above?

Related

RSpec - uninitialized constant for instance double

I am getting a name error for a small API i am building with RSpec, using TDD methodology
NameError:
uninitialized constant ExpenseTracker::API::Ledger
API.rb
require "sinatra/base"
require "json"
module ExpenseTracker
class API < Sinatra::Base
def initialize(ledger: Ledger.new) # default value so callers can just say API.new and get the default
#ledger = ledger
super() # rest of initialization from Sinatra
end
# Later, callers do this ledger: Ledger.new
app = API.new()
post '/expenses' do
JSON.generate('expense_id' => 42)
end
get '/expenses/:date' do
JSON.generate([])
end
end
end
api_spec.rb
require_relative "../../../app/api"
require "rack/test"
module ExpenseTracker
RecordResult = Struct.new(:success?, :expense_id, :error_message)
RSpec.describe API do
include Rack::Test::Methods
def app
API.new(ledger: ledger)
end
let(:ledger) { instance_double('ExpenseTracker::Ledger') }
describe "POST /expense" do
context "when the expenseis successfully recorded" do
it 'returns the expense id'do
allow(ledger).to receive(:record)
.with(expense)
.and_return(RecordResult.new(true, 417, nil))
post '/expense', JSON.generate(expense)
parsed = JSON.parse(last_response.body)
expect(parsed).to include('expense_id': 417)
end
it 'responds with a 200 (OK)'
end
context "when the expense fails validation" do
it 'returns an error message'
it 'responds with 422 (Unprocessable entity)'
end
end
end
end
Below is the error that i am getting :
An error occurred while loading ./api_spec.rb.
Failure/Error:
def initialize(ledger: Ledger.new) # default value so callers can just say API.new and get the default
#ledger = ledger
super() # rest of initialization from Sinatra
end
NameError:
uninitialized constant ExpenseTracker::API::Ledger
/Users/Denis/Desktop/TDD/Effective_Testing_with_Rspec3/04-acceptance-specs/expense_tracker/app/api.rb:7:in `initialize'
/Users/Denis/Desktop/TDD/Effective_Testing_with_Rspec3/04-acceptance-specs/expense_tracker/app/api.rb:13:in `<class:API>'
/Users/Denis/Desktop/TDD/Effective_Testing_with_Rspec3/04-acceptance-specs/expense_tracker/app/api.rb:5:in `<module:ExpenseTracker>'
/Users/Denis/Desktop/TDD/Effective_Testing_with_Rspec3/04-acceptance-specs/expense_tracker/app/api.rb:4:in `<top (required)>'
./api_spec.rb:1:in `require_relative'
./api_spec.rb:1:in `<top (required)>'
Am I missing something? i have tried to play around with passing the ledger: Ledger.new to app = API.new but not getting past this error.
any help or ideas would be great !
Ok figured this one out, after a lot of head banging on the desk.
I had to comment out the app = API.new as this is not yet implemented, every time i would load the file in the spec it would error me out.
# Later, callers do this ledger: Ledger.new
app = API.new()
now on to the next failing spec lol !

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.

Allow my own class in Rails to be configured with block syntax

I often see code of the form
RSpec.configure do |config|
config.include ApiHelper
# ## 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
config.include FactoryGirl::Syntax::Methods
end
Is there a feature in Rails that lets me do something similar? I want to be able to configure my own library with a similar syntax within config/initializers/my_class.rb
MyClass.configure do |config|
# allow configuration here
end
Nothing special is needed in Rails - it is simple Ruby code. Here is how it can be done:
class MyClass
def self.configure(&block)
a_hash = { :car => "Red" }
puts "Hello"
yield a_hash
puts "There"
end
end
MyClass.configure do |config|
puts "In Block"
puts config[:car]
end
Output:
Hello
In Block
Red
There
I am yielding a hash, but you can yield whatever object you want to.
Rails will load all the Ruby files in the config/initializers directory when starting up the server.
If you want to use the same style for your own custom configurable class then you just have to implement a configure class method that accepts a block and passes a configuration object to that block. e.g.
class MyClassConfiguration
# configuration attributes
end
class MyClass
def self.configure
yield configuration if block_given?
end
def self.configuration
#config ||= MyClassConfiguration.new
end
end
Using phoet's gem would be even easier.
Its worth taking a look at how RSpec does it if you are curious:
The RSpec.configure method is in https://github.com/rspec/rspec-core/blob/master/lib/rspec/core.rb
The Configuration class is implemented in https://github.com/rspec/rspec-core/blob/master/lib/rspec/core/configuration.rb
i don't know if rails provides a helper for that, but i wrote my own tiny solution for this problem that i use in several gems: https://github.com/phoet/confiture
it let's you define configurations:
module Your
class Configuration
include Confiture::Configuration
confiture_allowed_keys(:secret, :key)
confiture_defaults(secret: 'SECRET_STUFF', key: 'EVEN_MOAR_SECRET')
end
end
and have an easy api to do the configuration:
Your::Configuration.configure do |config|
config.secret = 'your-secret'
config.key = 'your-key'
end
besides this, there are a lot of other config tools out there like configatron or simpleconfig.
A global configuration set via the block.
module VkRobioAPI
module Configuration
OPTION_NAMES = [
:client_id,
:redirect_uri,
:display,
:response_type
]
attr_accessor *OPTION_NAMES
def configure
yield self if block_given?
self
end
end
end
module VkRobioAPI
extend VkRobioAPI::Configuration
class << self
def result
puts VkRobioAPI.client_id
end
end
end
Example:
VkRobioAPI.configure do |config|
config.client_id = '3427211'
config.redirect_uri = 'bEWLUZrNLxff1oQpEa6M'
config.response_type = 'http://localhost:3000/oauth/callback'
config.display = 'token'
end
Result:
VkRobioAPI.result
#3427211
#=> nil
If you are using rails, you can just include ActiveSupport::Configurable into your class and off you go. Outside of rails, you will have to add the activesuport gem to your Gemfile or gemspec and then call require 'active_support/configurable'.
Example
class MyClass
include ActiveSupport::Configurable
end
Usage
With block
MyClass.configure do |config|
config.key = "something"
config.key2 = "something else"
end
or inline, without block
MyClass.config.key = "something"
MyClass.config.key2 = "something else"
or like a hash
MyClass.config[:key] = "somehting"
MyClass.config[:key2] = "something else"
Note: key can be any character of your choice.

Mongoid dynamic finder with Mongoid::Errors::DocumentNotFound exception raised

I'm building a REST api for this project that uses Mongoid.
I've setup the following to catch the Mongoid::Errors::DocumentNotFound exception:
rescue_from Mongoid::Errors::DocumentNotFound in my base controller
In my controller I've this query code:
#current_account.users.find(:first, :conditions => {:name => "some_name"})
The above query just returns nil. It doesn't raise the exception.
Tried with another syntax as well:
User.find(:conditions => {:name => "same"}).first
All those methods just runs where internally and afaik where doesn't raise exception, its simply returns []
So what can be the solution to this? I want partially dynamic finder but should raise the exception too?
I've met same problem today, and found another solution.
Set raise_not_found_error to false. so your config/mongoid.yml should be
development:
host: localhost
port: 10045
username: ...
password: ...
database: ...
raise_not_found_error: false
from http://mongoid.org/docs/installation/configuration.html
I believe that Mongoid will only raise a DocumentNotFound exception when using the find method by passing in an object's id (and not with conditions). Otherwise it will return nil. From the Mongoid source:
# lib/mongoid/errors/document_not_found.rb
# Raised when querying the database for a document by a specific id which
# does not exist. If multiple ids were passed then it will display all of
# those.
You will have to check manually to see if you got any results and either raise the DocumentNotFound exception yourself (not great), or raise your own custom exception (better solution).
An example of the former would be something like this:
raise Mongoid::Errors::DocumentNotFound.new(User, params[:name]) unless #current_account.users.first(:conditions => {:name => params[:name]})
Update: I haven't tested any of this, but it should allow you to make calls like (or at least point you in the right direction - i hope!):
#current_account.users.where!(:conditions => {:name => params[:name]})
Which will throw a custom Mongoid::CollectionEmpty error, if the collection returned from the query is empty. Note that it's not the most efficient solution, since in order to find out if the returned collection is empty - it has to actually process the query.
Then all you need to do is rescue from Mongoid::CollectionEmpty instead (or as well).
# lib/mongoid_criterion_with_errors.rb
module Mongoid
module Criterion
module WithErrors
extend ActiveSupport::Concern
module ClassMethods
def where!(*args)
criteria = self.where(args)
raise Mongoid::EmptyCollection(criteria) if criteria.empty?
criteria
end
end
end
end
class EmptyCollection < StandardError
def initialize(criteria)
#class_name = criteria.class
#selector = criteria.selector
end
def to_s
"Empty collection found for #{#class_name}, using selector: #{#selector}"
end
end
end
# config/application.rb
module ApplicationName
class Application < Rails::Application
require 'mongoid_criterion_with_errors'
#...snip...
end
end
# app/models/user.rb
class User
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Criterion::WithErrors
#...snip...
end

Is it possible to "watch" Haml files in Compass (stylesheet authoring tool)?

Currently, Compass is watching the .scss files inside the src folder and automatically updating the cs files. (by typing compass watch myproject).
Is there any way of including haml files in the "watching process"?
(I couldn't install StaticMatic because I don't want to install Ruby1.8).
Firstly, you should be using RVM. Installing any version of Ruby becomes painless.
Secondly, I just wrote this for myself, using the same 'fssm' gem that Compass uses to watch files. Add this to your / create a Rakefile:
require 'rubygems'
require 'fssm'
require 'haml'
class HamlWatcher
class << self
def watch
refresh
puts ">>> HamlWatcher is watching for changes. Press Ctrl-C to Stop."
FSSM.monitor('haml', '**/*.haml') do
update do |base, relative|
puts ">>> Change detected to: #{relative}"
HamlWatcher.compile(relative)
end
create do |base, relative|
puts ">>> File created: #{relative}"
HamlWatcher.compile(relative)
end
delete do |base, relative|
puts ">>> File deleted: #{relative}"
HamlWatcher.remove(relative)
end
end
end
def output_file(filename)
# './haml' retains the base directory structure
filename.gsub(/\.html\.haml$/,'.html')
end
def remove(file)
output = output_file(file)
File.delete output
puts "\033[0;31m remove\033[0m #{output}"
end
def compile(file)
output_file_name = output_file(file)
origin = File.open(File.join('haml', file)).read
result = Haml::Engine.new(origin).render
raise "Nothing rendered!" if result.empty?
# Write rendered HTML to file
color, action = File.exist?(output_file_name) ? [33, 'overwrite'] : [32, ' create']
puts "\033[0;#{color}m#{action}\033[0m #{output_file_name}"
File.open(output_file_name,'w') {|f| f.write(result)}
end
# Check that all haml templates have been rendered.
def refresh
Dir.glob('haml/**/*.haml').each do |file|
file.gsub!(/^haml\//, '')
compile(file) unless File.exist?(output_file(file))
end
end
end
end
namespace :haml do
desc "Watch the site's HAML templates and recompile them when they change"
task :watch do
require File.join(File.dirname(__FILE__), 'lib', 'haml_watcher')
HamlWatcher.watch
end
end
Run it with:
rake haml:watch