I am creating a small college project in Ruby on Rails, and I came across a problem: I have a table named Person and another called Tools.
People have many tools and each tool and possessed by a person.
I need to add a feature that allows the loan of tools between people of the system. For this, I created an attribute called 'loan' in the Tools table, which has a value of 0 means that the tool not this borrowed and if it is 1, that this borrowed. In view show the tools, I created a button with the function to borrow the tool. My problem is how do to make this button change the attribute 'loan' from the Tools table from 0 to 1 and then reverse it. Someone would have a solution or a better suggestion?
I would create 2 routes name "barrow" and "retrieve" like this:
#config/routes.rb
get '/tool/:id/barrow' => 'tools#barrow', as: :barrow
get '/tool/:id/retrieve' => 'tools#retrieve', as: :retrieve
Now in your view you can do something like
# tool.index.html.erb
...
<% if tool.loan %>
<%= link_to retrieve_path(tool), 'Retrieve', class: 'btn ...' %>
<% else %>
<%= link_to barrow_path(tool), 'Barrow', class: 'btn ...' %>
<% end %>
...
now you have to create the controller action you have to create the retrieve and barrow methods
#app/controllers/tool_controller.rb
class UserController < ApplicationController
...
def retrieve
#tool = Tool.find_by(params[:tool])
#tool.update_attributes(:loan, 0)
end
def barrow
#tool = Tool.find_by(params[:tool])
#tool.update_attributes(:loan, 1)
end
...
end
I hope that this help you
Related
I'm trying to implement adding upvote and downvotes to an app, submit the controller actions remotely using a button or link_to, and refresh a count section with AJAX.
Somehow upvoting or downvoting always redirects to a path of a member. When I use head :no_content, I can't submit the form aka link_to. Having some respond_to do | f |... also just renders the action URL.
Thus, remote: true is kind of not working, as I have another controller using <%= form_for([entry, entry.review], remote: true, :authenticity_token => true) do |f| %> and it works perfectly.
I have tried implementing here, here, here, and this tutorial and this but nothing seems to work.
I'm using the acts_as_votable. Everything works except the routing functionality with AJAX
# routes.rb
resources :entries do
member do
post 'upvote'
post 'unupvote'
post 'downvote'
post 'undownvote'
end
end
# entries_controller.rb
def upvote
#entry.liked_by current_user
end
def unupvote
#entry.unliked_by current_user
end
def downvote
#entry.disliked_by current_user
end
def undownvote
#entry.undisliked_by current_user
end
# entries/index.html.erb
...
<%= button_to upvote_entry_path(entry.id), remote: true,
class: "btn",
id: "upvote-button-#{ entry.id }" do %>
<i class="bi bi-hand-thumbs-up" style="color: #ababab;" id="upvote-<%= entry.id %>"></i>
<small id="upcount-<%= entry.id %>" >
<%= render 'entries/upvote', entry: entry %>
</small>
<% end %>
...
# entries/_upvote.html.erb
<%= entry.get_upvotes.size %>
# entries/upvote.js.erb
$('#upvote-<%= entry.id %>').className('bi bi-hand-thumbs-up-fill');
$('#upvote-<%= entry.id %>').attr('href', '/entries/<%= entry.id %>/unupvote');
$('#upcount-<%= entry.id %>').html('<%=j render 'entries/upvote', entry: entry %>');
Edit, I have changed my link_to to button_to, changed the routes to post
Not sure if your a tag is inside other elements but you might want to consider using a button_to instead of a link_to.
So far the temporary solution, not probably recommended but still does the job programming practice, is hiding and unhiding certain elements on specific conditions.
Upon user interaction (e.g onClick), unhide and hide elements while updating values that resemble the controller update. Simply simulate a refresh and update the form paths/controller actions they point to.
This will mostly work better with boolean check-box forms, likes or dislikes where variables will only alternate between two instances at max. Hence you don't have to write much javascript to handle pseudo outputs.
But still, I'm sure there's a better method for example WebSockets through Action Cable. That can probably be overkill but the closest to realtime updates. respond_to just doesn't seem to work
I am building an E-commerce site on rails (I somehow convinced myself it was a good challenge haha) and am now having an issue where the button "edit/confirm order" which would appear once a user clicks on 'add to cart' isno longer appearing, and when I use raise it shows that an order_id is no longer being saved. I can't find issues with my create method why that would be the case
def create
if params[:order_sauce][:order_id].blank?
order = Order.create(state: 'pending')
session[:order_id] = order.id
order_sauce = OrderSauce.new(order_sauce_params)
order_sauce.order = order
order_sauce.save
else
OrderSauce.create(order_sauce_params)
end
redirect_back(fallback_location: order_sauces_path, notice: "succesfully added to cart")
end
def order_sauce_params
params.require(:order_sauce).permit(:sauce_id, :quantity, :order_id)
end
It seems like an order_id is no longer being generated, evident by;
<% if session[:order_id] %>
<%= link_to 'Edit/Confirm Order', edit_order_path(session[:order_id]), class: "checkout" %>
<% end %>
no longer returning anything
This is another I'm-totally-new-to-Ruby-please-have-mercy situation.
So i'm trying to figure out how to make a database of all my buttons to save the click count each time they're clicked. I started a new rails to try it out and generated a model Buttonand a controller buttons index
route.rbs
Rails.application.routes.draw do
resources :buttons
root 'buttons#index'
end
migration
class CreateButtons < ActiveRecord::Migration[5.0]
def change
create_table :buttons do |t|
t.integer :clicks
t.timestamps
end
end
end
buttons_controller
class ButtonsController < ApplicationController
def index
#button = Button.find(1)
end
def doit
#button = Button.find(1)
#newcount = #button.clicks + 1
Button.find(1).update_attributes(:clicks => #newcount)
end
end
Now.. i need to trigger the doit method.. is it possible to trigger a non CRUD operation ?
i tried this but it doesn't seem to work
index.html.erb
<h1>Hello, This is button and my click are :</h1>
<h1><%= #button.clicks %></h1>
<%= link_to 'click me', method: :doit %>
I know there's something I'm not getting here...
Ruby have been doing so much magic that I can't do a simple ruby method.. it have been really hard for me getting the part were methods are taking place without calling them by name..
Specially when I trigger a delete method and the destroy method is triggered by that.. I really need to get used to this too-much-magic coding
Several things to improve, I think. Please get back to me if something is not working (I did not run the code)
Make your index action list all the buttons
Controller:
def index
#buttons = Button.all
end
View:
<h1>These are all my buttons</h1>
<% #buttons.each do |button| %>
<%= link_to("Button #{button.id}", button_votes_path(button), method: :post) %>
<% end %>
It's common to have index show a list of resources.
Only create the routes you need, make increment a separate action
I'd chose to call it "vote". You could also call it "clicks" or "presses" or whatever.
resources :buttons, only: [:index] do
resources :votes, only: [:create]
end
Add the votes controller
class VotesController < ApplicationController
def create
button = Button.find(params[:id])
button.clicks += 1
button.save
redirect_to buttons_path
end
end
No error handling here. So this is just to get you started.
For the next steps I suggest you follow a tutorial or start with simpler stuff.
I have a Rails 4 app, with a Thing model that have 3 attributes : attr_1, attr_2, attr_3.
I would like to have two edit forms :
the first one would update attr_1 and attr_2
the second one would only update attr_3
I have written four methods in my things_controller.rb :
def edit
#edit form for attr_1 & attr_2
end
def edit_attr_3
#edit form for attr_3 only
end
def update
#update attr_1 & attr_2
end
def update_attr_3
#update attr_3 only
end
And here's my routes.db
resources :things do
member do
get :edit_attr_3
put :update_attr_3
end
end
Now, GET to both edit views work (/things/:id/edit and /things/:id/edit_attr_3) but on submit, the thing#update form is called both times (thing#update_attr_3 is never called)
How can I proceed to get thing#edit_attr_3 action linked to thing#update_attr_3 please ?
EDIT :
Answer, thanks to Ross & Steve Klein :
In my views, I am using bootstrap_form_for. Usually the :url parameter is optional, as standard edit/update are linked by convention. In the case of custom edit or update actions, it is necessary to specify the update path.
<%= bootstrap_form_for #thing, url: update_attr_3_thing_path(#thing) do |f| %>
<% end %>
It's hard to say without seeing the code for your form... but if you are using form_for then the route it will map to the update action in both cases (with an object that is not a new object).
To make it work with your unique update action, you will have to specify the url that form_for should use. Something like:
<%= form_for #thing, url: things_update_attr_3_path(#thing) do |f| %>
# other form stuff .......
You can use bundle exec rake routes at the command prompt to see what your current routes are, so that you now what route helper to use with form for.
Context
Gems like mail_view, mailcatcher, rails_email_preview, etc. seem to be more developer-oriented (a way to debug a template). But I need something that will be used by the trusted users of my rails app in production.
My app is a project management app, where project managers can update the status of their projects, operations during which emails must be sent to project contractors, developers, clients, etc.
The project manager must be able to tell whether or not he wants to send an email (this is easy), and be able to customize to some extent the message content (not the design, only specific text parts should be enough). They DO want to have some control over the email about to be sent, ie, they need a preview of the email they customized. Why ?
Project Managers are trusted users/programmers, and I let them add HTML as custom parts of the email (We are talking about a small-scale app, and the project managers are all trusted employees). But a closing tag is easily forgotten, so I want to provide them with a mean to check that nothing is wrong. Eg. that the text does not all appear as <h2> just because they forgot a closing </h2>
Some email templates already include some info about what the PM is writing about, and the PM may not be aware of it (understand : may be too drunk to remember it). An email preview is just a way to avoid duplicate sentences (like two times Hello M. President,)
CSS styles are applied to the email. It can be hard to anticipate the effect of tags like <h2>, etc. So I need to render the email with the CSS
REMARKS
Previsualize & Send button
Project managers have access to a form that will feed the content to my Rails app. I am thinking on having both a normal submit button, and a previsualize button. I will probably use some tricks given by this SO question to differentiate the behaviours of the 2 buttons
Letter_opener : a nice gem, but exclusive ?
I am using letter_opener for debug (so different context), but this is typically the preview I'd like to show to the project manager. However, in order to be used, letter_opener requires to modify action_mailer configuration config.action_mailer.delivery_method = :sendmail # (or :letter_opener). So I can only previews emails, or send them for real, not both ? I would accept a solution that would let me choose whether to use letter_opener or send the email for real
Small Editor ?
Instead of blindly trusting my project managers' ability to write basic html without forgetting closing tag, maybe you could recommend a nice WYSIWYG editor that would show the content of my f.text_area() ?
This would be a bonus, not an actual answer to my question
Email rendering engine ?
I am now aware that different email clients can render the email client differently. I will ignore this for now. So the way the preview is rendered doesn't matter. I like the rendering of letter_opener however
Current Code
View > Controller > Mailer
my_email_view.html.erb
<%= form_tag some_mailing_list_path %>
<%= fields_for :email do |f| %>
<!-- f.text_field(:subject, ....), etc -->
<% end %>
<%= submit_tag("Send email") %>
<%= submit_tag("Preview") %>
<% end %>
my_controller.rb
before_action :prep_email # Strong parameters, define #mail with form contents
# Handles the POST
def some_action
check(:to, :from, :subject) # I check the parameters in private functions
if email_alright? # Above checks will raise a flag if something went wrong
if Rails.env.production?
MailingListsMailer.my_action(#mail).deliver_later
else
MailingListsMailer.my_action(#mail).deliver_now
end
flash.notice = "Email sent"
redirect_to :back
else
redirect_to :back
end
end
mailing_list_mailer.rb
def my_action(message)
format_mail_params(message) # Will set more variables
#etude = etude
#include_reference = message[:include_reference]
#include_description = message[:include_description]
dst = Proc.new { read_emails_file }
mail(
to: dst,
from: message[:from],
subject: #subject_full)
end
Question update: based on your pseudocode, this is a simple case of creating a status update model and emailing the update to a mailing list.
There are several ways you can go about it, but I'd suggest that you keep things simple and avoid using gems.
<%= link_to "New update", new_status_update_path, class: 'button' %>
Model
class StatusUpdate
belongs_to :sender, class_name: 'User'
belongs_to :mailing_list
end
Controller
class StatusUpdateController
def new
#status_update = StatusUpdate.new
end
def create
#status_update = StatusUpdate.create(status_update_params)
#status_update.mailing_list = MailingList.where(whichever_mailing_list)
if #status_update.save
redirect_to :action => "preview", :status_update => #status_update
end
end
def preview
#status_update = StatusUpdate.where(id: params[:id]).first
#mailing_list = MailingList.where(id: #status_update.mailing_list_id)
end
def send
#status_update = StatusUpdate.where(id:params[:status_update_id]).first
Mailer.status_update_email(#status_update).deliver
end
end
status_updates/new.html.erb
<%= simple_form_for(#status_update) do |f| %>
<%= f.input :title %>
<%= f.input :content, as: :text %>
<%= f.button :submit, 'Post update' %>
<% end %>
status_updates/preview.html.erb
<h1>Preview</h1>
<%= simple_form_for(#status_update, :url => url_for(:controller => 'StatusUpdateController, :action => 'send') do |f| %>
<%= f.input :subject %>
<div class="email-render-container">
<%= #status_update.content %>
</div>
<p>Make changes</p>
<%= f.input :content, as: :text %>
<%= f.button :submit, 'Approve and send emails' %>
<% end %>
If I were you, I'd do away with the preview feature. If you're
loading content from a template and all you're worried about are
potential duplicate content, just do this:
Controller
class StatusUpdateController
def new
#status_update = StatusUpdate.new
template = UpdateTemplate.where(however_you_assign_the_template)
#status_update.content = template.content
end
def create
#status_update = StatusUpdate.create(status_update_params)
#status_update.mailing_list = MailingList.where(whichever_mailing_list)
if #status_update.save
Mailer.status_update_email(#status_update).deliver
end
end
end
and style the new status update form with css to simulate writing on the actual email template. You'll save your users and yourself a lot of time.
wysiwyg editor
Never trust the end user with the ability to write html. Depending on your needs, I find https://www.froala.com/wysiwyg-editor easy to deploy.
differentiating buttons
Just use a preview icon with a label on your button and/or a subtitle under your button to differentiate your buttons. You don't need much command logic in your view.
Alternatively, if you think that the preview is important to your end users, just use the "preview" button as the next logical step instead of presenting your users with too many unnecessary choices.
Suggestions
Adopting a front end framework like Angularjs makes this sort of use case almost trivially easy, but it may be overkill and comes with steep learning curve if you're not familiar with it.
Take a look at letter_opener gem. It was created by Ryan Bates, the Railscasts guy.