The code below is working
-#items.each do |item|
%tr
%td
=item.quantity
times
%td= item.product.title
end
However I want to use { } instead of do ... end. Is it possible?
-#items.each { |item|
%tr
%td
=item.quantity
times
%td= item.product.title
}
For some reason it throws an exception. What did I do wrong?
In your working example the end line is being treated as raw text, and will be included in your output. This works because you effectively don’t have an end keyword. If you change this line to - end — i.e. add the hyphen (and make sure you’ve got your indentation correct) — you’ll see an error like:
Syntax error on line 9: You don't need to use "- end" in Haml. Un-indent to close a block:
- if foo?
%strong Foo!
- else
Not foo.
%p This line is un-indented, so it isn't part of the "if" block
Note that Haml doesn’t always disallow end keywords, only end by itself. You can use them with statement modifiers, e.g.:
- #things.each do |thing|
%p= thing.to_s
-end unless #dont_show_things
or if you want to make use of the result of the block:
= #things.map do |thing|
- do_something_with(thing)
-end.join(",")
As for using {}, you can’t. When Haml compiles the template to Ruby the hardcoded string end is added to close any blocks as needed. It could be possible to modify Haml to be able to use do..end or {}, but the extra complexity probably isn’t worth it. This way is a better fit with the common Ruby idiom of using do...end for multiline blocks, and {} for single line ones.
If you need to use {} because of some issue with operator precedence it might be a sign to create a helper method. Your Haml templates (or templates in any language) aren’t the best place for complex logic, it’s best to move it into real Ruby files.
You DO NOT put any end in HAML. The indentation is semantic. Your code should therefore be :
- #items.each do |item|
%tr
%td
= item.quantity
times
%td= item.product.title
EDIT: Using { will throw a syntax error.
If you want to add complex ruby code, you can use the ruby syntax,
:ruby
x = case a
when 2
"two"
when 3
"three"
end
Related
Does anyone know of a way to get collection_select to name its fields for the text methods' names and not their values?
I've got print_100, print_200, and print_500 and a plan to add more when necessary. I'd like the values of the select box to read from Billing all the fields that start with print_ so the select box would just have options like 100, 200, and 500.
f.collection_select(:print_quantity, Billing.all, :print_100, :print_100)
Any thoughts? Cheers.
I'm not as familiar with this part of rails as I'd like to be, so be gentle.
http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-collection_select
the syntax is
collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
If you were to change the second parameter (method) to an actual method (rather than just the attribute that you want from the billing object) you can make the value whatever you would like.
If that doesn't work (or if you're not allowed to substitute the attribute for a method) then you may be able to make it work using the 5th or 6th parameters, value_method and text_method, which define what values should be applied to the tags.
Anyway, this answer is mostly to point you in (hopefully) the right direction, since I'm not certain of the method or how it works.
Good luck.
Thanks to #DavidDraughnn for the idea for this solution. I wrote a method in the relevant helper, thus:
def get_quantities
#quantities = {}
Billing.column_names.each do |a|
if a.match(/^print_/)
#quantities[a.delete "print_"] = a.delete "print_"
end
end
return #quantities
end
And I've adjusted collection_select to select, thus:
<% get_quantities %>
<%= f.select(:print_quantity, #quantities, {:prompt => "Please select..."}) %>
Hope that helps someone.
When I call a find with an id, it becomes a targeted find, and will throw an error RecordNotFound.
Foo::Bar.find(123) # RecordNotFound if no Bar with id 123 exists.
But when I call that with conditions, I get nil if not found:
Foo::Bar.find(:first, :conditions => [ "lower(name) = ?", name.downcase ])
I want such a conditional search to raise an error too. I know I can do:
Foo::Bar.find_by_name!("CocktailBar") #=> raises Recordnotfount if not not found.
But that has only really simple conditions. Mine need a little more complexity; actually something like:
Foo.Bar.select{ |pm| pm.name.downcase =~ /cocktail/}.first
And, if nothing is found, I want it to raise the RecordNotFound error. Is that possible at all? Or should I simply add some code to check against nil? and if nil? raise the error myself? And if so, how do I do that in Rails 3?
In the last snippet of code you are actually fetching all records from DB and then doing select on a Ruby array. It has nothing to do with ActiveRecord, so you can do whatever you like to raise exception manually, either use the code suggested by Douglas, or if, or unless etc. But it seems that you don't quite understand what your code does. Your select {...} is not translated into SQL SELECT ... WHERE(...).
If you need an exception raised automatically by ActiveRecord query, use this:
Foo::Bar.where([ "lower(name) = ?", name.downcase ]).first!
The equivalent bang methods exist for find_by methods as well, for example Foo::Bar.find_by_name!(name)
For anyone coming across this question, now you have the method find_by!
that does exactly what the OP asked for the find case.
example:
Foo::Bar.find_by!(name: "CocktailBar")
https://apidock.com/rails/v4.0.2/ActiveRecord/FinderMethods/find_by%21
Your final paragraph is what you need to do. Either check against nil, or raise the exceptions yourself. To raise the exception yourself, do the following:
Foo::Bar.find(:first, :conditions => [ "lower(name) = ?", name.downcase ]) || raise(ActiveRecord::RecordNotFound)
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.
this is my test (with shoulda helpers):
context "searching from header" do
setup do
Factory(:city, :name => 'Testing It')
ThinkingSphinx::Test.index 'city_core', 'city_delta'
ThinkingSphinx::Test.start
get :index,
:query => 'Testing It'
end
should respond_with(:success)
should assign_to(:results)
should "have one city on the result" do
assert_equal( assigns(:results).count, 1 )
assert_kind_of( assigns(:results).first, City )
end
ThinkingSphinx::Test.stop
end
Everything works fine except the test always say the count of the results is 0, not 1.
I have debugged this code and when the request reaches the controller, the Sphinx indexes are completely empty, even with the explicit call of index for it.
Am I doing something wrong here?
Any help appreciated.
I found out the problem... even tho the insertion in the database is right before the ThinkingSphinx.index, with transactional fixtures, after the setup block the records get deleted.
The solution was adding to the test the following line:
self.use_transactional_fixtures = false
Hope this helps anyone with the same problem.
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