Rails test for contents in a form - ruby-on-rails

I have a Comment form that also contains an Attachment form.
Comment model contains:
accepts_nested_attributes_for :attachments
Comment form contains:
<%= f.fields_for :attachments do |builder| %>
<%= builder.input :name, :label => 'Attachment Name' %>
<%= builder.file_field :attach %>
<% end %>
Comment Controller contains:
def new
#comment = Comment.new
#comment.attachments.build
If the user adds an Attachement, everything works fine.
I would like the user to be able to submit a Comment with or without an Attachment.
Right now, if the user enters a Comment without an attachment, the form re-displays and the Comment does not get created.
This is the log if I try to post a new Comment without an Attachement:
Started POST "/comments" for 127.0.0.1 at 2013-12-19 10:34:31 -0700
Processing by CommentsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"A6MOeMgoGUDmGiJr9PWinHVTAa7X63fgtA7+2my0A2Y=", "comment"=>{"user_id"=>"1", "status_date"=>"2013-12-19", "worequest_id"=>"10", "statuscode_id"=>"", "comments"=>"test", "attachments_attributes"=>{"0"=>{"name"=>""}}}, "_wysihtml5_mode"=>"1", "commit"=>"Save Comment"}
Tenant Load (0.3ms) SELECT "tenants".* FROM "tenants" WHERE "tenants"."subdomain" = 'ame' LIMIT 1
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."tenant_id" = 1 AND "users"."id" = 1 LIMIT 1
(0.1ms) BEGIN
(0.1ms) ROLLBACK
I need to figure out the right code so that the Attachment fields show up in the form, but the Comment will get created if no Attachment is selected.
Maybe I need to put code in the Attachment controller?

You could use Rails present? method to check if the object is not blank:
#comment.attachments.build if #comment.attachments.present?

I changed the Comment model to this:
accepts_nested_attributes_for :attachments, :reject_if => lambda { |a| a[:attach].blank? }, :allow_destroy => true

Related

Image from polymorphic model does't display

