Create link in Ruby on Rails to update a specific database entry - ruby-on-rails

I am new to Ruby on Rails and I want to create a very simple application. I used scaffolding to create a database called users. Two of the fields in users are limit and containers, which is the max number of containers a user can have and the remaining total containers that they can have. They both start off at a given number (e.g. 15), but when a user takes one containers will go down to 14 and will continue to decrease every time they take one, until it reaches 0. When a user returns a container, the number for container goes up by one until the max number, 15 in my example.
Since I created this using Ruby scaffolding, I can just go to the edit page for each user right now and manually change the containers value, but that can allow me to change it to any number. On the users/:id page I want to have a link like the edit link that is there right now
<%= link_to 'Edit', edit_user_path(#user) %>
and have something similar, but along the lines of
<%= link_to 'Take container', #run method to decrease and return here# %>
<%= link_to 'Return container', #run method to increase and return here# %>
For now, all I care about is just changing the number and elsewhere I will be rendering the user information.

AJAX would be a good fit here, but that would complicate the question, so I will stick to doing it with Rails.
You will first need to edit your routes.rb file to include something like:
get '/remove_container/:user_id', to: 'users#remove_container', :as => :remove_container
get '/add_container/:user_id', to: 'users#add_container', :as => :add_container
Then add methods in your Users controller:
def remove_container
user_id = params[:user_id]
#user = User.find user_id
# code to change container number
redirect_to edit_user_path(#user)
end
def add_container
user_id = params[:user_id]
#user = User.find user_id
# code to change container number
redirect_to edit_user_path(#user)
end
There are a lot of things to make this better, but this should get you going in the right direction anyway.
Links to these could be made manually or done with paths:
Remove Container
or
link_to "Remove Container", remove_container_path(#user)

Related

create edit URL route that doesn't show :id

I'm in the midst of trying to clean up my routing. I have a company model that can log in and create applications. They can create several.
Currently this is my setup:
Routes
get 'applications/edit/:id', to: 'applications#edit'
Applications_controller
def edit
#application = current_company.applications.find(params[:id])
end
def update
#application = Application.find(params[:id])
if #application.update(application_params)
redirect_to dashboard_path
else
render 'edit'
end
end
Each company have their own dashboard. Here's my code from
/dashboard
Your active applications
<% #applications.all.each do |f| %>
<%= link_to "Application", show_path + "/#{f.id}" %> | <%= link_to "Edit", edit_application_path("#{f.id}") %>
<br>
<% end %>
Now this all works, if I go to edit_application/11 f.ex I see it.
The thing I'd like changed is to remove the :id from the URL.
Thus make it more secure and to give a nicer feel. Now it took me 5 minutes before I realised I could just change the :id url and edit everything. Thus I added the current_company.applications to stop that. Yet I don't feel like this is very secure.
If you want to remove the :id, you'll still need a way to find the data you want.
As long as you have the url /edit/12 and as long as you use the id 12 in the GET url to find your content, it will show in the browser bar. The only way to "hide" it (but it's not more secure at all, because it's easily found out), is to use a POST request with a form containing a hidden field with the id (can be made in JavaScript).
You are asking the application to get the id from the link in the #applications.all.each but the only way it can do that is to include it somewhere in the request (be it GET, POST, COOKIES/SESSION, ...).
For another (possibly better) solution, read on.
A very common practice is to use slugs: you create a unique key for each content, for example, if your title is "My great app", the slug will be my-great-app. Thus there is no id in your URL (and it cannot be found out if you always use slugs as references). The advantage is that you'll still find a quick match for what you're searching for (creating an unique index on the slugs).
Some further reading about slugs:
http://rubysnippets.com/2013/02/04/rails-seo-pretty-urls-in-rails/
What is the etymology of 'slug'?

Redirecting to a random page while maintaining browser history

I'm trying to implement a "random page" link, which will render to the user a random article from the database. I've tried two separate (but similar) approaches, in both of which I route the URL "/random" to the "random" method in the ArticleController. Here's the first:
def random
offset = rand(Article.published.size)
#article = Article.published.offset(offset).first
render :action => 'show'
end
This works for serving random articles, but there are two issues: First, the URL doesn't update to the correct article, so users can't copy the link or bookmark the article; second, the previously viewed random articles don't show up in the browser's back button's history (i.e. pressing "Back" brings the user back to the page they were on before clicking "random" for the first time).
The second approach substituted render with redirect_to:
def random
offset = rand(Article.published.size)
#article = Article.published.offset(offset).first
redirect_to #article
end
This fixes the first issue - it's a redirect, so the browser is actually redirected to the appropriate URL for the randomly selected article (so it's available for copying/bookmarking). However, the problem with the Back button still remains. Moreover, it feels a bit wrong to co-opt an HTTP Redirect for something like this.
What would be the best way to go about serving random articles, while displaying the correct URL for the article and also maintaining a browser history chain?
Why don't you make actually a LINK for random article?
Helper:
def random_article_link
random_article = Article.find_by_sql("SELECT 1 FROM articles ORDER BY RANDOM() LIMIT 1") # for MySql RAND()
link_to "Random Article", random_article
end
In your approach you can not change URL string on a fly on a controller level. Only on routing level using Constraints.
Umm what about
#article = Article.find(rand(Article.count))
redirect_to #article
I wanted to do something similar and was also having trouble. Here is the solution I came up with:
In your controller you don't need a new page articles/random. I'm assuming you want the link on your article page which would be the show action in your controller.
def show
#article = Article.find(params[:id]
#random_article = Article.order('random()').first
end
Then in your view show.html.erb file
<%= link_to "Random Article", #random_article %>
Expanding on Chris's comment a bit - it's possible create a link within the view which will generate a random id for the next link (if you have some reason to do this...that's up to you).
<%= link_to 'Show me a random thing', thing_path(rand(1..4)) %>
You could drop this wherever and when clicked it'll simply route to a random page. Of course you'll want to change the number to reflect actual :id 's

Rails 3 increment the value of my counter with link_to

Good morning,
In my Ruby On Rails application I am trying to build a stat counter for wins and for losses.
These stats are displayed in the sidebar of my page, which is always visible. For these values i use a model named Stats. The stats should be available global, since I want to display them on every side so they were put into application_helper.rb. I also have two actions, that should be called if you click on the add win / or add loss link, which is only displayed for admins. After clicking the link you get redirected to the main page. At the moment, both counters increase each time someone enters the site or refreshes it. Can you please help me, I am looking to solve this problem now for several hours and i still don't get it.
My application_helper
module ApplicationHelper
def stats
#stats = Stats.find(1)
end
def addwin
#stats = Stats.find(1)
#stats.update_attribute(:wins, #stats.wins+1)
end
def addloss
#stats = Stats.find(1)
#stats.update_attribute(:loss, #stats.loss+1)
end
end
An extract of my application.html.erb file, where the sidebar is located:
<tr><td>Dotards StatsTracker</td></tr>
<tr><td>Wins: </td><td style="color:green"><%= stats.wins%></td></tr>
<tr><td>Losses: </td><td style="color:red"><%= stats.loss%></td></tr>
<hr>
</table >
<%if user_signed_in? && current_user.admin? %>
<small><%= link_to "Add Win", news_index_path(addwin) %>|<%= link_to "Add Loss", news_index_path(addloss)%></small>
<% end %>
Would be awesome if someone could help me, because I don't know what else i can do.
Thank you very much in advance.
news_index_path(addwin)
calls the add win method directly on generation of the page, that's why the counter is increased.
move the two methods to an appropriate controller (maybe news_controller?) and create routes in config/routes.rb like this:
match '/addwin', to: 'news#addwin'
then you get path helpers which you can use like this:
link_to "Add Win" addwin_path
run rake routes to see the helper names generated.
addition: I would also consider your Singleton Stat into a model, and define methods there which are just used by the controller.

Making sure I got the idea . - Ruby on rails login system

I am currently reading a book, and learning ruby on rails. (Agile Web Development with Rails 4th Edition) .In the book it says how to write a simple product list and display it. I am modifying this idea, to create a user login system.
I am only working on the views now.
So I just need to make sure that my idea is right. My idea is:
The show.html file from the USER model, show data for one user. (given of course its ID)
for example : http://localhost:3000/users/980190974 will give me the html page for the current user right? Now I can allow the user to edit his/her information by using the
<%= link_to 'Edit', edit_user_path(#user) %> link or restrict him from viewing other users by removing the <%= link_to 'Back', users_path %> << that lists all the users from the database. But before the user views his/her details he must login, using his email and password. So by making an html page, that takes 2 strings (username, and password) searches my mySQL database and return the user ID , that I then use to "render" the user's HTML page.
Is my way of thinking correct? Or am I just completely irrelevant on how Ruby on Rails works? O_o
You are heading in the right direction. One thing to point out is that simply removing the link <%= link_to 'Back', users_path %> is not sufficient to avoid other users from accessing the /users path. In the (user) controller you have to use something like:
class UserController < ApplicationController
def index
unless current_user.is_admin
redirect_to user_path(current_user)
return
end
... rest of code here
end
end
where current_user could be a method returning the user object

Newbie rails database relation question

I have two models Station and Broadcast. Broadcast belongs_to Station and has a station_id column.
I don't know how to make new method in BroadcastController to expect the station_id value and how to create a new Broadcast with right station_id in it.
I'd recommend against using a link_to here. Using a 'get' action to change/add data on the server is generally frowned upon. Also, I wouldn't use the 'new' action, as it's used by convention in a Rails Restful route to present a form, not actually persist data. All that said, the simple answer to your question is to pass a value through the link_to helper like so:
link_to 'Add broadcast', new_broadcast_path(:station_id => station.id)
...and your 'new' method on BroadcastsController would do:
def new
#broadcast = BroadCast.new(:station_id => params[:station_id])
if #broadcast.save
flash[:notice] = "New broadcast created!"
redirect :back # or whatever
else
# etc.
end
end
But, again, this is not the right way to do this. What you probably want to do is stay within the Rails (and web) conventions and use a form to create the new broadcast record by way of the 'create' action on the controller. You might place this form next to your stations on the index view which presents a button that points to the correct 'create' action of BroadcastsController, and uses a hidden_field to set the station_id. Something like (EDIT: better use of hidden_field):
<% form_for :broadcast do |f| %>
<%= f.hidden_field :station_id, :value=> station.id %>
<%= submit_tag 'Add broadcast' %>
<% end %>
Assuming you've set a restful route in routes.rb for broadcast like:
map.resources :broadcasts
...then your form will automatically point to a 'create' action on BroadcastsController, which you should write to look something like:
def create
#broadcast = BroadCast.new(params[:broadcast])
if #broadcast.save
# etc., etc.
end
The Rails Guides are a good place to get more comfortable with the Rails controller actions. I'd also spend some time looking over the RESTful actions and how they're used in Rails as well.
You actually do specify a station_id for your Broadcast model, such as
script/generate scaffold broadcast name:string station_id:integer ...
so when you add a broadcast record, it will ask you for a station_id.

Resources