Why isn't my search method working in Ruby on Rails? - sql

In my Ruby on Rails application, I have a cinema system and am trying to return the screen a showing is in when a user searches for the showing.
To display the search drop down I am using this code in my _application.html.erb:
<%= render( :partial => '/screen_lookup', :locals => {:showings => #showings = Showing.all, :my_path => '/screens/display_screens_by_showing' })%>
Which renders the search from the _screen_lookup.html.erb:
<%= form_tag my_path, :method=>'post', :multipart => true do %>
<%= select_tag ('showings_id'),
options_from_collection_for_select(#showings, :id, :showing_times, 0 ),
:prompt => "Showings" %>
<%= submit_tag 'Search' %>
<% end %>
And uses the display_screens_by_showing in the screens_controller:
def display_screens_by_showing
#screens = Screen.showing_search(params[:showing_id])
if #screens.empty?
# assign a warning message to the flash hash to be displayed in
# the div "feedback-top"
flash.now[:alert] = "There are no films of that genre."
# return all products, in alphabetical order
#screens = Screen.all
end
render :action => "index"
end
And this searches using the method in the screen.rb model:
def self.showing_search(showing_id)
screen = Showing.where("id = ?", showing_id).screen_id
self.where("id = ?", screen)
end
Now, the problem I am having is that because a showing belongs_to a screen, and a screen has_many showings, I need to be able to search for the showing, and store that showing's screen_id in a variable to search for the screen that showing is in with, which I have tried doing in the model:
screen = Showing.where("id = ?", showing_id).screen_id
self.where("id = ?", screen)
But the error I am getting is:
NoMethodError in ScreensController#display_screens_by_showing
undefined method `screen_id' for #<ActiveRecord::Relation []>
These are the model relationships:
showing.rb:
class Showing < ActiveRecord::Base
belongs_to :screen
end
screen.rb:
class Screen < ActiveRecord::Base
has_many :showings
end
What code will get my search working?

The problem is that where doesn't return a record, it returns a relation that can be enumerated or chained, instead you want to use find or find_by to return a single record, which is kind equivalent to where + first
screen = Showing.find(showing_id).screen_id
which is sort of like doing
screen = Showing.where(id: showing_id).first.screen_id
If you want to pass a hash you can use find_by which will be like this
screen = Showing.find_by(id: showing_id).screen_id
PS:
I'm not sure what you're doing exactly, but i think those two lines can be merged into a single query ( not sure what it should be returning, but I'm assuming a screen )
def self.showing_search(showing_id)
Showing.find(showing_id).screen
end

Related

Duplicating a record with associated images using Carrierwave

I have an app which you can store order/invoices in. I'm building a simple feature where you can duplicate invoices for my customers. I wrote this method in my Order.rb model which:
Takes the invoice, duplicates the associated lineitems, adds the new OrderID into them...and does the same for associated images.
def self.duplicate_it(invoice)
new_invoice = invoice.dup
new_invoice.save
invoice.lineitems.each do |l|
new_lineitem = l.dup
new_lineitem.order_id = new_invoice.id
new_lineitem.save
end
invoice.images.each do |i|
new_image = i.dup
new_image.order_id = new_invoice.id
new_image.save
end
return new_invoice
end
Unfortunately, you can't just .dup the image because there's all this associate expiration stuff since I'm storing images on S3. Is there a way to regenerate the image maybe using its image_url?
The error I get when running this is below. Which tells me not all the associated image information is dup'd correctly.
Showing /Users/bruceackerman/Dropbox/printavo/app/views/orders/_image-display.erb where line #3 raised:
undefined method `content_type' for nil:NilClass
Extracted source (around line #3):
1: <% #order.images.each do |image| %>
2: <% if image.image && image.image.file %>
3: <% if image.image.file.content_type == "application/pdf" %>
4: <%= link_to image_tag("/images/app/pdf.jpg",
5: :class => 'invoice-image'),
6: image.image_url,
i think you can do the following
invoice.images.each do |i|
new_image = new_invoice.images.new({ order_id: new_invoice.id })
new_image.image.download!(i.image_url)
new_image.store_image!
new_image.save!
end
This is actually how I did it for each lineitem on an order:
def self.duplicate_it(invoice)
new_invoice = invoice.dup :include => {:lineitems => :images} do |original, kopy|
kopy.image = original.image if kopy.is_a?(Image)
end
new_invoice.save!
return new_invoice
end
It is a bit late however this is my solution. I have had far too many problems with .download!
if #record.duplicable?
new_record = #record.dup
if new_record.save
#record.uploads.each do |upload|
new_image = new_record.uploads.new({ uploadable_id: new_record.id })
new_image.filename = Rails.root.join('public'+upload.filename_url).open
new_image.save!
end
end
Here is my upload.rb
class Upload < ActiveRecord::Base
belongs_to :uploadable, polymorphic: true
mount_uploader :filename, ImageUploader
end
Hope it helps!

Rails: Retrieving polymorphic data from nested model

I'm trying to retrieve data for all items from a box, a box can have compartments and I'd like to get all compartment info at the box level. items are made polymorphic as boxes won't necessarily have compartments.
MODEL
class Box < ActiveRecord::Base
has_many :compartments
has_many :items, :as => :itemable
end
In my Controller I can get results back with:
#box = Box.find(params[:id])
#itemable = #box.compartments.first
#itemable = #box.compartments.last
VIEW
<% #items.each do |item| %>
<%= item.name %>
<% end %>
but if I then try
#itemable = #box.compartments
OR
#itemable = #box.compartments.find(:all)
I get the error
undefined method `items' for #<ActiveRecord::Array>
OR
undefined method `items' for #<ActiveRecord::Relation>
Can anyone help with getting results back from all compartments?
So in compartments you have
belongs_to :box
has_many :items, :as => :itemable
Is this the case? #box.compartments should return an array of compartments right? It sounds like items is somehow getting called on #box.compartments somehow

rails OR query based on multiple checkbox selections

This seems like it should be a common problem but I'm having trouble finding an answer. Basically I want to have a form with 10 or so checkboxes which I'm creating with check_box_tag. When the form is submitted I want to generate a query that return all records that match ANY of the checked selections. So, the number of checked selections will vary.
So, for example, if I have
class Book < ActiveRecord::Base
belongs_to :author
end
I want to generate something like
Book.where("author_id = ? or author_id = ?", params[authors[0]], params[authors[1]]) if there are two boxes checked, etc.
Thanks for any insight.
Will this work for you?
Book.where(author_id: [array_of_author_ids])
You need to collect author_ids from params first
I recently had to do something similar, this is how I achieved this. It's pretty clever (at least I think so. :))
I created a query model that serializes the query column (text field) in JSON. I use a form to get the query data from the user with selection fields.
class BookQuery < ActiveRecord::Base
has_many :books
# loop through each foreign key of the Book table and create a hash with empty selection
def self.empty_query
q = {}
Book.column_names.each do |column_name|
next unless column_name.ends_with?("_id")
q.merge column_name => []
end
end
end
I'm using Author as an example below:
<%= form_for #book_query do |f| %>
<% for author in Author.all %>
<%= check_box_tag "book_query[query][author_ids][]", author.id, false%>
<%= author.name %>
<% end %>
<%= f.submit "Save Query" %>
<% end %>
When this form is submitted you ended up with parameters like this:
When the form is submitted it generates this parameter:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"XXXXXXXXXXX", "book_query"=>{"query"=>{"author_ids"=>["2", "3"]}}, "commit"=>"Save Query"}
Now in the BookQuery controller's create action you can just do what create function always does:
def create
#book_query = BookQuery.build(params[:book_query])
if #book_query.save
flash[:success] = "Book query successfully saved."
redirect_to ...
else
flash[:error] = "Failed to save book query."
render :new
end
end
But by default rails serializes the data in hash type:
1.9.3p194 :015 > pp BookQuery.find(9).query
BookQuery Load (0.7ms) SELECT "book_queries".* FROM "book_queries" WHERE "book_queries"."id" = $1 LIMIT 1 [["id", 9]]
"--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nauthor_ids:\n- '2'\n- '3'\n"
=> "--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nauthor_ids:\n- '2'\n- '3'\n"
In BookQuery model, add the following:
serialize :query, JSON
But rail would change the IDs to string:
1.9.3p194 :018 > query = JSON.parse(BookQuery.find(10).query)
BookQuery Load (0.5ms) SELECT "book_queries".* FROM "book_queries" WHERE "book_queries"."id" = $1 LIMIT 1 [["id", 10]]
=> {"author_ids"=>["2", "3"]}
1.9.3p194 :019 > query["author_ids"]
=> ["2", "3"]
What I did then is override the attribute accessors in BookQuery model:
The below has to be done because the hash returns strings, not ids in integer.
def query=(query)
query.each_pair do |k, v|
if query[k].first.present?
query[k].map!(&:to_i)
else
query.except!(k)
end
end
write_attribute(:query, query)
end
# just want to avoid getting nil query's
def query
read_attribute(:query) || {}
end
To find book with this query, you can simply add this function to your Book model:
def self.find_by_book_query(book_query, options = {})
options[:conditions] = book_query.query
find(:all, options)
end
Now you get a customizable query string based on the model definition Book and everything works like the Rails way. :)

How to make parts of Profile searchable or not

In my Rails 3 app, I want to allow a user to specify which parts of their profile can be searchable by others. I know how to do this if I wanted to make the entire user invisible, but how can I set it up so multiple fields can be designated searchable or not separately?
More info:
In terms of functionality, I want to limit searches based on what parts of their profile a user chooses to be searchable in /settings. Parts of the profile would be, for example, #user.profile.hometown or #user.profile.current_city. I'm working off of a Combination of Railscasts #52 and Trevor Turk's tutorial to set what others can search through checkboxes in the settings.
When searchability is defined in settings, when a user searches (or filters) the /users index, what isn't hidden will be public and searchable. In terms of how this works in the DB as far as hiding table columns or grouping, I thought about hiding tables but maybe that's not the best solution. I'm as beginner as can be and hadn't really thought much about that to be honest.
Method 1 - show/hide specific columns
So, the most direct way (and this will work if there are only a handful of things you want to show/hide), is just to create a boolean column for every thing you need to show/hide. So, if you had a phone number field, you could have a column called "show_phone_number", and when true it would show it.
Method 2 - show/hide whole sections
The next level that you might need is, rather than showing/hiding particular columns, have your show/hide boolean columns something like show_contact_info, show_photos, etc. for each logical section that a user would show or hide.
Then in your view, you'd have something like:
app/views/user/show.html.erb (or .haml or whatever you're using)
....
<% if #user.show_contact_info %>
<%= render :partial => "user_contact_info", :locals => {:user => #user} %>
<% end %>
app/views/partials/_user_contact_info.html.erb
<%=h user.email %><br />
<%=h user.phone_number %><br />
<%= user.blog_url %><br />
...
Method 3 - show/hide sections based on who is viewing it
Finally (and the code here is untested, but I think you'll get the idea) let's say your site has a social structure, and you want to show information to some people, but not to others. Basically you'll need the following in some form or another:
Section visibilities (who can view what sections)
Roles (friends, followers, public, private)
a few methods to make these relationships clear/easy to understand
So, in your User model you'd have something like:
class User < ActiveRecord::Base
has_many :friends, :through => "friendships" # or whatever construct you have
has_many :followers, :through => "followings" # or whatever construct you have
has_many :profile_visibilities
...
def is_friends_with(user)
friends.include?(user)
end
def is_a_follower_of(user)
user.followers.include?(self)
end
def can_see(visibilities)
visibilities.each do |v|
v.user == self || v.is_public || can_see_because_we_are_friends(v) || can_see_because_i_follow(v)
end
end
private:
def can_see_because_we_are_friends(visibility)
visibility.is_friend && is_friends_with(visibility.user)
end
def can_see_because_i_follow(visibility)
visibility.is_follower && is_follower_of(visibility.user)
end
end
Then a class called ProfileVisibilities:
class ProfileVisibilities < ActiveRecord::Base
belongs_to :user
...
def is_public
visibility == "public"
end
def is_friend
visibility == "friends"
end
def is_follower
visibility == "followers"
def is_private
!is_public && !is_friend && !is_follower
end
end
Then a table called profile_visibilities
id | user_id | profile_section | visibility
----------------------------------------------
1 | 1 | contact_info | public # <= visible to everyone
2 | 1 | personal_info | friends # <= visible only to "friends"
3 | 1 | blog_posts | friends # <= visible to "friends"
4 | 1 | blog_posts | followers # <= ...and followers
5 | 1 | photos | friends # <= visible only to "friends"
Then in your controller, something like:
app/controllers/users_controller.rb
...
def show
#user = User.find(params[:id])
#contact_info_visibilities = ProfileVisibilities.find(:all, :conditions = ['user_id = ? AND profile_section = "contact_info"', #user.id]
#photo_visibilities = ProfileVisibilities.find(:all, :conditions = ['user_id = ? AND profile_section = "photos"', #user.id]
# ... and more for each section visibility you need
end
...
And in your view:
app/views/user/show.html.erb
...
<% if current_user.can_see(#contact_info_visibilities) %>
<%= render :partial => "user_contact_info", :locals => {:user => #user}
<% end %>
<% if current_user.can_see(#photo_visibilities) %>
<%= render :partial => "user_photos", :locals => {:user => #user}
<% end %>
...

Rails filtering with acts_as_taggable gem

I am working the acts-as-taggable-on gem and have a question about how to filter down search results based on tags users select. Here's an abridged look at my controller:
class PhotosController < ApplicationController
def index
#photos = Photo.where(["created_at > ? AND is_approved = ?", 1.months.ago, true])
#tags = ["Animals", "Architecture", "Cars", "Flowers", "Food/Drink", "General", "Landscape", "Mountains", "Nature"]
end
def search_by_tag(tag)
#photos = Photo.where('tagged_with LIKE ?', tag)
end
end
Photos/index
<% #tags.each do |tag| %>
<%= link_to tag, {:search_by_tag => tag}, :class => "tag" %>
<% end %>
This lists out all of the tags from the hash #tags defined in index, but clicking them doesn't actually filter anything down. Here's a look at what clicking one of those links produces in the log:
Started GET "/photos?search_by_tag=Animals" for 127.0.0.1 at Sun Oct 09 17:11:09 -0400 2011
Processing by PhotosController#index as HTML
Parameters: {"search_by_tag"=>"Animals"}
SQL (0.5ms) SELECT name FROM sqlite_master WHERE type = 'table' AND NOT name = 'sqlite_sequence'
The result I want is for the page to display only Photos that are tagged_with whichever tag was clicked on. I can't quite figure out how to accomplish this.
(Side-question: I can't seem to find a way to list out all of the tags from the tags table that acts-as-taggable-on generated. Doing something like Photo.tagged_with or Photo.tags doesn't work. I am able to see the "tags" table the gem created, and the entries inside of it; I'm just not really clear how to handle that using this gem)
Any thoughts greatly appreciated.
UPDATE
I've updated my code and am a bit closer.
class PhotosController < ApplicationController
def search_by_tag
#photos = Photo.tagged_with(params[:tag_name])
end
photos/index
<% Photo.tag_counts_on(:tags).map(&:name).each do |tag| %>
<%= link_to tag, {:action => 'search_by_tag', :tag_name => tag}, :class => "tag" %>
<% end %>
I believe this is closer, but still working through this...
You have a number of errors in your code:
Your link_to call is actually calling the index action.
Your search_by_tag method is expecting an argument, where it should be using the params hash to access the parameters passed to it in the web request.
tagged_with is a class method added by acts_as_taggable_on, not a field in your table - therefore you can't use it in the where method like you have done.
Finally, to get all the tag names: Photo.tag_counts_on(:tags_or_whatever_you_tagged_on).map(&:name)
Take a look at the acts_as_taggable_on documentation and you'll see examples of how to use tag_counts_on and tagged_with.
As for the Rails things: http://guides.rubyonrails.org/ http://railsforzombies.org/ and/or http://railscasts.com/