Say I have these translations:
en:
article:
name: "article"
Is it possible to do something like:
test1: "We have zero #article.name.pluralize"
test2: "There is an error in your #article.name"
test3: "Title for this page is: My #article.name.pluralize.capitalize"
I'm not too fussed about 1 and 3 (i.e. performing additional functions on the variable), but test2 would be a great help.
This also begs the question: is this a good way to structure translation files? I ask this because other languages can be structured in a completely different way. I'm just thinking of ways to DRY up my translation files.
You can possibly define a translation like this:
test1: "We have zero %{things}"
And then, somewhere in your code you can use it like this:
I18n.t(:test1, :scope => [:flashes], :things => t(:name, :scope => [:article] ))
or, using different notation:
I18n.t("flashes.test1", :things => I18n.t("article.name"))
Related
I've yet to get my head around the rspec way of writing tests, but would appreciate some help in understanding what I've done wrong with this test.
describe Source do
describe "Upcase name" do
names = {'bob' => 'Bob',
'edward jones' => 'Edward Jones'
'Edward jones' => 'Edward Jones'}
names.each do |name,expect|
before { #source = Source.create(name: name) }
after { #source.destroy! }
it "source #{name} should be #{expect}" do
subject { #source }
#source.name.should == expect
let!(:find) do
Source.find_by_proper_name(expect)
end
it "should find" do
should == find
end
end
end
end
end
I have a unique constraint on the name column of the source model.
Every iteration of the test comes back as a failure due to unique violation. (even though I thought it would create only one model per iteration.
If I take out the middle of the three so all names are unique if it creates all at once - then the tests fail because #source is equal to the last value in all tests.
Essentially I want a DRY way to write the tests
Source.create(name: "bob").name.should == "Bob"
Source.create(name: "edward jones").name.should == "Edward Jones"
Source.create(name: "Edward jones").name.should == "Edward Jones"
How should I go about it?
you are mixing a lot of stuff in there...
first things first:
do not nest it
do not use let in it they belong in context
do not iterate its iterate in the it
do not cleanup the db yourself, use database cleaner or transactional fixtures or something like that
have a look at how i do spec like these, maybe this helps you somehow: https://github.com/phoet/on_ruby/blob/master/spec/models/user_spec.rb#L15
I have the following validation in my ActiveRecord.
validates :active, :inclusion => {:in => ['Y', 'N']}
I am using the following to test my model validations.
should_not allow_value('A').for(:active)
should allow_value('Y').for(:active)
should allow_value('N').for(:active)
Is there a cleaner and more through way of testing this? I am currently using RSpec2 and shoulda matchers.
EDIT
After some looking around I only found, this probably an 'ok' way of testing this, shoulda does not provide anything for this and anyone who requires it can write their own custom matcher for it.(And probably contribute it back to the project). Some links to discussions that might be intresting:
Links which indicate to the above . Link 1 , Link 2
should_ensure_value_in_range This one comes close to what can be used, but only accepts ranges and not a list of values. Custom matcher can be based on this.
Use shoulda_matchers
In recent versions of shoulda-matchers (at least as of v2.7.0), you can do:
expect(subject).to validate_inclusion_of(:active).in_array(%w[Y N])
This tests that the array of acceptable values in the validation exactly matches this spec.
In earlier versions, >= v1.4 , shoulda_matchers supports this syntax:
it {should ensure_inclusion_of(:active).in_array(%w[Y N]) }
If you have more elements to test than a boolean Y/N then you could also try.
it "should allow valid values" do
%w(item1 item2 item3 item4).each do |v|
should allow_value(v).for(:field)
end
end
it { should_not allow_value("other").for(:role) }
You can also replace the %w() with a constant you have defined in your model so that it tests that only the constant values are allowed.
CONSTANT = %w[item1 item2 item3 item4]
validates :field, :inclusion => CONSTANT
Then the test:
it "should allow valid values" do
Model::CONSTANT.each do |v|
should allow_value(v).for(:field)
end
end
I found one custom shoulda matcher (in one of the projects I was working on) which attempts to coming close to test something like this:
Examples:
it { should validate_inclusion_check_constraint_on :status, :allowed_values => %w(Open Resolved Closed) }
it { should validate_inclusion_check_constraint_on :age, :allowed_values => 0..100 }
The matcher tries to ensure that there is a DB constraint which blows up when it tries to save it.I will attempt to give the essence of the idea. The matches? implementation does something like:
begin
#allowed_values.each do |value|
#subject.send("#{#attribute}=", value)
#subject.save(:validate => false)
end
rescue ::ActiveRecord::StatementInvalid => e
# Returns false if the exception message contains a string matching the error throw by SQL db
end
I guess if we slightly change the above to say #subject.save and let Rails validation blow up, we can return false when the exception string contains something which close matches the real exception error message.
I know this is far from perfect to contributed back to the project, but I guess might not be a bad idea to add into your project as a custom matcher if you really want to test a lot of the :inclusion validation.
I'm running RSpec tests against a website product that exists in several different markets. Each market has subtly different combinations of features, etc. I would like to be able to write tests such that they skip themselves at runtime depending on which market/environment they are being run against. The tests should not fail when run in a different market, nor should they pass -- they're simply not applicable.
Unfortunately, there does not seem to be an easy way to mark a test as skipped. How would I go about doing this without trying to inject "pending" blocks (which aren't accurate anyway?)
Use exclusion filters.
describe "market a", :market => 'a' do
...
end
describe "market b", :market => 'b' do
...
end
describe "market c", :market => 'c' do
...
end
RSpec.configure do |c|
# Set these up programmatically;
# I'm not sure how you're defining which market is 'active'
c.filter_run_excluding :market => 'a'
c.filter_run_excluding :market => 'b'
# Now only tests with ":market => 'c'" will run.
end
Or better still, use implicit filters.
describe "market a", :if => CurrentMarket.a? do # or whatever
...
end
I spotted this question looking for a way to really skip examples that I know are going to fail now, but I want to "unskip" them once the situation's changed. So for rspec 3.8 I found one could use skip inside an example just like this:
it 'is going to fail under several circumstances' do
skip("Here's the reason") if conditions_met?
expect(something)
end
Actually, in most situations (maybe not as complicated as in this question) I would rather use pending instead of skip, 'cause it will notify me if tests stop failing, so I can "unskip" them. As we all know that skipped tests will never be performed ever again =)
Ultimately, I would like to use Inflector.parameterize to create slugs for article heading that have a bunch of unicode chars in them (e.g. "ḤellẒ no" => "hellz-no"). According to http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-transliterate it says to put them in the locales/en.yml file.
# Store the transliterations in locales/en.yml
i18n:
transliterate:
rule:
Ḥ: "h"
Ẓ: "z"
I tried that but the following does not work:
"ḤellẒ no".parameterize
# => "ell-no"
However, when I change it in Ruby like the second paragraph suggests, it works.
I18n.backend.store_translations(:en, :i18n => {
:transliterate => {
:rule => {
"Ḥ" => "H",
"Ẓ" => "Z"
}
}
})
"ḤellẒ no".parameterize
# => "hellz-no"
I guess I would like to know why putting the custom transliterations in locales/en.yml doesn't work.
And even if someone give the answer for that, being a Rails noob, I would also like to know where one usually puts code like the second block to manually set the I18n.backend.store_translations?
Ehh, I've got a part of the answer. Unlike what the doc at http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-transliterate says, the yml files should still specify the language - i.e. de:
# Store the transliterations in locales/de.yml
de:
i18n:
transliterate:
rule:
ü: "ue"
ö: "oe"
Please still answer the second part of the question, where should code like I18n.backend.store_translations(:en,... live in a Rails 3 app?
[...] where should code like I18n.backend.store_translations(:en,... live in a Rails 3 app?
I know. I might be a little late on this but I would put it into an initializer file: config/initializers/i18n.rb
I'm writing a Rails 3 ActiveRecord query using the "where" syntax, that uses both the SQL IN and the SQL OR operator and can't figure out how to use both of them together.
This code works (in my User model):
Question.where(:user_id => self.friends.ids)
#note: self.friends.ids returns an array of integers
but this code
Question.where(:user_id => self.friends.ids OR :target => self.friends.usernames)
returns this error
syntax error, unexpected tCONSTANT, expecting ')'
...user_id => self.friends.ids OR :target => self.friends.usern...
Any idea how to write this in Rails, or just what the raw SQL query should be?
You don't need to use raw SQL, just provide the pattern as a string, and add named parameters:
Question.where('user_id in (:ids) or target in (:usernames)',
:ids => self.friends.ids, :usernames => self.friends.usernames)
Or positional parameters:
Question.where('user_id in (?) or target in (?)',
self.friends.ids, self.friends.usernames)
You can also use the excellent Squeel gem, as #erroric pointed out on his answer (the my { } block is only needed if you need access to self or instance variables):
Question.where { user_id.in(my { self.friends.ids }) |
target.in(my { self.friends.usernames }) }
Though Rails 3 AR doesn't give you an or operator you can still achieve the same result without going all the way down to SQL and use Arel directly. By that I mean that you can do it like this:
t = Question.arel_table
Question.where(t[:user_id].in(self.friends.ids).or(t[:username].in(self.friends.usernames)))
Some might say it ain't so pretty, some might say it's pretty simply because it includes no SQL. Anyhow it most certainly could be prettier and there's a gem for it too: MetaWhere
For more info see this railscast: http://railscasts.com/episodes/215-advanced-queries-in-rails-3
and MetaWhere site: http://metautonomo.us/projects/metawhere/
UPDATE: Later Ryan Bates has made another railscast about metawhere and metasearch: http://railscasts.com/episodes/251-metawhere-metasearch
Later though Metawhere (and search) have become more or less legacy gems. I.e. they don't even work with Rails 3.1. The author felt they (Metawhere and search) needed drastic rewrite. So much that he actually went for a new gem all together. The successor of Metawhere is Squeel. Read more about the authors announcement here:
http://erniemiller.org/2011/08/31/rails-3-1-and-the-future-of-metawhere-and-metasearch/
and check out the project home page:
http://erniemiller.org/projects/squeel/
"Metasearch 2.0" is called Ransack and you can read something about it from here:
http://erniemiller.org/2011/04/01/ransack-the-library-formerly-known-as-metasearch-2-0/
Alternatively, you could use Squeel. To my eyes, it is simpler. You can accomplish both the IN (>>) and OR (|) operations using the following syntax:
Question.where{(:user_id >> my{friends.id}) | (:target >> my{friends.usernames})}
I generally wrap my conditions in (...) to ensure the appropriate order of operation - both the INs happen before the OR.
The my{...} block executes methods from the self context as defined before the Squeel call - in this case Question. Inside of the Squeel block, self refers to a Squeel object and not the Question object (see the Squeel Readme for more). You get around this by using the my{...} wrapper to restore the original context.
raw SQL
SELECT *
FROM table
WHERE user_id in (LIST OF friend.ids) OR target in (LIST OF friends.usernames)
with each list comma separate. I don't know the Rails ActiveRecord stuff that well. For AND you would just put a comma between those two conditions, but idk about OR