Doing multiplication on my Rails App with user input - ruby-on-rails

I'm just starting to learn Rails so please forgive the dumb question. In my web app, I was able to set up a working model, form, and view. A user is able to input their decimal answer and it shows on the web page perfectly. However, I want to multiple whatever the user inputs by 10. So if the user inputs 2 I want it to show 20. Any help that you guys can give me would be so amazing. I have listed below all my relevant code. Thank you again :)
Rails Controller
class Ratings1sController < ApplicationController
before_action :authenticate_user!
def create
#post = Post.find(params[:post_id])
#ratings1 = Ratings1.create(params[:ratings1].permit(:content))
#ratings1.user_id = current_user.id
#ratings1.post_id = #post.id
if #ratings1.save
redirect_to post_path(#post)
else
render 'new'
end
end
end
Rails Model
class Ratings1 < ActiveRecord::Base
belongs_to :post
belongs_to :user
end
_Form.html
<%= simple_form_for ([#post, #post.ratings1s.build]) do |f| %>
<%= f.input_field :content %>

Assuming content is your rating that you wish to multiply simply do:
#ratings1 = Ratings1.create(params[:ratings1].permit(:content))
#ratings.content *= 10
Incidentally you are saving your Ratings1 model twice - once when creating it and then when you call save on it. It would be better to build it then save it - you may wish to add validation for presence of user for example at some point and then calling create would fail.
#ratings1 = Ratings1.build(params[:ratings1].permit(:content))

Related

RoR: Using a method defined in user model in a different controller

I'm new in RoR and hoping you experts can help me on this. Apologies in advance if my question sounds weird or stupid. Let me know if you need more clarification, thanks so much in advance.
I have a boolean method called is_pollie (set default to false) in the user model which I want to change it to true once a user completed a form in a different controller called profiles_controller.rb.
Now, I have a user model with a defined method:
class User < ApplicationRecord
has_many :profiles
def self.is_pollie?
is_pollie
end
And in a different controller called profiles_controller.rb:
class ProfilesController < ApplicationController
before_action :authenticate_user!, except: [:show]
def create
#pollie = User.is_pollie?
#profile = current_user.profiles.build(profile_params)
if #profile.save
# what should I put here if I want the is_pollie? to change to true upon
a user click the save button on the form?
redirect_to basic_profile_path(#profile)
else
flash[:alert] = "Oh no, something went wrong."
render :new
end
end
In the page where the form is:
<%= form_for #profile do |f| %>
<div class="form-group">
<label>Displayed name:</label>
<%= f.text_field :display_name,class: "form-control"%>
</div>
<%= f.submit "Save", class: "btn-submit" %>
<% end %>
Hope you understand my question and are able to help. Thanks very much again.
You can try:
current_user.update(is_pollie: true)
BTW, a couple of other points...
This:
class User < ApplicationRecord
has_many :profiles
def self.is_pollie?
is_pollie
end
end
doesn't make any sense because self makes is_pollie? a class method. But, is_pollie is an instance value.
Also, you don't even need is_pollie? because you can use do current_user.is_pollie which will return true of false.
Finally, you're not using #pollie = User.is_pollie? anywhere, so why do it?
Use current_user.update_column(:is_pollie, true)
update method will trigger the call_backs, it's recommended to use update_column for updating a selected attribute.
For multiples you can use update_columns(attributes1: value, attributes2: value)

Ruby on Rails: Saving user id manually? Or is there a better method?

Should I manually add a user_id into the hidden form? Or is there a better way?
I have models:
class Project < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_many :projects
end
In my view:
<%= simple_form_for #project do |f| %>
<%= f.hidden_field :value => current_user.id %>
<% end %>
Or is there another way to do this? I thought if the model was associated with each other, it would automatically add the user_id into the database for projects?
Thanks!
Edit: Converted all trip to projects, so others needing help knows
Nope. That shouldn't be in form, because of everybody can change hidden value at the form.
Assuming you are using strong parameters
def create
#project = Project.new(project_params)
#project.save # or something else
end
def project_params
params.require(:project).permit(something_permitted_here)
.merge(user_id: current_user.id)
end
If you put user id on hidden form it will cause a breach in security. Because using browser tools a hacker can change the user_id and can inject information for other user. A better way is to put it into controller.
Inside controller, you can do so:
def create
#project.user = current_user
#project.save
#...
end
This way you protect yourself against someone manually changing the user_id in html.
Use the association to your advantage:
def create
#project = current_user.projects.build(project_params)
if #project.save
# ...etc
end

Rails has_one build_association deletes record before save

So this has been asked previously, but with no satisfying answers.
Consider two models, User, and Subscription associated as such:
class User < ActiveRecord::Base
has_one :subscription, dependent: :destroy
end
class Subscription < ActiveRecord::Base
belongs_to :user
end
Inside of SubscriptionsController, I have a new action that looks like this
def new
user = User.find(params[:user_id])
#subscription = user.build_subscription
end
Given that a subscription already exists for a user record, I'm faced with the following problem:
user.build_subscription is destructive, meaning that simply visiting the new action actually destroys the association, thereby losing the current subscription record.
Now, I could simply check for the subscription's existence and redirect like this:
def new
user = User.find(params[:user_id])
if user.subscription.present?
redirect_to root_path
else
#subscription = user.build_subscription
end
end
But that doesn't seem all that elegant.
Here's my question
Shouldn't just building a tentative record for an association not be destructive?
Doesn't that violate RESTful routing, since new is accessed with a GET request, which should not modify the record?
Or perhaps I'm doing something wrong. Should I be building the record differently? Maybe via Subscription.new(user_id: user.id)? Doesn't seem to make much sense.
Would much appreciate an explanation as to why this is implemented this way and how you'd go about dealing with this.
Thanks!
It depends on what you want to do
Thoughts
From what you've posted, it seems the RESTful structure is still valid for you. You're calling the new action on the subscriptions controller, which, by definition, means you're making a new subscription (not loading a current subscription)?
You have to remember that Rails is basically just a group of Ruby classes, with instance methods. This means that you don't need to keep entirely to the RESTful structure if it doesn't suit
I think your issue is how you're handling the request / action:
def new
user = User.find(params[:user_id])
#subscription = user.build_subscription
end
#subscription is building a new ActiveRecord object, but doesn't need to be that way. You presumably want to change the subscription (if they have one), or create an association if they don't
Logic
Perhaps you could include some logic in an instance method:
#app/models/user.rb
Class User < ActiveRecord::Base
def build
if subscription
subscription
else
build_subscription
end
end
end
#app/controllers/subscriptions_controller.rb
def new
user = User.find(params[:user_id])
#subscription = user.build
end
This will give you a populated ActiveRecord, either with data from the subscription, or the new ActiveRecord object.
View
In the view, you can then use a select box like this:
#app/views/subscriptions/new.html.erb
<%= form_for #subscription do |f| %>
<%= "User #{params[:user_id]}'s subscription: %>
<%= f.collection_select :subscription_id, Subscription.all,:id , :name %>
<% end %>
They are my thoughts, but I think you want to do something else with your code. If you give me some comments on this answer, we can fix it accordingly!
I also always thought, that a user.build_foobar would only be written to the db, if afterwards a user.save is called. One question: After calling user.build_subscription, is the old subscription still in the database?
What is the output user.persisted? and user.subscription.persisted?, after calling user.build_subscription?
Your method to check if a subscription is present, is IMHO absolutely ok and valid.
I came across this today and agree that deleting something from the db when you call build is a very unexpected outcome (caused us to have bad data). As you suggested, you can work around if very easily by simply doing Subscription.new(user: user). I personally don't think that is much less readable then user.build_subscription.
As of 2018 Richard Peck's solution worked for me:
#app/models/user.rb
Class User < ActiveRecord::Base
def build_a_subscription
if subscription
subscription
else
build_subscription
end
end
end
My issue was that a user controller didn't have a new method, because users came from an api or from a seed file.
So mine looked like:
#app/controllers/subscriptions_controller.rb
def update
#user = User.find(params[:id])
#user.build_a_subscription
if #user.update_attributes(user_params)
redirect_to edit_user_path(#user), notice: 'User was successfully updated.'
else
render :edit
end
end
And I was finally able to have the correct singular version of subscriptions in my fields_for, so :subscription verses :subscriptions
#app/views
<%= f.fields_for :subscription do |sub| %>
<%= render 'subscription', f: sub %>
<% end %>
Before I could only get the fields_for to show in the view if I made subscriptions plural. And then it wouldn't save.
But now, everything works.

Trying to link to specfic new comic review page

I'm trying to get a link on an articles show page so that when a user clicks write new review it takes them to the link
/comic_reviews/'the article they want to comment on'/reviews/new
where they will be directed to the new reviews page
how can i accomplish this with
In your routes file you would specify a route like this
match '/comic_reviews/:comic_name/reviews/new' => 'reviews#new', via: :get
Then in your reviews controller you would need something like this
reviews_controller.rb
class ReviewsController < ApplicationController
def new
#comic = Commic.find_by_name(params[:comic_name])
if #comic
#review = #comic.reviews.build
render 'new'
else
#Render some error page since comic was not found
end
end
end
You will then have access to #comic and #review in your reviews/new view so you could build a form that just makes a post to create a review and allows you to store it. This should get you going.
Edit
In your new view you'd need to have a form that looks something like this
<%= form_for #review do |f| %>
<%= f.label :some_attribute %>:
<%= f.text_field :some_attribute %><br />
<%= f.submit %>
<% end %>
This will be expecting you have a route to create a review in your routes file and an action in your ReviewsController.
If you are struggling with such topics I suggest you read over this excellent tutorial
http://ruby.railstutorial.org/ruby-on-rails-tutorial-book
Or just read through the documentation for Rails API which will give you pretty accurate examples.
You can do this via routes
resources :comic_reviews do
resources :reviews
#probably_some_other_route_here
end
And with restful pattern it will be easy to achieve whatever you want
controller
Someclass < Someotherclass
#some your code
def new
#instance_var = Your_model.new
end
def create
#instance_var = Your_model.new(params[:some_name_here])
if #instance_var.save
redirect_to somewhere
else
render 'new'
end
end
end
Also you'll need form, but i dont think that will cause any troubles

Any way around putting hidden field in forms for resources with belongs_to association

I'm learning Rails by writing simple TODO tasks aplication.
Two models are:
class List < ActiveRecord::Base
has_many :tasks, :dependent => :destroy
# ...
end
class Task < ActiveRecord::Base
belongs_to :list
# ...
end
Tasks are routed as a nested resources under Lists. So when a new Task is created by user a POST message is sent to /lists/:list_id/tasks. So far in Tasks#new view's form there is
f.hidden_field :list_id, :value => params[:list_id]
but it's a terrible solution, because anyone can change value of that hidden field.
What is the convention here? Should I put something like
#task.list_id = params[:list_id]
in Tasks#create action and get rid of the hidden field, or maybe
#task = List.find(params[:list_id]).tasks.new(params[:task])
if #task.save
# ...
end
or there is even a better way I don't know about?
Edit:
Yeah, well there was similar question and its answer is pretty much covering my question. If you have different one please post it.
You're right - that would be horrible. No need for hidden fields. Something like the following.
In your TasksController:
def new
#list = List.find(params[:list_id])
#task = #list.tasks.build
end
def create
#list = List.find(params[:list_id])
#task = #list.tasks.new(params[:task])
# etc
end
In your Task#new view:
<% form_for [#list, #task] ... %>
...
<% end %>
If you are concerned about security (like one user creating to-dos in another user's lists - and I assume you are, because you didn't want to use a hidden field stating that anyone can change value of that hidden field), I don't see how #bjg solution is any better then yours, since you're getting #list from params anyways, and anybody can manipulate params on the browser (changing the URL to post to is as easy as changing the hidden field value).
One common way to solve this without having to implement a more complex permission solution is to just use current_user association's, like this:
def new
#list = current_user.lists.where(id: params[:list_id]).take
#task = #list.tasks.build
end
def create
#list = current_user.lists.where(id: params[:list_id]).take
#task = #list.tasks.new(params[:task])
# etc
end
This way, no matter what is the value of params[:list_id] (it could have been manipulated by the user), you can rest assured the #task will end up on that user's account, since #list will only find a record that belongs to current_user.
You can evolve this in a real-world app by returning an error message if #list is not found.

Resources