I want to create a gem which can add some helper method to rails:
module SharpAssets
class Railtie < Rails::Railtie
initializer "my_railtie.configure_rails_initialization" do |app|
app.middleware.use ::ActionDispatch::Static, "public"
ActionView::Base.send :include, ViewHelpers
end
config.to_prepare do
ActionView::Helpers.class_eval do
#ActionView::Base.send :include, SharpAssets::ViewHelpers
end
end
end
module ViewHelpers
#include Generators::Pagedown
puts "initializer my_railtie.configure_rails_initialization"
def sharp_assets
"sharp_assets"
end
end
end
and my test case is:
require 'test_helper'
class SharpAssetsTest < ActionView::TestCase
test "pagedown_tag be html safe" do
assert sharp_assets, ""
end
end
and my test_helper is :
# Configure Rails Environment
ENV["RAILS_ENV"] = "test"
require File.expand_path("../dummy/config/environment.rb", __FILE__)
require "rails/test_help"
Rails.backtrace_cleaner.remove_silencers!
# Load support files
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
# Load fixtures from the engine
if ActiveSupport::TestCase.method_defined?(:fixture_path=)
ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__)
end
and my gem's directory is:
The error is
NameError: undefined local variable or method `sharp_assets' for #<SharpAssetsTest:0x007ffe3c5abf08>
Did you required your lib in your test_helper.rb?
Related
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.
My structure is something like (base on this):
# lib/myapp/uploader.rb
require 'uploader/base'
# lib/myapp/uploader/base.rb
module MyApp
module Uploader
class Base
end
end
end
# app/models/model.rb
class Model < ActiveRecord::Base
def test
MyApp::Uploader::Base
end
end
Called directly in console:
$ MyApp::Uploader::Base #> MyApp::Uploader::Base
Through model in console:
$ Model.new.test
LoadError: Expected <snip>/lib/myapp/uploader.rb to define Uploader
Why is this, and how can I fix it?
Updated Answer:
In this case, you missing the require in your Model
So add the following in your app/models/model.rb
# app/models/model.rb
class Model < ActiveRecord::Base
require Rails.root.join('lib').join('myapp').join('uploader').join('base').to_s
def test
MyApp::Uploader::Base
end
end
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.
I've modularised by classic sinatra app and moved my routes in my sinatra app into individual routes files as per https://stackoverflow.com/a/5030173/111884, however, I can't seem to get my tests working.
This is what my files look like:
./web.rb
require 'sinatra'
require 'sinatra/flash'
class MyApp < Sinatra::Application
# ...
end
require_relative 'models/init'
require_relative 'helpers/init'
require_relative 'routes/init'
./routes/init.rb
require_relative 'main'
./routes/main.rb
# The main routes for the core of the app
class MyApp < Sinatra::Application
get '/' do
erb :main
end
end
./spec/spec_helper.rb
ENV['RACK_ENV'] = 'test'
require 'minitest/autorun'
require 'rack/test'
require 'factory_girl'
# Include factories.rb file
begin
require_relative '../test/factories.rb'
rescue NameError
require File.expand_path('../test/factories.rb', __FILE__)
end
# Include web.rb file
begin
require_relative '../web'
rescue NameError
require File.expand_path('../web', __FILE__)
end
./spec/web_spec.rb
begin
require_relative 'spec_helper'
rescue NameError
require File.expand_path('spec_helper', __FILE__)
end
include Rack::Test::Methods
def app() Sinatra::Base end
describe "Some test" do
# ...
end
Rakefile
# Test rake tasks
require 'rake/testtask'
Rake::TestTask.new do |t|
t.libs << "test"
t.libs << "spec"
t.test_files = FileList['test/factories.rb', 'test/test_*.rb', 'spec/spec_helper.rb', 'spec/**/*_spec.rb']
t.verbose = true
end
The output of the tests is:
<h1>Not Found</h1>
It doesn't seem to be loading the ./routes/*.rb files.
I'm using Sinatra::Application, instead of Sinatra::Base, but https://stackoverflow.com/a/5030173/111884 uses it. It also references it here http://www.sinatrarb.com/extensions.html. I have tried changing it to use Sinatra::Base, but it didn't fix it.
I've also tried Sinatra tests always 404'ing and Using Cucumber With Modular Sinatra Apps, but they don't work.
I think you just need to change your app method to return your modularised application class (MyApp) rather than the Sinatra::Base class. So replace:
def app() Sinatra::Base end
in web_spec.rb, with:
def app
MyApp
end
Rack::Test::Methods depends on the app method to tell it which class to call to process requests. In a simple non-modular Sinatra application that class is Sinatra::Base because that is the class in which the routes are applied by default. In a modular application that is the class in which you define your routes (MyApp in your case).
I'm trying to implement some unit tests in my rails3 application. The following is my 'test_helper.rb' file.
ENV["RAILS_ENV"] = "test"
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
class ActiveSupport::TestCase
include Devise::TestHelpers
fixtures :all
end
Following my fixture file (site_layouts.yml)
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
one:
name: layout1
content: <html><head></head><body>layout1</body></html>
two:
name: layout2
content: <html><head></head><body>layout2</body></html>
and following is my Unit test class
require 'test_helper'
class SiteLayoutTest < Test::Unit::TestCase
fixtures :site_layouts
test "show site layout" do
layout = site_layouts(:one)
end
end
and when I try to run the rake test:units I'm getting the following error
undefined method `fixtures' for SiteLayoutTest:Class (NoMethodError)
I'm a little new to testing. I'm on
Rails 3.0.0
Linux
test-unit (2.4.1, 2.4.0, 2.3.2, 1.2.3)
test-unit-notify (0.3.0)
I think you don't need the fixtures :site_layouts call inside SiteLayoutTest. Rails should auto load your fixtures.
More about fixtures in the Rails Guides.
Should you derive from the ActiveSupport::TestCase you've created in test_helper.rb? That should autoload your fixtures
require 'test_helper'
class SiteLayoutTest < ActiveSupport::TestCase
test "show site layout" do
layout = site_layouts(:one)
end
end