How to use a digest-aware asset for default_url in rails4? - asset-pipeline

In rails 3, to use an asset from the pipeline as the default_url in carrierwave's uploader, you did something like the following:
class MyUploader
# Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility:
include Sprockets::Helpers::RailsHelper
include Sprockets::Helpers::IsolatedHelper
def default_url
# For Rails 3.1+ asset pipeline compatibility:
asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
end
end
In rails 4, the pipeline has been abstracted into the sprockets-rails gem/railtie, so the above will give you:
uninitialized constant Sprockets::Helpers
Looking at the sprockets-rails gem, the replacement helper would seem to be Sprockets::Rails::Helper. But with this module included, asset_path("fallback/default.png") returns, simply:
# wrong:
"/fallback/default.png"
not the asset- and digest-aware URL that I expect:
"/assets/fallback/default-b3beee1588afe2ae582c64888cd007db.png"
How can I get the correct asset_path behavior outside of a view?

TL;DR:
Don't include anything. Use the helper proxy instead, like this:
def default_url
ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
end
The Gory Details:
Sprockets::Rails::Helper makes use of a sprockets manifest to properly locate and digest assets. Once it got abstracted into a gem for rails 4, this manifest started getting set by the railtie. So simply include-ing the module's methods into our class wasn't enough to pull that manifest in, and asset_path (mis)behaved accordingly.
Thankfully, there is the oft-overlooked ActionController::Base.helpers class method:
Provides a proxy to access helpers methods from outside the view.
Like the docs say, it acts as a helper proxy and is probably what should have been used for this purpose from the begining (including helper modules into your namespace runs the risk of polluting your class with unused methods and potentially overwriting stuff, so using the proxy is the better alternative).
There might yet be some equivalent of Sprockets::Helpers::IsolatedHelper that hacks enough together to give Sprockets::Rails::Helper what it needs to get the job done. But the proxy is, IMHO, a much more elegant solution. So I've stopped searching.

Related

Is there a way in Sinatra to separate helpers used in views and helpers used in routes?

All helpers in Sinatra can be accessed from both my HAML code and my routes. But I'm finding myself writing two completely separate sets of code for these two.
Ideally I'd like to keep them apart. Currently I have them in two different directories, but that seems kind of pointless. Can anyone suggest a way I can limit the potential damage caused by namespace collision?
At first I thought modules. From the Sinatra readme:
module FooUtils
def foo(name) "#{name}foo" end
end
module BarUtils
def bar(name) "#{name}bar" end
end
helpers FooUtils, BarUtils
But is there any point in actually doing this? Aren't they going to have to share the same namespace inside my Sinatra app?
Some tentative and not-entirely-satisfactory answers:
1) Methods defined in the same way as helpers but without the call to Sinatra::helpers don't appear to be accessible from views, but are accessible from within the Sinatra application block. That is:
require 'sinatra'
module Sinatra
module MyThing
# Helper classes and methods go here
end
helpers MyThing # <- leave this out if you don't want access from views
end
2) You can of course also have MyThingHelpers and MyThingLib modules, instead of just MyThing, to prevent namespace collision.
3) And you can put them in separate subdirectories.
I've yet to be convinced that these represent a comprehensive solution to my concerns. Time and testing will tell, I suppose, unless someone can give me a better answer here.

Use RSpec's "expect" etc. outside a describe ... it block

I'm building a web-app automation framework that is designed to allow for:
automating tasks, of course
building test scenarios easily
I'm using Capybara to communicate with a browser and I have a library of components with a number of helper functions (login_to_the_back_office, create_customer, etc.).
Now I would like my components to be usable standalone as well as within RSpec tests. This means that my components, as included in the library, are not wrapped around describe... it... blocks by default, but they will be at some point when a test uses them and so they should use expect and friends as often as possible.
I followed rspec's .should fails (outside describe/it block) in Ruby 2? to enable should and it works (actually just requiring rspec-expectations was enough in my case), but I can't figure out how to make expect work.
I think expect is defined here https://github.com/rspec/rspec-expectations/blob/master/lib/rspec/expectations/syntax.rb but my knowledge of Ruby's meta-magic is too limited to figure out how to make expect available to my class.
I've tried including RSpec::Expectations::Syntax in my class but I still get undefined method 'expect' whenever I try to use it.
How can I use expect outside of describe... it... ?
include ::RSpec::Matchers
class A
include ::RSpec::Matchers
def test
expect('1'.to_i).to eq 1
end
def failed_test
expect('1'.to_i).to eq 2
end
end
A.new.test
# => true
A.new.failed_test
# RSpec::Expectations::ExpectationNotMetError:
# expected: 2
# got: 1

How to test a Padrino controller from Padrino console

I know in Rails I can do something like
app.get 'url'
app.response
But this does not work in Padrino, nor do any of the regular controller calls because Padrino uses different controller methods than Rails.
What I'm trying to do is test my controller methods from the Ruby Padrino MRI console. For example, I want to store the objects present, call the same method 100 times, then compare what objects are left behind. I'm trying to find a memory leak.
So it would be great to be able to call the method from a Padrino console.
I can't find anything that tells me how to do it in the official documentation or elsewhere.
The get in you Padrino::Application is just part of the DSL to define new routes, not to retrieve their contents. What you are trying to achieve is usually part of the Rack::Test library.
Have a look at the Sinatra documentation:
http://www.sinatrarb.com/testing.html
Specially compare the sections about Rack::Test with Mixin VS without Mixin. That should make you understand where the fetching get comes from.
In your case, if you want to test from the console, then it should be something like this part:
require 'rack/test'
browser = Rack::Test::Session.new(Rack::MockSession.new(Padrino::Application))
browser.get '/'
Now, where you see Padrino::Application you must type your own application main class that inherits from that class, not the abstract class itself
Notice that the result will be a Rack::MockSession object, so if you just want to see the html do:
browser.get('/').body

