Looks like inflections don't work for module names with the nesting level more than one.
If you have the following in your config/initializers/inflections.rb:
ActiveSupport::Inflector.inflections do |inflect|
inflect.acronym 'VCloud'
end
Then when you create a directory under app/, say app/services/vcloud/ you will get two modules:
Vcloud #=> Vcloud
VCloud #=> VCloud
But if you create a directory with a higher nesting level, say app/services/vmware/vcloud/ you will get only one module:
Vmware::Vcloud #=> Vmware::Vcloud
Vmware::VCloud #=> NameError: uninitialized constant Vmware::VCloud
Is this a bug?
I would go with this is a bug. You can go around it with (within initializers):
module ActiveSupport::Inflector
def underscore_with_acronym_fix(string)
words = string.split('::')
return words.map(&method(:underscore)).join('/') unless words.one?
underscore_without_acronym_fix(string)
end
alias_method_chain :underscore, :acronym_fix
end
I'll make a pull request to fix this, however will need slightly more time to confirm it will not break anything. There are quite a lot of cases here.
I wonder if i could replicate this "issue".
Tried this running rails console.
> ActiveSupport::Inflector.camelize 'vcloud'
=> "Vcloud"
> ActiveSupport::Inflector.camelize 'v_cloud'
=> "VCloud"
There are test cases for all sorts of combinations below.
http://api.rubyonrails.org/classes/ActiveSupport/Inflector/Inflections.html#method-i-acronym
https://github.com/rails/rails/blob/master/activesupport/test/inflector_test_cases.rb
Related
I have a need to define an inflection for the word 'chassis' where the same word defines both singular and plural and I am really struggling with this.
I thought I was there with the initialize/inflections.rb definition
ActiveSupport::Inflector.inflections do |inflect|
# inflect.plural /^(ox)$/i, '\1en'
# inflect.singular /^(ox)en/i, '\1'
# inflect.irregular 'person', 'people'
# inflect.uncountable %w( fish sheep )
inflect.uncountable(/.*chassis.*/i)
end
Taking note of the example inflect.uncountable %w( fish sheep ) I tried to use inflect.uncountable %w( chassis ) when I first set up the scaffolding but that didn't work at all well as it didn't take into account leading parts in paths and caused problems with relationships and other tables like car_chassis and chassis_lookup.
Having looked at various solutions provided as answers to similar questions in Stack Overflow I eventually came up with inflect.uncountable(/.*chassis.*/i) Which seemed to take care of scaffolding but I'm having an issue with routes where <%= link_to "Chassis", admin_chassis_url%> gives me a no route for the show action error.
ActionController::RoutingError - No route matches {:action=>"show", :controller=>"admin/chassis"}
Which makes sense as I want the index action so I'm not passing an object to the path but Rails is obviously thinking I am requesting the show action
The other examples for regex's
# inflect.plural /^(ox)$/i, '\1en'
# inflect.singular /^(ox)en/i, '\1'
are just complete gobbledygook to me and learning regular expressions needs a lifetime of learning that I neither have the inclination or the sanity to get my head round and the rails documentation http://api.rubyonrails.org/classes/ActiveSupport/Inflector/Inflections.html on inflections is quite frankly pathetic.
I obviously haven't got the inflection right. Can anyone give me a complete solution as to exactly how I should define an inflection for the word "chassis" for me and for others please as none of the previous answers I've found provide a complete and proper solution
Your inflection seem to be correct.
Check what 'rake routes' tell you. In my case it was smart enough to detect that plural and single form of chassis was the same, so it generated admin_chassis_index instead of just admin_chassis for the #index action. Probably, the same is true for you. This is what I did:
In config/routes.rb
namespace :admin do
resources :chassis
end
Running 'rake routes' gives (note the first path):
admin_chassis_index GET /admin/chassis(.:format) admin/chassis#index
POST /admin/chassis(.:format) admin/chassis#create
new_admin_chassis GET /admin/chassis/new(.:format) admin/chassis#new
edit_admin_chassis GET /admin/chassis/:id/edit(.:format) admin/chassis#edit
admin_chassis GET /admin/chassis/:id(.:format) admin/chassis#show
PUT /admin/chassis/:id(.:format) admin/chassis#update
DELETE /admin/chassis/:id(.:format) admin/chassis#destroy
So, for #index I'd need to call:
<%= link_to "Chassis", admin_chassis_index_url%>
Very often when I have a missed expectation in a unit test using mocha, it spits out dozens or hundreds of "satisfied expectations" that I really don't care about. There's so much of it that I have to redirect testing output to a temp file, which is really annoying.
I am using Rails 2.3 and Mocha 0.10.0.
To clarify, I have the same issue as in
Mocha Mock Carries To Another Test
, and the solution there has not worked for me. However, even if I can get that resolved, I would like to suppress "satisfied expectations".
Thanks.
You could monkey patch mocha to achieve this. There's a method on Mocha::Mockery that returns the satisfied expectations which you could patch to return an empty array:
module Mocha
class Mockery
def satisfied_expectations
[]
end
end
end
If you put this in test_helper.rb it'll get picked up.
Alternatively for a bit more flexibility you could opt to only hide them when an environment variable is set:
module Mocha
class Mockery
def satisfied_expectations_with_optional
if ENV['MOCHA_HIDE_SATISFIED']
[]
else
satisfied_expectations_without_optional
end
end
alias_method_chain :satisfied_expectations, :optional
end
end
Then run your tests like this:
> MOCHA_HIDE_SATISFIED=1 rake test
I am upgrading a Rails 2 to Rails 3 application (code not written by me).
The (well tested code) uses shoulda and Test::Unit, and extensively uses the macros should_create and should_change.
I understand from this discussion that the shoulda maintainers want to get rid of both methods but that people using Test::Unit don't find it necessary (not sure I am grasping the whole discussion though).
Anaway, is there a way to selectively turn of the deprecation warnings for specified macros? I already know from this posting that you can turn off the deprecation warnings in the Rake test output entirely by setting:
ActiveSupport::Deprecation.silenced = true
in your the test environment file and I also know that you can put specific pieces of code in a block to get them silenced:
ActiveSupport::Deprecation.silence do
# no warnings for any use of deprecated methods here
end
The latter is an option but would require me to go over all the tests and enclose the should_create macros in such a block. So I was wondering there was a way to eliminate warnings for specific macros entirely with one configuration setting?
Old question - but if you have new depreciations you'd like to selectively ignore:
ActiveSupport::Deprecation.behavior = lambda do |msg, stack|
unless /LIBRARY_NAME/ =~ msg
ActiveSupport::Deprecation::DEFAULT_BEHAVIORS[:stderr].call(msg,stack) # whichever handlers you want - this is the default
end
end
This is for ActiveSupport 3.
In fact I stil had lots of other deprecation warnings from code that was in plugins or gems I had installed. In order to avoid most of that, I overwrote the Deprecation::warn method in test_helper.rb. So instead of the previous code, use:
module ActiveSupport
module Deprecation
class << self
def warn(message = nil, callstack = caller)
# modif pvh the following lines make sure no deprecation warnings are sent
# for code that is
# not by my but in some gem or plugin...
return if silenced || callstack.grep(/myrailsappname/).blank?
# return if silenced
deprecation_message(callstack, message).tap do |m|
behavior.each { |b| b.call(m, callstack) }
end
end
end
end
end
BTW you need to replace myrailsappname with your app's name (the name of the folder it resides in). There is probably a more generic way to get that name, but I couldn't find it right now.
Can I recommend an alternative?
module ActiveSupport
class Deprecation
module Reporting
# Mute specific deprecation messages
def warn(message = nil, callstack = nil)
return if message.match(/Automatic updating of counter caches/)
super
end
end
end
end
I think I have found a solution: in test/test_helper.rb I reopened the module and overwrote macro definition with an identical definition but the deprecation warning commented out. There are probably much more elegant ways to do this though...
# modif pvh DEPREC
# reopen the module and silence the deprecation statement to avoid
# having my results overflown by these deprecation warnings...
module Shoulda # :nodoc:
module Macros
def should_create(class_name)
##::ActiveSupport::Deprecation.warn
should_change_record_count_of(class_name, 1, 'create')
end
end
end
Create a file called selective_deprecation_silencer.rb in your config/initializers folder with the following content:
#place in the following array the messages you want to silence
silenced = [/Using a dynamic :action segment in a route is deprecated/,
/Using a dynamic :controller segment in a route is deprecated/]
silenced_expr = Regexp.new(silenced.join('|'))
ActiveSupport::Deprecation.behavior = lambda do |msg, stack, deprecation_horizon, gem_name|
unless msg =~ silenced_expr
ActiveSupport::Deprecation::DEFAULT_BEHAVIORS[:stderr].call(msg, stack, deprecation_horizon, gem_name)
end
end
My question is very similar to this one: How do I add a method to a ruby gem without editing the gem source?. However, this question is almost a year old and the solution that was chosen isn't the cleanest, not to me at least.
The person who provided the answer offered 3 suggestions. The first suggestion was chosen as the answer, but I would really like to figure out how to do it the second way.
I need to override an instance method of a class that is defined by a Gem. More specifically, it is the SessionSerializer class in 1.1.2 Devise. The issue is that Devise doesn't respect non-standard primary key names. It always uses id. You can see that in warden_compat.rb on Line 30, it uses the following to find a model by it's ID:
klass.constantize.find(:first, :conditions => { :id => id })
In my case, the name of my id column is application_user_id, so it is obvious that this won't work. Devise has fixed this issue in 1.1.3, however, I cannot use 1.1.3 because the Devise LDAP Authenticatable plugin does not support 1.1.3.
So here's what I've done instead. I should mention first that I tested this fix by editing the Gem source directly, so now I simply want to move it into my project.
Created a session_serializer.rb file in lib/warden/ (i.e., lib/warden/session_serializer.rb), reopened the Warden::SessionSerializer class, and redefined the deserialize method.
Modified application.rb to include lib/ in config.autoload_paths
config.autoload_paths += ["#{config.root}/lib"]
However, this doesn't seem to do the trick. It is still using the same code that is defined in the Gem source. So I have couple questions that I hope that can be answered:
Questions
What am I doing wrong here?
Does Rails load files of the paths defined in config.autoload_paths before Gems, or is it the other way around?
Thanks for the help in advance!
lib/warden/session_serializer.rb
module Warden
class SessionSerializer
def deserialize(keys)
klass, id = keys
if klass.is_a?(Class)
raise "Devise changed how it stores objects in session. If you are seeing this message, " <<
"you can fix it by changing one character in your cookie secret, forcing all previous " <<
"cookies to expire, or cleaning up your database sessions if you are using a db store."
end
# NOTE: Original line code. Notice that it uses an :id symbol. It doesn't respect the primary key that explicity defined in the model
# klass.constantize.find(:first, :conditions => { :id => id })
# NOTE: THIS IS THE FIX
klass.constantize.find(:first, :conditions => { :application_user_id => id })
rescue NameError => e
if e.message =~ /uninitialized constant/
Rails.logger.debug "Trying to deserialize invalid class #{klass}"
nil
else
raise
end
end
end
end
I would create a file called warden.rb in initializers directory and put the monkey patch code inside the file. I use this technique often in my projects to patch a gem.
To put the patch under the lib directory, do the following:
config.autoload_paths += ["#{config.root}/lib/warden"]
PS: I know you have tried this, but it looks like your path is not correct.
PPS To understand the Rails 2.3 load sequence refer to this code.
Have you read:
http://guides.rubyonrails.org/configuring.html
?
I specified the default_url_options in my environments/test.rb with
config.action_mailer.default_url_options = { :host => "www.xyu.at" }
This is quite ok and in my cucumber story, where i test registration of users,
the user-activation link gets generated right
invitation_activation_url(1)
=> "www.xyu.at/signup/1231hj23jh23"
But when I try to follow the link provided in the email with following code in features/steps/user_steps.rb (using email-rspec from http://github.com/bmabey/email-spec/tree/master):
When /^I follow the invitation link$/ do
When 'I follow "'+invitation_activation_url(1) + '" in the email'
end
Here the url gets created with the default-host:
invitation_activation_url(1)
=> "www.example.com/signup/1231hj23jh23"
Can anybody help me? I don't get what I'm doing wrong....
Thanks!
EDIT:
It seems to do with the method
current_url
but I dont know where it comes from..?
EDIT:
And I have the right environment specified in my features/support/env.rb
ENV["RAILS_ENV"] ||= "test"
EDIT:
My temporary solution is, what edbond said,
invitation_activation_url(1, :host => "www.xyz.at")
=> "www.xyz.at/signup/1231hj23jh23"
but I dont want to name the domain explicit this way
(i specified it already in my environments/test.rb file - that way it wouldnt be dry)
Use :host option in your url.
invitation_activation_url(1, :host => "www.xyz.at")
=> "www.xyz.at/signup/1231hj23jh23"
EDIT:
You can parse email body and get link
mail = YourMailer.deliveries.last
email_html = Nokogiri::HTML mail.body.to_s
approve_link = email_html.at_css("a")["href"]
I know its years since this was posted.. but I had this issue and took me hours to decipher until I got it figured out.
You should use instead
When /^I follow the invitation link$/ do
When 'I follow "'+invitation_activation_path(1) + '" in the email'
end
the _url generates the URL path; whilst the _path generates the URI path.
web_steps.rb uses the URI to determine the current_url which it uses to work out the host.
from http://api.rubyonrails.org/classes/ActionDispatch/Routing.html
Routes can be named by passing an :as option, allowing for easy
reference within your source as name_of_route_url for the full URL and
name_of_route_path for the URI path.
from web_steps.rb
Then /^(?:|I )should be on (.+)$/ do |page_name| | # end
current_path = URI.parse(current_url).path | #
if current_path.respond_to? :should | # collection do
current_path.should == path_to(page_name) | # get 'sold'
else | # end
assert_equal path_to(page_name), current_path | # end
end |
end
You say that you edited config/environments/test.rb. Are you sure that your Cucumber features are actually executing in the 'test' environment?
I recently added Cucumber to a project I'm working on, and it seems to set itself up to use a 'cucumber' environment by default.
In features/support/env.rb in my project there is this:
ENV["RAILS_ENV"] ||= "cucumber"
So if your project is similar, then you will need to customize config/environments/cucumber.rb as well.
I'm not terribly familiar with Cucumber, so I can't say with certainty where exactly you'll have to apply this fix. But the problem is that the default_url_options is not set in another place where you're trying to generate your url...
So my advice is to first find out in what context the faulty url is being generated. Before or after it, just output self.class. That's the class you'll have to monkey-patch. For the example, let's say 'ClassName' was printed out.
When you have that, in your config/environments/test.rb, just add the attribute accessor and then set it to what you want:
class ClassName
cattr_accessor :default_url_options
# or mattr_ if it's a module
end
and then set it the same way as your actionmailer
ClassName.default_url_options = { :host => "www.xyu.at" }
This whole process can be useful as well when you want to generate urls in models or in other esoteric places (then you'll also need to include ActionController::UrlWriter).
One solution (based on info here) is to have a step definition like
Given /^the domain "(.+?)"$/ do |domain|
host! domain
end
and use it like
Given the domain "www.foo.com"
in features.
With that, though, I ran into issues where redirects were not followed. I tried applying this patch but had no luck.
I ended up using a very simple workaround in Cucumber's env.rb file:
# There is a bug in internal_redirect? when used with explicit (non-example.com) domains.
# This is a simple workaround but may break if we start specing external redirects.
# https://webrat.lighthouseapp.com/projects/10503/tickets/140-current_url-should-be-fully-qualified
class Webrat::Session
alias internal_redirect? redirect?
end
As mentioned in the comment, this may of course break with external redirects, but we have none.