Rails In Place Editing - ruby-on-rails

I'm using rails 3 and am trying to use the in_place_editing plugin:
http://github.com/wanglian/in_place_editing
# Controller
class BlogController < ApplicationController
in_place_edit_for :post, :title
end
# View
<%= in_place_editor_field :post, 'title' %>
However I'm getting the error: id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
I'm calling the plugin in my photo_album controller, which has a title attribute...
class PhotoAlbumsController < ApplicationController
in_place_edit_for :photo_album, :title
The in the Index View, I'm doing the following:
<% #photoalbums.each do |photoalbum| %>
<%= in_place_editor_field :photoalbum, 'title' %>
<% end %>
Does anyone understand this or have experience with this plugin?
Thanks

The error is because, its trying to update the title of a nil object. You should use this instead
<% #photoalbums.each do |photoalbum| %>
<%= in_place_editor_field photoalbum, 'title' %>
<% end %>
if you see the code of the plugin the definition of the method is
def in_place_editor_field(object, method, tag_options = {}, in_place_editor_options = {})

In case you need to use in_place_editor_field in a loop, do something like this:
Class Item < AR::Base
#attributes like name, price
end
<% for some_item in #items %>
<% #item = some_item %>
<%= in_place_editor_field :item, :price %>
<% end %>

Related

Ruby on Rails: Why empty value is displayed when form for creation of new objects in DB is included?