Loading attributes only once in Grape

I'm little new to Ruby, but I have the following coded using Grape API in Ruby. I have #data = YAML.load() called every time it hits GET /api/v1/foo, is there a way in Grape to only load it once and use it? This way is more optimized and not calling YAML.load() at every time. Should I override the initialize method and put a super() for this operation?
Thanks,
require 'grape'
require 'json'
require "yaml"
module MyProject
CONFIG_FILE = "./config.yml"
class Api < Grape::API
rescue_from :all
prefix 'api'
version 'v1'
format :json
resources :foo do
get do
#data = YAML.load(File.open(MyProject::CONFIG_FILE))
end
end
end
end
The short answer is that Grape doesn't work quite how you think, and attribute variables of the MyProject::Api are not the way forward for your new web service. However, it is an interesting question, and worth exploring why this is so.
If you add a puts self.inspect inside the resources :foo block, and run using rackup, when you call the route you should see that self is in fact a Grape::Endpoint object. Also, no matter what you try to do with instance variables, they will always start in the same state for each request. That is because Grape turns your route definitions into prepared Grape::Endpoint objects, with a lot of the definition data and setup put into a quickly-accessible form (so that it is not figured out on each request). Eventually, on each request, the matching Grape::Endpoint object including your block (and other details you defined for the route) is duplicated before being called, meaning that state is not maintained between requests.
This may seem complicated, but most frameworks covering web service requests will do something similar. Generally you don't want request-handling state to persist between requests. Frameworks with larger scope - e.g. Rails - have places to put more persistent data planned out for you. Grape does not have this defined, which has its pros and cons. One obvious plus point is that you are more free to use whatever other data persistence approach that you wish to.
23tux's answer will sort you out immediately for loading config. Although I'm not entirely sure how ##data becomes accessible to the endpoint block (it may even be creating a closure around the variable).
Longer term, you should look to moving config management out of your MyProject::Api class, and including it as a module via Grape's helpers method (I'm happy to provide an example if you are interested).
Edit: Example based on your current code, but moving config management to a separate module:
require 'grape'
require 'json'
require "yaml"
module MyProject
module Config
CONFIG_FILE = "./config.yml"
##data = nil
def config
##data ||= YAML.load( File.open( CONFIG_FILE ) )
end
end
class Api < Grape::API
rescue_from :all
prefix 'api'
version 'v1'
format :json
helpers MyProject::Config
resources :foo do
get do
config
end
end
end
end
This is one step further, structurally, than 23tux's answer, but is still not completely separating concerns of storage (and caching etc) versus api access. As you progress towards a more sophisticated web service, you will want to keep the Grape route definitions simple, with only a small amount of logic that manages or manipulates the data - well, at least as seen directly in the blocks.
One way to link between your Grape definitions, and other gems that might manage config, logging, authentication and other services, is via Grape's helpers method. Grape also has some built-in helper methods for common tasks.
The main exception to using helpers MyModule to add shared functions into Grape is when you want to manage displaying data objects (aka "models") from your core application. For that you have a few choices, but the grape-entity gem and the present method is not a bad place to start.
If the #data is the same for the whole api, and doesn't change at any time, just use a class variable
require 'grape'
require 'json'
require "yaml"
module MyProject
CONFIG_FILE = "./config.yml"
class Api < Grape::API
##data = YAML.load(File.open(MyProject::CONFIG_FILE))
rescue_from :all
prefix 'api'
version 'v1'
format :json
resources :foo do
get do
puts ##data
end
end
end
end
Not tested, but with this way, you ensure that the data is only loaded once, when your Api class is loaded

Kaminari and Capybara conflict

I seem to have some sort of conflict between the page method of capybara and the page method of Kaminari.
That's what I guessed, anyway, here is the error :
Failure/Error: before { sign_in_as user }
ActionView::Template::Error:
wrong number of arguments (1 for 0)
# ./app/models/feed.rb:9:in `microposts'
[Rest of the backtrace]
The code sample :
class Feed
def microposts(opts = { urgent: false })
urgent = opts[:urgent]
p Microposts.where(id: 1).page # <Capybara::Session>
p Microposts.where(id: 1).page(1) # Error
end
end
If I remove the pagination, the test works fine.
I don't understand how this is possible, I guess Capybara is adding the "page" method to the Object scope, but as Kaminari add its page method to ActiveRecord::Base (if I recall correctly) it should override Capybara's one.
I did not see anyone having this kind of trouble, how is it possible ?
Thanks.
I had the same problem with Capybara 2.x
My feature specs are in the spec/feature directory. I realised from reading the Capybara documentation that there is no need to include the Capybara::DSL in your spec_helper if your using the features directory. It's already included.
There is a warning given if you include Capybara::DSL in the spec_helper that it will pollute the global namespace and this is exactly why it's a bad idea!
Check out this rspec-rails page on Capybara for details
This is a little bit of a hack but I was able to work around the problem (where Capybara 'pollutes' the object space) by undef-ing the method in my spec:
# Capybara adds a 'page' method to the Object class which conflicts with the Kaminari scope
# Remove it here to allow things to work
Object.send :undef_method, :page
I have traced back where this is happening and essentially:
The #page method comes from Capybara::DSL
The Capybara::DSL method is included into the Object class via RSpec's #configure.include method (see lib/capybara/rspec.rb).
RSpec then includes it into the 'group', however I believe this is where it drops into Object.
The solution here might just be to change the name of the method in Capybara, but I guess thats not a decision I'm willing to make :)

Resources