I have the following named scope in the 'Book' model
named_scope :batch_book_status_55, lambda { |batch_id| {
:select => "ifnull( sum( if( BK_DB_STATUS_FK = 55, 1, 0 ) ), 0) as ASSIGNED",
:conditions => ["BK_BATCH = ?", batch_id]
}}
<% Book.batch_book_status_55(batch.BATCH_ID).each_with_index do |book, index| %>
<% if book.ASSIGNED.to_i != 0%>
Assigned : <%= book.ASSIGNED %>
<% end %>
<% end %
The code below display the number of books in a particular batch with status = 55 (BK_DB_STATUS_FK = 55).
I am a bit confused with the named scope code. I would appreciate it if someone can please explain the code to me.
You might be better off using a self method here instead of a named scope. Please refer: http://www.railway.at/2010/03/09/named-scopes-are-dead/
Related
I have edited my first code and now it's better and cleaner thanks to #FunTimeFreddie, but the issue it's not yet properly solved. I'll come back with the right answer sooner.
In a search form I need to filter all menuitems:
1. per category
2. per category and search “query”
3. per min price && || max price
… and so on, with all possible combinations
I’ve manage to make a search in all menuitems with a “query”, min_price and max_price --all possible combinations from the search form. I can NOT manage to have the list of results of the chosen category, what am I doing wrong?
This is my model(edited):
class Menuitem < ActiveRecord::Base
belongs_to :menu_category
include Filterable
scope :newest_first, lambda { order('menuitems.created_at DESC') }
scope :last_one, lambda { order('menuitems.created_at ASC').last }
scope :search_keyword, lambda { |query|
where(["title LIKE ? or body LIKE ?", "%#{query}%", "%#{query}%"]) if query != ""
}
scope :menu_category_id, lambda { |menu_category_id|
where( "menu_category_id = ?", menu_category_id ) if menu_category_id != ""
}
scope :min_price, lambda { |price|
where("price > ?", price) if price != ""
}
scope :max_price, lambda { |price|
where("price < ?", price) if price != ""
}
end
This is my controller(edited):
class MenuitemsController < ApplicationController
def index
#menuitems = Menuitem.newest_first.filter(params.slice(:menu_category_id, :search_keyword, :min_price, :max_price))
end
And this is my view:
<%= simple_form_for :menuitem, :method => 'get', :url => {:action => 'index'} do |f| %>
<p>
<%= f.select :menu_category_id, options_for_select(#menucategories.map {|s| [s.title, s.id]}, params[:menu_category_id]), :selected => params[:menu_category_id], :onchange => "this.form.submit();", prompt: "Select category" %>
</p>
<p>
<%= f.input :search_keyword, input_html: { name: 'search_keyword', :value => params[:search_keyword]}, label: 'search recipe title', :required => false %>
<%= f.input :min_price, input_html: { name: 'min_price', :value => params[:min_price]}, label: 'min price:', :required => false %>
<%= f.input :max_price, input_html: { name: 'max_price', :value => params[:max_price]}, label: 'max price:', :required => false %>
<%= f.button :submit, "search" %>
</p>
<% end %>
You can save yourself the trouble of all the IF statements in your controller for all of the combinations by adding an IF statement within the scopes. For example, and this can similarly be applied to the four scopes associated to your form,
# menuitem model
scope :search_keyword, lambda { |query|
where(["name LIKE ?", "%#{query}%"]) if query != ""
}
This will allow to include only a single line in your controller, the line beneath your first IF statement, as this will handle the blank parameters.
There seems to be two issues with the category parameter. The first is it is a nested parameter within params[:menuitem], so in order to access it we need to call params[:menuitem][:menu_category_id]. Not too sure why this is happening to be honest, though I would recommend in this instance using a form_tag as opposed to form_for, given that we are not adding or editing the menuitems table itself.
The second issue is the category parameter is passed as a string, whereas we need it as an integer. We'll need to convert to parameter before applying it.
Now I'm not familiar with the .filters method (is this part of a gem?) but I got this to work the old fashioned way just by concatenating all the scopes together in one line as follows...
# menuitems controller
def index
#menuitems = Menuitem.newest_first.min_price(params[:min_price]).max_price(params[:max_price]).search_keyword(params[:search_keyword]).menu_category_id(params[:menuitem][:menu_category_id].to_i)
end
Note, another way of changing the data type would be to do so in the scope. You could do this as follows
# menuitem model
scope :menu_category_id, lambda { |menu_category_id|
where( "menu_category_id = ?", menu_category_id.to_i ) if menu_category_id != ""
}
i am fairly new to rails and i have to update an existing project. I have an existing database, beforehand it was just one User group, now there should be multiple. Now i want to use the old view, but filter with the help of a dropdown menu, but for some reason i can't work out what i am doing wrong.
Here are the code snippets i changed, since it was working beforehand, i assume my mistake must be somewhere within these lines.
event.rb
scope :men, lambda { { :conditions => ["team_id == ?", 1] } }
scope :women, lambda { { :conditions => ["team_id == ?", 2] } }
scope :juniors, lambda { { :conditions => ["team_id == ?", 3] } }
events_controller.rb
def index
# #events = Event.where("startdate >= ?", Date.today).order("startdate, starttime")
# #events = Event.order("startdate, starttime")
if params[:search]
#events = Event.search(params[:search])
else
if params[:filter].nil?
#events = Event.all
else
if params[:filter] == "Alle" then #events = Event.all end
if params[:filter] == "Men" then #events = Event.men end
if params[:filter] == "Women" then #events = Event.women end
if params[:filter] == "Juniors" then #events = Event.juniors end
end
end
end
and the index.html.erb
<div class="left">
<%= form_tag events_path, :method => 'get' do %>
<%= select_tag "filter", options_for_select([ "Alle", "Men", "Women", "Juniors" ], params[:filter]), {:onchange => 'this.form.submit()'} %>
<% end %>
probably it is a simple mistake. My guess is, that in the index.html.erb i am doing something wrong.
as a follow up, i want to filter just the events which are upcoming, for that i can use the commented part in the controller. can i just add that to the assignmnet in the style of:
#events = Event.men.where("startdate >= ?", Date.today).order("startdate, starttime")
thanks for the help
Lenny
You should change your scopes to new syntax:
scope :men, -> { where(team_id: 1) }
scope :women, -> { where(team_id: 2) }
scope :juniors, -> { where(team_id: 3) }
Your controller logic is a little buggy and twisted (checking 5 times filter isnt best way, why checking e.g. if filter is "Men" if you already matched it with "Alle" ?). Here is some help:
#events = if params[:search].present?
Event.search(params[:search].to_s)
else
case params[:filter]
when "Men"
Event.men
when "Women"
Event.women
when "Juniors"
Event.juniors
else
Event.all
end
end
Speaking about view, you shouldnt use inline js, just because its XXI century, and such "quick solutions" are harder to maintain later, so:
<div class="left">
<%= form_tag events_path, :method => 'get' do %>
<%= select_tag "filter", options_for_select([ "Alle", "Men", "Women", "Juniors" ], params[:filter]), class: 'my_filter' %>
<% end %>
and then add to your events.coffee:
$('select.my_filter').on 'change', ->
$(this).parents('form').submit()
Hope this helps!
i have a single search field that is querying three different columns from two different tables: "companies" and "industries" from a positions table and "schools" from an educations table. it is successfully returning all users that meet ALL fields entered into the search field (using select_tag). this is from my view:
<%= form_tag(search_path, :method => :get, :id => "people_search") do %>
<div class="row-fluid">
<div class="span4">
<table>
<tr>
<td>
<div class="search-table" style="padding-left:55px">
<%= select_tag "all", options_for_select((#visible_companies + #visible_industries + #visible_schools).uniq, params[:all]), { :placeholder => "Search by companies, industries and schools...", :multiple => true, :js => "if (evt.keyCode == 13) {form.submit();}" } %>
</div>
</td>
<td>
<%= submit_tag "Add", id: "send-button", style:"width:175px;" %>
</td>
</tr>
</table>
</div>
<% end %>
</div>
and controller:
#visible_positions = Position.where{ is_visible.eq('true') }
#visible_educations = Education.where{ is_visible.eq('true') }
#visible_companies = #visible_positions.order("LOWER(company)").map(&:company).uniq
#visible_industries = #visible_positions.order("LOWER(industry)").map(&:industry).uniq
#visible_schools = #visible_educations.order("LOWER(school)").map(&:school).uniq
#c = #visible_positions.where{company.in(my{params[:all]})}.map(&:user_id)
#i = #visible_positions.where{industry.in(my{params[:all]})}.map(&:user_id)
#s = #visible_educations.where{school.in(my{params[:all]})}.map(&:user_id)
#C = #visible_positions.where{company.in(my{params[:all]})}.map(&:company)
#I = #visible_positions.where{industry.in(my{params[:all]})}.map(&:industry)
#S = #visible_educations.where{school.in(my{params[:all]})}.map(&:school)
#blacklist = []
#cis = #c + #i + #s
#experiences = ([#C,#I,#S].reject(&:empty?).reduce(:&))
#cis.uniq.each do |user_id|
unless #C.empty?
#C.uniq.each do |company|
unless Position.find_all_by_company(company).map(&:user_id).include?(user_id) || Position.find_all_by_industry(company).map(&:user_id).include?(user_id) || Education.find_all_by_school(company).map(&:user_id).include?(user_id)
#blacklist << user_id
end
end
end
unless #I.empty?
#I.uniq.each do |industry|
unless Position.find_all_by_industry(industry).map(&:user_id).include?(user_id) || Position.find_all_by_company(industry).map(&:user_id).include?(user_id) || Education.find_all_by_school(industry).map(&:user_id).include?(user_id)
#blacklist << user_id
end
end
end
unless #S.empty?
#S.each do |school|
unless Education.find_all_by_school(school).map(&:user_id).include?(user_id) || Position.find_all_by_company(school).map(&:user_id).include?(user_id) || Position.find_all_by_industry(school).map(&:user_id).include?(user_id)
#blacklist << user_id
end
end
end
end
unless #c.empty? && #i.empty? && #s.empty?
#users = User.find(#cis - #blacklist)
end
the search looks like this (notice the single field), with a sample query included (notice the AND filter...i'm the only user in the database that fits all search terms ['dartmouth college' for school, 'world health organization' for company, 'internet' for industry]):
i realize this is not an efficient query and am thinking of ways to speed it up, but could use some ideas at this point.
happy turkey day :)
Based on your description rather then on understanding your code I figured out something like this
User.joins(:positions, :educations).where("lower(positions.company) like lower(?) and lower(positions.industry) like lower(?) and lower(educations.school) like lower(?) and positions.is_visible and educations.is_visible", "%#{company}%", "%#{industry}%", "%#{school}%")
or if there is only one company or industry in column
User.joins(:positions, :educations).where("(lower(positions.company) = lower(?) or lower(positions.industry) = lower(?)) and lower(educations.school) = lower(?) and positions.is_visible and educations.is_visible", company,industry, school)
But to put many industries, companies, schools as params will be more complicated
and create indexes
create index positions_lower_company on positions (lower(company));
create index positions_lower_industry on positions (lower(industry));
create index educations_lower_school on educations (lower(school));
I hope it will help somehow.
I have Awards and Categories, joined with Awards_Categories
I have 2 Categories.
I need to find all the Awards that have Category.id = 1, but not Category.id =2. Some categories have one or the other, some have just one. I want a list of the Awards that have category 1 but not category 2.
I have a scope:
scope :in_categories, lambda { |categories|
joins(:categories).
where(:awards_categories => { :category_id => categories } ).
select("DISTINCT awards.*")
}
And this works with a query like:
#awardsall = Award.in_categories([1 && 2]).order("name ASC")
I have tried
#awards_store = Award.in_categories([1]).order("name ASC")
With:
<% #awards_store.each do |store| %>
<li><%= link_to store.name, award_path(store), :title => store.info %> |
<% store.categories.each do |cat| %>
<%= cat.id%>
<% end %>
</li>
<% end %>
EDIT---
I know the block is not what I need. it is just my attempt at finding a way to make it work.
And while this lists all the awards, and all the award categories its still grabbing awards that have category.id = 2 because some awards have both
any ideas?
Sorry, didn't test it, but the main idea is to count the rows in the connecting table.
scope :in_categories, lambda { |*categories|
joins(:categories).
where(:awards_categories => { :category_id => categories } ).
where("(select count(distinct category_id) from awards_categories where category_id in (?)) = ?", categories, categories.size)
}
and use it this way:
#awardsall = Award.in_categories(1, 2).order("name ASC")
#awards_store = Award.in_categories(1).order("name ASC")
If you have a model for awards_categories, then it will look better:
scope :in_categories, lambda { |*categories|
joins(:categories).
where(:awards_categories => { :category_id => categories } ).
where("#{AwardCategory.where(:category_id => categories).count("distinct category_id").to_sql}=#{categories.size}")
}
Absolute RoR newbie here, I'm trying to render out multiple leagues in a loop, incrementing the div_# each time, here's a cut down version, without the html. It works when I hard code div_1 or div_2 to be sorted, but div_name doesn't work, even though it has the right contents I need it to be seen as the array.
<% div_1 = Array.new
div_1 << { :Name => 'Rob', :Played => '2', :Won => '1', :Lost => 1, :Points => 4}
div_2 = Array.new
div_2 << { :Name => 'Gavin', :Played => '2', :Won => '1', :Lost => 1, :Points => 4}
for i in (1..2)
i = i.to_s
div_name = "div_" + i
div_name.sort_by { |position| position[:Points] }.reverse!.each do |position| %>
<%= position[:Name] %>
There are lots of problems here:
div_1 = Array.new
div_1 << { :Name => 'Rob', :Played => '2', :Won => '1', :Lost => 1, :Points => 4}
div_1 is now an array with a single element, which is a hash. You don't need an array if you'll just have one element in it.
for i in (1..2)
Where's the block for this loop? After that statement, i is still undefined, so when you call
i = i.to_s
you'll get a NameError.
div_name = "div_" + i
Even if i == 1, div_name will be a string with value 'div_1', and not a copy or instance of the div_1 variable you define above.
div_name.sort_by { |position| position[:Points] }.reverse!.each do |position| %>
Now you're trying to call sort_by on a string, which doesn't respond to that, because it doesn't make sense.
<%= position[:Name] %>
You don't have a variable named position defined at this scope.
Also, when you find yourself putting lots of logic inside of a view within <% %> tags, that's a sign that you need to move that code elsewhere, like to the controller. You could define and calculate #positions as an array of hashes in the controller, and then in the view do something like:
<% #positions.each do |position| %>
<%= position[:name] %>
<% end %>