Active Record: Adding "visit" counter on the model - ruby-on-rails

I currently have a Subscriber model that takes in a "phone_number" and a "visit" attribute that is an integer. I want to set up a "check in" view form that will have a subscriber type in their phone_number and it will say if phone_number exists? add 1 to the visit attribute. So it will run a sql query and see if that number is in the database.
To be more clear I have to break the REST actions because the create action is already taken for the new subscribers. I'm pretty new to rails and I'm having a super hard time figuring this feature out. I'm curious if this is possible and how I should go about implementing this?
Here is my controller at the moment:
class SubscribersController < ApplicationController
def index
#subscriber = Subscriber.all
end
def new
#subscriber = Subscriber.new
end
def create
#subscriber = Subscriber.create(subscriber_params)
if #subscriber.save
flash[:success] = "Subscriber Has Been successfully Created"
redirect_to new_subscriber_path(:subscriber)
else
render "new"
end
end
def visit
end
private
def subscriber_params
params.require(:subscriber).permit(:first_name, :last_name, :email, :phone_number)
end
end

Something along those lines?
def visit
subscriber = Subscriber.find_by_phone_number(params[:phone_number])
if subscriber
subscriber.visit += 1
subscriber.save
end
end
Make sure that the default value (via DB/Migration) for visit is set to 0.
You don't need to break REST style controller though. You can create another controller which does that check. For example something like:
class Subscriber::VisitController < ApplicationController
def create
# code here
end
end

Related

How can I restrict someone to access the data he hasn't created? (ruby)