I have a model named Hen:
class Hen < ApplicationRecord
has_many :pictures, as: :imageable
validates :name, :description, presence: true
end
I added the :picture to whitelisted params in the HensController as well
Picture model:
class Picture < ApplicationRecord
belongs_to :imageable, polymorphic: true
has_attached_file :image
end
When creating new Hen, I can check that the instance is created.
In console Hen.last.pictures returns
[["imageable_id", 3], ["imageable_type", "Hen"], ["LIMIT", 11]]
=> #
,so I assume that it is fine there.
... but I do not know how to dispay it in the view section. After some tries I have done that:
<p><%= #hen.pictures do |pic| %></p>
<%= image_tag pic.url %>
<% end %>
It dispays
#<Picture::ActiveRecord_Associations_CollectionProxy:0x00005640044034f0>*
because I left the "=" there (to check if there is any object inside), but the picture is not displayed. I checked some other variants like pic.image_url and nothing works for me. Other images on the page display without problems.
Console returns status 200:
Processing by HensController#show as HTML
Parameters: {"id"=>"3"}
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
Hen Load (0.5ms) SELECT "hens".* FROM "hens" WHERE "hens"."id" = $1 LIMIT $2 [["id", 3], ["LIMIT", 1]]
↳ app/controllers/hens_controller.rb:16:in `show'
Rendering hens/show.html.erb within layouts/application
Rendered hens/show.html.erb within layouts/application (Duration: 5.9ms | Allocations: 3302)
[Webpacker] Everything's up-to-date. Nothing to do
Completed 200 OK in 125ms (Views: 67.7ms | ActiveRecord: 10.7ms | Allocations: 36995)*
You're not actually looping through pictures. You need to use #hen.picutres.each or for picture in #hen.pictures. Without this, you're just passing a block to #hen.pictures; the block gets ignored and the expression returns #hen.pictures (Picture::ActiveRecord_Associations_CollectionProxy).
The equivalent plain-ruby would be
#hen.pictures { ... this block isn't used but it's not a syntax error ... }
instead of
#hen.pictures.each { |picture| ... this block is used by the `each` method }
Here <%= #hen.pictures do |pic|%> you are printing the picture url. you should not include = when you are looping <%= #hen %> wrong you should use <% #hen %> then you need to loop the pictures using each.
<% #hen.pictures.each do |pic| %>
<%= image_tag pic.url %>
<% end %>
if the picture url in hash try to convert it into string using .to_s
<%= image_tag pic.url.to_s %>
This may not be the direct answer for the problem, but I found that better option is to use ActiveStorage than creating Polymorphic associations between models.
First, install the ActiveStorage:
rails active_storage:install
then add macro has_one_attached or has_many_attached to the models:
class Hen < ApplicationRecord
has_one_attached :image
validates :name, :description, presence: true
end
add to the view form:
<%= f.file_field :image %>
whitelist the :image attribute:
def hens_params
params.require(:hen).permit(:name, :description, :image)
end
Done.
To dispay the image in views:
<%= image_tag hen.image if hen.image.attached? %>
No need to create new model or to nest attributes.

Working with Rails arrays in Postgres

I have a postgres column called content which is an array.
But when trying to use it in a form I'm getting:
can't cast ActionController::Parameters to text
Despite the fact that the output looks pretty good:
{"utf8"=>"✓",
"_method"=>"patch",
"authenticity_token"=>"NkK4BggxknfEn0A8shTs06xmesERaZdYtZdl9oEEUTk=",
"notification_template"=>{"content"=>{"0"=>"Join us {{event_time}} {{{twitter_name}}} to win Big! hint: {{{question}}} #quiz {{location_tags}} {{url}} sdfsdfsdf"}},
"commit"=>"Update Notification template",
"id"=>"25"}
strong params
params.require(:notification_template).permit(:name, :content => {})
routes
resources :notification_templates do
get 'edit/:id', to: 'notification_templates#edit_content', as: 'edit_content'
end
controller
def edit_content
#notification_template = NotificationTemplate.find(params[:notification_template_id])
end
def update
if #notification_template.update(notification_template_params)
redirect_to admin_notification_template_path(#notification_template), notice: 'Social message was successfully updated.'
else
render action: 'edit'
end
end
my form
the url looks like: /notification_templates/25/edit_content/7 # custom action, but uses normal update
<%= simple_form_for([:admin, #notification_template]) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.simple_fields_for :content do |fields| %>
<%= fields.input params[:id], input_html: { value: #notification_template.content[params[:id].to_i] } %>
<% end %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
the DB column
add_column :notification_templates, :content, :text, array: true, default: []
Lastly, I was unsure about the conventions for adding it. The above worked fine, but I also noticed other possibilities such as
add_column :notification_templates, :content, :text, array: true, default: []
add_column :notification_templates, :content, :sting, array: true, default: []
add_column :notification_templates, :content, :text, array: true, default: {}
I choose the first one on the basis that a string wouldn't allow for as many characters as I might eventually need and text is more convenient. Also the default of [] vs {} or '{}'
But in postgres is see content text[] DEFAULT '{}'::text[]
log
Started PATCH "/admin/notification_templates/25" for 127.0.0.1 at 2014-11-28 14:25:43 +0100
Processing by Admin::NotificationTemplatesController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"NkK4BggxknfEn0A8shTs06xmesERaZdYtZdl9oEEUTk=", "notification_template"=>{"content"=>{"4"=>"{{{question}}} Study up and stop by {{{twitter_name}}} {{event_time}} for a #quiz {{location_tags}} {{url}} sdfsdfsdf"}}, "commit"=>"Update Notification template", "id"=>"25"}
User Load (0.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
NotificationTemplate Load (0.5ms) SELECT "notification_templates".* FROM "notification_templates" WHERE "notification_templates"."id" = $1 LIMIT 1 [["id", 25]]
(0.3ms) BEGIN
(0.3ms) ROLLBACK
Completed 500 Internal Server Error in 54ms
Reporting exception: can't cast ActionController::Parameters to text
TypeError (can't cast ActionController::Parameters to text):
app/controllers/admin/notification_templates_controller.rb:40:in `update'
Rendered /Users/holden/.rvm/gems/ruby-2.0.0-p481#questionone-2.0/gems/actionpack-4.1.7/lib/action_dispatch/middleware/templates/rescues/_source.erb (1.1ms)
Rendered /Users/holden/.rvm/gems/ruby-2.0.0-p481#questionone-2.0/gems/actionpack-4.1.7/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (2.0ms)
Rendered /Users/holden/.rvm/gems/ruby-2.0.0-p481#questionone-2.0/gems/actionpack-4.1.7/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (1.4ms)
Rendered /Users/holden/.rvm/gems/ruby-2.0.0-p481#questionone-2.0/gems/actionpack-4.1.7/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (27.5ms)
UPDATE
I also observed that update array type field doesn't work as expected in the console.
eg. if I attempt to update a member of the array, something = record.content[2] = 'blah' it appears to work. But when I save the record it doesn't update it.
Yeah, Rails postgres Arrays are still a bit wonky. Hstore is a bit easier.
You may be better served going thru a virtual attribute and doing what you want expressly rather than relying on standard rails behavior through a form.
eg.
def content_member=(member)
unless member.blank?
self.content_will_change!
self.content[member.keys.first.to_i] = member.values.first
end
end
You also need to let rails know if you're going to update a member of an array, that's why it doesn't work in the console.
There's a full explanation here:
Rails 4 Postgresql array data-type: updating values