I am newbie. I am trying to develop a simple web application involving shops and candies where a shop can have many candies.
I have the following code in my shops/show.html.erb which displays list of candies twice.
<% i=0 %>
<% for candy in #shop.candies do %>
<% i+=1 %>
<%= i %> <%= candy.name %>
<% end %>
<%= form_for([#shop, #shop.candies.build]) do |f| %>
<%= render(:partial => 'form',
:locals => {:f => f, :header => "Add a Candy",
:placeholder => "Enter a candy name"}) %>
<% end %>
<% i=0 %>
<% for candy in #shop.candies do %>
<% i+=1 %>
<%= i %> <%= candy.name %>
<% end %>
My code in _form.html.erb for creating a new candy:
<%= f.text_field(:name, :placeholder=> placeholder, :class=>"form-control custom-input")%>
<%= button_tag( :class => "btn btn-primary mb-2 btn-custom btn-custom-sc") do %>
<i class="fas fa-plus icon"></i>
<% end %>
Code of Shops Controller:
class ShopsController < ApplicationController
def show
#shop = Shop.find(params[:id])
#unshelved_candies = #shop.candies.unshelved_candies
end
private
def shop_params
params.require(:shop).permit(:name)
end
end
Code of Candies Controller:
class CandiesController < ApplicationController
def create
#shop = Shop.find(params[:shop_id])
#candy = #shop.candies.create(candy_params)
redirect_to(shop_path(#shop))
end
private
def candy_params
params.require(:candy).permit(:name)
end
end
end
When I run the code and view it on browser, I notice that it creates an empty candy in the second loop (not in database). However, when I remove the form for creating candies, it behaves as it should. I am unable to understand why it's looping one more time and displaying blank value.
The output of the first loop is the correct one:
Candy 1
Candy 2
Candy 3
And the output of second loop is:
Candy 1
Candy 2
Candy 3
[<---Empty. I am not inserting anything new to the database]
Can anybody tell me why it is displaying a blank value in second loop and how to prevent this extra iteration?
I believe the "extra" candy is the one you're instantiating here:
<%= form_for([#shop, #shop.candies.build]) do |f| %>
The candy name for the new candy is nil, so you're getting the blank.
BTW, this:
<% i=0 %>
<% for candy in #shop.candies do %>
<% i+=1 %>
<%= i %> <%= candy.name %>
<% end %>
Strikes me as non-idiomatic ruby. I would expect to see something more like:
<% #shop.candies.each.with_index(1) do |candy, index| %>
<%= index %> <%= candy.name %>
<% end %>
I guess a brute force way of making sure you don't get that extra candy would be to do something like:
<% #shop.candies.each.with_index(1) do |candy, index| %>
<% unless candy.new_record? %>
<%= index %> <%= candy.name %>
<% end %>
<% end %>
You might also try:
<%= form_for([#shop, #candy]) do |f| %>
Which I believe can be written:
<%= form_for(#shop, #candy) do |f| %>
If you want to save yourself a couple of key strokes (they add up over time).
And then in your ShopsController do:
class ShopsController < ApplicationController
def show
#shop = Shop.find(params[:id])
#candy = Candy.new
#unshelved_candies = #shop.candies.unshelved_candies
end
private
def shop_params
params.require(:shop).permit(:name)
end
end
This is also nice because it avoids:
#shop.candies.build
Which requires that your view knows a lot about the relationship between shop and candies and also requires that your view interacts directly with the database.
Since you're apparently using nested routes, you might want to look at the shallow: true directive.
Also (this is not related to your question), you might want to be thoughtful about the Law of Demeter. I notice you do:
#unshelved_candies = #shop.candies.unshelved_candies
Personally, I would do something more like:
#unshelved_candies = #shop.unshelved_candies
And in Shop, you might have something like:
class Shop < ApplicationRecord
def unselved_candies
candies.unshelved
end
end
And in Candy, something like:
class Candy < ApplicationRecord
class < self
def unshelved
where(shelved: false) # or however you determine a candy is unshelved
end
end
end
Many people would make unshelved a scope, which is another way of doing the same thing.
This way, your ShopsController knows less about the mechanics of the relationships between shops and candies and shelved status. FWIW.

In Rails, how can I create a button that saves an entry from an external api into my database?

I have an Rails api that consumes an external design search api using the HTTParty gem. With the data that is returned to my view I'd like to be able to save selected entries to my database. Kind of like a bookmark function. Is there anyway of having a button next to each search result item that would achieve this? Any help would really be appreciated. My current code is below.
designs controller:
class DesignsController < ApplicationController
def index
#search = Api.new.find(params[:q])['results']
end
end
Class method:
class Api
include HTTParty
base_uri "http://search.example.com/searchv2"
attr_accessor :name, :limit, :offset
# Find a particular design, based on its name
def find(name)
self.class.get("/designs", query: { q: name }).parsed_response
end
end
View:
<h1>Design Search</h1>
<%= form_tag(designs_path, method: :get) do %>
<%= label_tag(:q, "Search for:") %>
<%= text_field_tag(:q) %>
<%= submit_tag("Search") %>
<% end %>
<h2>Search results:</h2>
<% #search.each do |design| %>
<h3><%= design['name'] %></h3>
<h5><%= image_tag design['thumbnail_url'] %></h5>
<% end %>
Design model:
class Design < ApplicationRecord
end
app/views/designs/index.html.erb:
...
<% #search.each do |design| %>
<h3><%= design['name'] %></h3>
<h5><%= image_tag design['thumbnail_url'] %></h5>
<%= button_to 'Save',
designs_path(
design: design.slice('name', 'thumbnail_url') # etc...
),
method: :post,
data: { disable_with: 'Loading...' }
%>
<% end %>
app/config/routes.rb:
resources :designs, only: [:index, :create]
app/controllers/designs_controller.rb:
class DesignsController < ApplicationController
# ...
def create
#design = Design.new(design_params)
#design.save!
# just an example...
redirect_to designs_path, success: 'Design has been saved!'
end
private
def design_params
params.require(:design).permit(:name, :thumbnail_url) # etc...
end
end
TODOs:
your Design model needs to have attributes that you want to be saved (i.e: :name? and :thumbnail_url?). My assumption was that you already have created/migrated these attributes; if not yet, then please do add them.
because there's still a vulnerability in my code above in that any user can modify the <button>'s <form action='...'> (i.e. they can modify the "name" of the design as they wish when it gets Saved), then my solution above is still not the best solution, though for simplicity and that assuming that this vulnerability is not a problem for you, then the above code already is sufficient.

Rails - How to render an image if a field is populated?

In my application a user can say they have completed a piece of homework.
A field "completed_on" is populated with the date the homework was completed. If not completed the field is blank.
I would like to show a tick if the homework is completed or an x if the homework is not completed.
The column "completed_on" is located in a table called homework_students.
class Homework < ActiveRecord::Base
has_many :homework_students, :class_name => 'HomeworkStudent'
class HomeworkStudent < ActiveRecord::Base
belongs_to :homework, :class_name => 'Homework'
I have tried the following which does not work for me:
In my model:
def getCompletion
homework_students.where("completed_on is not null")
end
View:
<% if homework.getCompletion %><%= image_tag "fi-check.svg", class: "homework_student_complete" %><% else %><%= image_tag "fi-x.svg", class: "homework_student_complete" %><% end %>
I also tried this:
Model
def completed?
!homework.homework_student.completed_on.blank?
end
View
<% if homework.completed? %>
<%= image_tag "fi-check.svg", class: "homework_student_complete" %><% else %><%= image_tag "fi-x.svg", class: "homework_student_complete" %><% end %>
Appreciate any guidance.
UPDATE
This works:
def completed?
homework_students.where("completed_on is not null").length == 0
end
<% #homeworks.each do |homework| %>
...
<td height="1" class="text-center"><% if homework.completed? %>
<%= image_tag "fi-check.svg", class: "homework_student_complete" %>
<% else %>
<%= image_tag "fi-x.svg", class: "homework_student_complete" %>
<% end %></td>
...
<% end %>
The return type of your getCompletion method is an ActiveRecord::Relation. You are then asking if this relation exists by placing this inside a conditional. This will always be true, even if the relation contains an empty data set (e.g. []), because an empty Array is truthy.
You should be expecting getCompletion to return a collection of HomeworkStudent records. If you are wanting to show a tick, or an 'x' only if all students have completed the homework, then you need an aggregate method to check something about all of the records (if there are any, or none, etc). Try do something like:
# app/models/homework.rb
def completed?
homework_students.where("completed_on is null").none?
end
# in the view
<% if homework.completed? %>
<%= image_tag "fi-check.svg", class: "homework_student_complete" %>
<% else %>
<%= image_tag "fi-x.svg", class: "homework_student_complete" %>
<% end %>
More information on ActiveRecord::Relation methods at http://api.rubyonrails.org/classes/ActiveRecord/Relation.html#method-i-none-3F
The result of your query can be an ActiveRecord::Relation or an empty array and on both cases the if statement evaluates to true. Try changing your query to:
def completion
homework_students.where("completed_on is null").length == 0
end
your method get_completion is not returning anything. Try this
#homework_student = homework_students.where("completed_on is not null")
return #homework_student
may be it works. Also make sure that the completed_on is not null works correctly.

Send data between controllers

I have two models: Activity and Comment. Comment belongs_to :activity and Activity has_many :comments. For building activities, I've followed this Railscast (Activity feed from scratch)
So, I'm using activity_presenter.rb
class ActivityPresenter < SimpleDelegator
attr_reader :activity
def initialize(activity, view)
super(view)
#activity = activity
end
def render_activity
div_for activity do
link_to((gravatar_for activity.user, {size: 20}) + activity.user.name, activity.user) + ' ' + render_partial
end
end
def render_partial
locals = {activity: activity, presenter: self}
locals[activity.trackable_type.underscore.to_sym] = activity.trackable
render partial_path, locals
end
def partial_path
partial_paths.detect do |path|
lookup_context.template_exists? path, nil, true
end || raise("No partial found for activity in #{partial_paths}")
end
def partial_paths
[
"activities/#{activity.trackable_type.underscore}/#{activity.action}",
"activities/#{activity.trackable_type.underscore}",
"activities/activity"
]
end
end
which renders _activities_feed.rb partial with #activities.each do |activity| condition.
In this partial I have 3 blocks:
activity with form for activity for updating purposes
activity.comments block with each do |comment|
activity.comments.new (form)
And this is a problem. I need to render Activity.comments and form for creating a new comment from comments controller, because I have 2 forms in one view and 2 submit buttons, which call 2 update or create actions. And, of course, I need it for making app logic better.
But I can't send activity from #activities.each do |activity|, which I use everywhere in this partial to comments_controller.
I've tried to add #activity = Activity.find(params[:id]), #comment = Activity.comments.new(params[:activity_id]), #comments = Activity.comments.all to activities and comments controllers, and also changed activity to #activity and activity.comments.new to #comment. But it doesn't help. I think, that I don't understand the basic logic of interaction between controllers, but can't find my mistakes.
So, my activities_controller's index action is:
#activities = Activity.order('updated_at DESC')
And in comments_controller I only have create and destroy actions, which work ok.
Thanks for any help!
UPD:
This is my _activities_feed.rb structure:
<% #activities.each do |activity| %>
<%= form_for activity, remote: true do |a| %>
<%= ActivityPresenter.new(activity, self).render_activity %>
.
.
.
<%= link_to ... class: 'add_comment', remote: true %> # Shows activity.new.comment form
<%= link_to ... 'Edit' ... %> # edit activity link
<%= a.submit 'Update' %> # first submit button for submitting activity changes
.
.
.
<% activity.comments.each do |comment| %> # activity.comments block
<%= comment.text %>
<%= link_to comment.user.name, comment.user %>
<% end %>
.
.
.
<%= form_for activity.comments.new, remote: true do |f| %> # form for adding comment
<%= f.text_area :text, 'rows' => '2', class: 'comment_text' %>
<%= f.button 'Cancel', class: 'btn comment_cancel', type: 'button' %>
<%= f.submit 'Post', class: 'btn btn-primary', controller: 'comments' %> # second submit button
<%= f.hidden_field :activity_id, value: activity.id %>
<% end %>
<% end %>
<% end %>
The problem was with nested forms. So, when I submitted form for new comment, it also submitted the parent form for updating activity.

Scope Search Field

I have a scope configured to search a database on a name. The scope looks like:
class AdminVerified
scope :search, lambda {|query| where(["name LIKE ?", "%#{query}%"])}
end
I want to call this scope in a form. Does anyone know how to call a scope in a form. I want to create a form_tag that has a text field that its parameters call the scope. Any ideas?
First you need to create a form for searching..
<%= form_tag("/search") do %>
<%= text_field_tag :search %>
<%= submit_tag %>
<% end %>
In controller
def search
#results = AdminVerified.search(params[:search])
end
In view
<% #results.each do |r| %>
<%= r.field_1 %>
...
<% end %>
Hope this helps

Resources