I'm using Ruby on rails with devise.
I generated a scaffold.
tasks_controller.rb:
def index
#tasks= current_user.tasks
end
By using this, I'm able to show the people only what they have created,
but other users can see the data of tasks that they have not entered, like:
GET /tasks/1
GET /tasks/2
Although the tasks with id 1 is not created by the current_user, the user can access that.
Yes you can restrict this using filters e.g.
class TasksController < ApplicationController
before_filter :user_can_view_task, only: :show
def show
#you do not need to set #task here as it will be set by the filter method
end
private
def user_can_view_task
#task = Task.find(params[:id])
unless #task.user_id == current_user.id
flash[:notice] = "You may only view Tasks you have created."
redirect_to(tasks_path)
end
end
end
Every time a User hits the route for the show view it will execute this method prior to rendering the view. (Thus "before")
This method will look up the task and determine if the current_user created the task (assuming associations between User and Task). If the user is not the task creator it will redirect them back to the index view and inform them that they can only view tasks they have created rather than allowing them to access the show.
There is Pundit gem(https://github.com/elabs/pundit) for these cases. Your code will look:
class TaskPolicy
attr_reader :user, :task
def initialize(user, task)
#user = user
#task = task
end
def show?
user.tasks.include? task
end
end
And your controller's action:
def show
#task = Task.find(params[:id])
authorize #task
...
end
This action will raise Pundit::NotAuthorizedError if current user hasn't certaint task
Try:
def show
#task = current_user.tasks.find(params[:id])
# or with error handling
# begin
# #task = current_user.tasks.find(params[:id])
# rescue
# redirect_to [:tasks], flash: { error: 'not authorized' }
# return
# end
end

In a Ruby Rails App, I am getting "project/app/controllers/workouts_controller.rb:43: syntax error, unexpected keyword_end, expecting $end

So I've looked all through Stack Overflow trying to find a reason why I would be hitting a parsing error. This is a relatively new Rails app, but I don't think I've missed any configuration as I've been able to deploy on Heroku successfully.
From previous Stackoverflow posts it would seem that I have an extra end since Rails is complaining that it is finding an end when it is expecting the end of the file. I tried taking out the last end (which is the closer for the controller class itself) as well as commenting various parts of the code in and out and it just simply isn't working. Does anyone have any ideas/advice on how to circumvent/solve this issue?
Here is my controller file
Class WorkoutsController < ApplicationController
def show
id = params[:id] # retrieve workout ID from URI route
#workout = Workout.find(id) # look up workout by unique ID
# will render app/views/workouts/show.<extension> by default
end
def index
#all_workouts = Workout.all_workouts
#workouts = Workout.all
end
def new
# default: render 'new' template
end
def create
#workout = Workout.create!(params[:workout])
flash[:notice] = "#{#workout.title} was successfully created."
redirect_to workouts_path
end
def edit
#workout = Workout.find params[:id]
end
def update
#workout = Workout.find params[:id]
#workout.update_attributes!(params[:workout])
flash[:notice] = "#{#workout.title} was successfully updated."
redirect_to workouts_path(#workout)
end
def destroy
#workout = Workout.find(params[:id])
#workout.destroy
flash[:notice] = "Workout '#{#workout.title}' destroyed."
redirect_to workouts_path
end
end
This probably isn't needed, but here is my model file:
Class Workout < ActiveRecord::Base
attr_accessible :title, :time, :creator, :exercise_list
def self.all_exercises
%w(push-ups pull-ups crunches rest)
end
end
Replace
Class WorkoutsController < ApplicationController
with
class WorkoutsController < ApplicationController
Same for your model. class keyword should be used in lower register.
I assume you misprinted it, thats why parser sees extra end keyword

Ruby on Rails CRUD

I am building an application that allows users to create a trip. However, for some reason I am not sure if I am truly utilizing the power of rails or am I being redundant in my code. In the following example you will see a trip controller where a trip can be created and then displayed. Everything works, I just want to make sure I am going about it in the most minimal fashion.
class TripsController < ApplicationController
def new
#user = User.find(session[:id])
#trip = Trip.new
end
def create
#trip = Trip.create(trip_params)
#user = User.find(session[:id])
redirect_to user_trip_path(#user.id, #trip.id)
end
def show
#trip = Trip.find(params[:id])
end
private
def trip_params
params.require(:trip).permit(:where, :when, :price_per_person)
end
end
To tighten it up, "scope the trip to the user".
class TripsController < ApplicationController
before_filter :find_user
def new
#trip = #user.trips.build #assuming a User has many trips
end
def create
#trip = #user.trips.create(trip_params) #you may want to add an if else here to catch bad trips
redirect_to user_trip_path(#user.id, #trip.id)
end
def show
#trip = #user.trips.find(params[:id])
end
private
def trip_params
params.require(:trip).permit(:where, :when, :price_per_person)
end
def find_user
#user = User.find(session[:id]) # or current_user if you are using popular authentication gems
end
end
It's about readability too, not just less lines.

how to make clean code in controller rails

how to make this code clean in rails?
profiles_controller.rb :
class ProfilesController < ApplicationController
before_action :find_profile, only: [:edit, :update]
def index
#profiles = Profile.all
end
def new
#profile = Profile.new
end
def create
profile, message = Profile.create_object(params["profile"], current_user)
flash[:notice] = message
redirect_to profile_url
end
def edit
end
def update
profile, message = #profile.update_object(params["profile"])
flash[:notice] = message
redirect_to profile_url
end
private
def find_profile
#profile = Profile.friendly.find(params["id"])
end
end
i look flash[:notice] and redirct_to profile_url is duplicate in my code, how to make the code to clean and dry?
How about moving the repetitive code to a separate method and call that method inside the actions.
def flash_redirect # you can come up with a better name
flash[:notice] = message
redirect_to profile_url
end
then in update action:
def update
profile, message = #profile.update_object(params["profile"])
flash_redirect
end
do the same thing for create action
UPDATE:
in case you are wondering about usingafter_action, you can't use it to redirect as the call-back is appended after the action runs out its course. see this answer
Take a look at Inherited Resources. It's based on the fact that many CRUD controllers in Rails have the exact same general structure. It does most of the work for you and is fully customisable in case things are done a little different in your controllers.
Using this gem, your code would look like this:
class ProfilesController < InheritedResources::Base
def create
redirect_to_profile(*Profile.create_object(params[:profile], current_user))
end
def update
redirect_to_profile(*#profile.update_object(params[:profile]))
end
private
def redirect_to_profile(profile, message)
redirect_to(profile_url, notice: message)
end
def resource
#profile ||= Profile.friendly.find(params[:id])
end
end
The create and update methods return multiple values, so I used the splat operator to DRY this up.
create_object and update_object don't follow the Rails default, so we need to implement those actions for Inherited Resources instead. Currently they don't seem to be handling validation errors. If you can, refactor them to use ActiveRecord's save and update, it would make everything even easier and DRYer.

Rails redirect based on user type

I'm learning Rails by building a shop application and I'm having a bit of trouble with redirects. I have 3 roles in the application:
Buyer
Seller
Administrator
Depending on which type they are logged in as then I would like to redirect to a different page/action but still show the same URL for each (http://.../my-account).
I don't like having to render partials in the same view, it just seems messy, is there another way to achieve this?
The only way I can think of is to have multiple actions (e.g. buyer, seller, administrator) in the accounts controller but that means the paths will look like http://.../my-account/buyer or http://.../my-account/seller etc.
Many thanks,
Roger
I've put my code below:
models/user.rb
class User < ActiveRecord::Base
def buyer?
return type == 'buyer'
end
def seller?
return type == 'seller'
end
def administrator?
return type == 'administrator'
end
...
end
controllers/accounts_controller.rb
class AccountsController < ApplicationController
def show
end
end
controllers/user_sessions_controller.rb
class UserSessionsController < ApplicationController
def new
#user_session = UserSession.new
end
def create
#user_session = UserSession.new(params[:user_session])
if #user_session.save
if session[:return_to].nil?
# I'm not sure how to handle this part if I want the URL to be the same for each.
redirect_to(account_path)
else
redirect_to(session[:return_to])
end
else
#user_session.errors.clear # Give as little feedback as possible to improve security.
flash[:notice] = 'We didn\'t recognise the email address or password you entered, please try again.'
render(:action => :new)
end
end
def destroy
current_user_session.destroy
current_basket.destroy
redirect_to(root_url, :notice => 'Sign out successful!')
end
end
config/routes.rb
match 'my-account' => 'accounts#show'
Many thanks,
Roger
In UserSessionsController#create (i.e.: the login method) you could continue to redirect to the account path (assuming that goes to AccountsController#show) and then render different views according to the role. I.e.: something like this:
class AccountsController < ApplicationController
def show
if current_user.buyer?
render 'accounts/buyer'
elsif current_user.seller?
render 'accounts/seller'
elsif current_user.administrator?
render 'accounts/administrator
end
end
end
Better yet, you could do this by convention...
class AccountsController < ApplicationController
def show
render "accounts/#{current_user.type}"
end
end
If I understand you question correctly, then the solution is simple.
You can just call the method you want inside your controller. I do this in my project:
def create
create_or_update
end
def update
create_or_update
end
def create_or_update
...
end
In your case it should be:
def action
if administrator? then
admin_action
elsif buyer? then
buyer_action
elseif seller? then
seller_action
else
some_error_action
end
end
You should probably explicitly call "render" with an action name in each of those actions, though.

Resources