Rails many-to-many creation fails

I have event and band models, which have a many-to-many relationship through event_bands. I am trying to change my create method to use chosen jQuery like in episode 258 of railscasts. I am not sure how to read the message from my localhost when I try to create an event:
Started POST "/events" for 127.0.0.1 at 2014-03-16 17:11:07 +0900
Processing by EventsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Jx1Cm09uCwJcnY8573ZTRKMjH1BHWhlREFCfhij/AB0=", "event"=>{"name"=>"pojpj", "ko_name"=>"", "band_ids"=>["", "110"], "venue_id"=>"", "date(1i)"=>"2014", "date(2i)"=>"3", "date(3i)"=>"16", "time"=>"", "contact"=>"", "facebook"=>"", "ticket"=>"true", "price"=>"", "door_price"=>"", "ticket_url"=>"", "info"=>"", "info_ko"=>""}, "commit"=>"등록", "locale"=>"ko"}
Band Load (0.2ms) SELECT "bands".* FROM "bands" WHERE "bands"."id" = ? LIMIT 1 [["id", 110]]
(0.1ms) begin transaction
Band Exists (1.0ms) SELECT 1 AS one FROM "bands" WHERE ("bands"."name" = '...Whatever That Means' AND "bands"."id" != 110) LIMIT 1
(0.2ms) rollback transaction
Redirected to http://localhost:3000/events/new
It looks like it fails because the Band already exists in the database, but why is it doing that instead of creating the relation?
def new
#event = Event.new
end
def create
#event = Event.new(event_params)
if #event.save
flash[:notice] = "Event Created!"
redirect_to event_path(#event)
else
flash[:notice] = "Event not created!"
redirect_to new_event_path
end
end
private
def event_params
params.require(:event).permit(:name,
:ko_name,
:avatar,
:time,
:facebook,
:ticket,
:door_price,
:ticket_url,
:info_kr,
:contact,
:price,
:info,
:info_ko,
:venue_id,
:date,
band_ids: [])
end
Relevant part of the form:
<p>
<%= f.label "Bands" %><br />
<%= f.collection_select :band_ids, Band.order(:name), :id, :name, {}, {multiple: true} %>
</p>
I have accepts_nested_attributes_for :bands in the model and I think the relation is setup correctly because I can do a = Event.new(name: 'asdf', band_ids: [1,5]) a.save and it is persisted.
update: it seems the problem is coming from the empty item in band_ids. For some reason, rails is setting the param as band_ids: ['',3,5]. I can replicate the SQL message from my server by trying to create a new entry in the console like this: a = Event.create(name: 'asdfasdfasdfasdf2345', band_ids: ['', 3, 2]). But where is the empty first item coming from?
edit2: Disregard the above, it seems the problem is actually that there is no event_id to use in the association?
#messages={:"event_bands.event_id"=>["에 내용을 입력해 주세요"]}

How to make a search form with acts_as_taggable

I have a search form to search for images by their tags. The form kinda works, it sends the parameters to the /search_results page but it sends as this:
search_results?utf8=✓&search=squid%2C+color&x=0&y=0
And here is my form:
<%= form_tag ("/search_results"), :method => "get", :class=>"search_form" do %>
<%= text_field_tag ("search"), nil, :class => 'search_input',
:onblur=>"if(this.value=='')this.setAttribute('class', 'search_input');",
:onfocus=>"this.setAttribute('class', 'search_input_clear');"
%>
<%= image_submit_tag("search.png") %>
<% end %>
and and my route/controller:
match "/search_results/" => "index#search_results", :via => :get, :as =>"search_results"
class IndexController < ApplicationController
def search_results
#tattoos = Tattoo.tagged_with("%#{params[:search]}%")
end
But I never get any results.
Rails console shows this:
Parameters: {"utf8"=>"✓", "search"=>"color, animals", "x"=>"0", "y"=>"0"}
SQL (0.5ms) SHOW TABLES
ActsAsTaggableOn::Tag Load (0.2ms) SELECT `tags`.* FROM `tags` WHERE (name LIKE '\\%color' OR name LIKE 'animals\\%')
SQL (0.1ms) SELECT COUNT(*) FROM `tattoos` WHERE (1 = 0)
Tattoo Load (0.3ms) SELECT `tattoos`.* FROM `tattoos` WHERE (1 = 0) ORDER BY tattoos.created_at DESC
I removed the % surrounding my params and that seemed to do the trick:
def search_results
#tattoos = Tattoo.tagged_with("#{params[:search]}")
end

Named Scope Problem With "Has Many Through" Association

I'm using named scopes to process some filtering actions, and the log is showing that everything is working perfectly, except that after the app goes and finds the correct data, it then ignores what it found and just lists a find.all instead of the filtered result. Here's the details.
I have 3 models: Users, Markets and Schedules.
User has_many :schedules
User has_many :markets, :through => :schedules
Market has_many :schedules
Market has_many :users, :through => :schedules
Schedule belongs_to :user
Schedule belongs_to :market
On the "show" page for each market, I display the users who sell things at that market. I also display the days of the week that such users are at the market. This data is contained in the join model (i.e. schedules).
On the market page, I need to support filtering the users by day of the week that the user is at the market.
In my Market controller, I have this:
def show
#market = Market.find(params[:id])
#users = #market.users.filter_marketdate(params[:filter])
end
In my Market model, I have this:
def self.filter_marketdate(filter)
case filter
when 'monday' then monday.all
else find(:all)
end
end
In my User model, I have this:
named_scope :monday, :include => :schedules, :conditions => {'schedules.monday' => true }
AND
def self.filter_marketdate(filter)
case filter
when 'monday' then monday.all
else find(:all)
end
end
In my show view for Markets, I have this:
<%= link_to_unless_current "All", :filter => 'all' %>
<%= link_to_unless_current "Mon", :filter => 'monday' %>
<% #market.schedules.each do |c| %>
<%= link_to c.user.name, c.user %>
<% end %>
Here's the weird part. The following is my log output when I select the Monday filter. If you look at the Select Schedules line of the output, you can see that the query is finding a limited number of user IDs. These are, in fact, the correct user IDs that I want to display for my filtered query. The part I don't understand is that after the app does the query perfectly, it then goes and loads all of the users instead of just the filtered results. I'm not sure what I'm missing.
Processing MarketsController#show (for ip at 2009-09-21 05:19:25) [GET]
Parameters: {"id"=>"1", "filter"=>"monday"}
Market Load (0.8ms) SELECT * FROM "markets" WHERE ("markets"."id" = 1)
User Load (7.3ms) SELECT "users".* FROM "users" INNER JOIN "schedules" ON "users".id = "schedules".user_id WHERE ((("schedules".market_id = 1)) AND ("schedules"."monday" = 't'))
Schedule Load (4.3ms) SELECT "schedules".* FROM "schedules" WHERE ("schedules".user_id IN (16,23,25,30,39,61,73,75,85,95,97,111,112,116,121,123,126,134,136,145,160,165,171,188,189))
Rendering template within layouts/application
Rendering markets/show
Schedule Load (14.3ms) SELECT * FROM "schedules" WHERE ("schedules".market_id = 1)
User Load (0.8ms) SELECT * FROM "users" WHERE ("users"."id" = 2)
User Load (0.8ms) SELECT * FROM "users" WHERE ("users"."id" = 8)
It goes on to list every user who is connected to this particular market, essentially doing a find.all.
UPDATE
In response to Brad's comment below, I tried changing the code in my markets/show view to the following, but I got the same result. I agree that the problem is in here somewhere, but I can't quite figure out how to solve it.
<%= link_to_unless_current "All", :filter => 'all' %>
<%= link_to_unless_current "Mon", :filter => 'monday' %>
<% #market.users.each do |user| %>
<%= link_to user.name, user %>
<% end %>
In your view, you are iterating over each market.schedule, not over the #users variable that you set. In fact, you aren't even using #users in the code you show us.
Shouldnt that be:
#Market.rb
has_many :users, :through => :schedules
Beside this, maybe this plugin helps you:
http://github.com/ntalbott/query_trace
It provides feedback for each sql statement and where it comes from..
f.e.:
Before:
Schedule Load (0.023687) SELECT * FROM schedules WHERE (schedules.id = 3) LIMIT 1
Resource Load (0.001076) SELECT * FROM resources WHERE (resources.id = 328) LIMIT 1
Schedule Load (0.011488) SELECT * FROM schedules WHERE (schedules.id = 3) LIMIT 1
Resource Load (0.022471) SELECT * FROM resources WHERE (resources.id = 328) LIMIT 1
After:
Schedule Load (0.023687) SELECT * FROM schedules WHERE (schedules.id = 3) LIMIT 1
app/models/available_work.rb:50:in `study_method'
app/helpers/plan_helper.rb:4:in `work_description'
app/views/plan/_resource_schedule.rhtml:27:in `_run_rhtml_plan__resource_schedule'
app/views/plan/_resource_schedule.rhtml:24:in `_run_rhtml_plan__resource_schedule'
app/views/plan/_schedule_listing.rhtml:5:in `_run_rhtml_plan__schedule_listing'
app/views/plan/_schedule_listing.rhtml:3:in `_run_rhtml_plan__schedule_listing'
app/views/plan/_schedule_listing.rhtml:1:in `_run_rhtml_plan__schedule_listing'
app/views/plan/index.rhtml:6:in `_run_rhtml_plan_index'
vendor/plugins/textmate_footnotes/lib/textmate_footnotes.rb:60:in `render'
Resource Load (0.001076) SELECT * FROM resources WHERE (resources.id = 328) LIMIT 1
app/models/available_work.rb:54:in `div_type'
app/helpers/plan_helper.rb:6:in `work_description'
app/views/plan/_resource_schedule.rhtml:27:in `_run_rhtml_plan__resource_schedule'
app/views/plan/_resource_schedule.rhtml:24:in `_run_rhtml_plan__resource_schedule'
app/views/plan/_schedule_listing.rhtml:5:in `_run_rhtml_plan__schedule_listing'
app/views/plan/_schedule_listing.rhtml:3:in `_run_rhtml_plan__schedule_listing'
app/views/plan/_schedule_listing.rhtml:1:in `_run_rhtml_plan__schedule_listing'
app/views/plan/index.rhtml:6:in `_run_rhtml_plan_index'
vendor/plugins/textmate_footnotes/lib/textmate_footnotes.rb:60:in `render'
UPDATE:
You have to call
<%= link_to_unless_current "All", :filter => 'all' %>
<%= link_to_unless_current "Mon", :filter => 'monday' %>
<% #users.each do |user| %>
<%= link_to user.name, user %>
<% end %>

Resources