I'm trying to implement my first decorator for my view. I'm running into an issue though. When I try to render the view I get the title error undefined method "decorate" for #<Assignment::ActiveRecord_AssociationRelation. I'm not sure what I'm supposed to do? Any help would be great. Here is my code.
Assignment decorator:
class AssignmentDecorator < Draper::Decorator
delegate_all
decorates :assignment
def status
if finished
"Finished"
else
"Waiting"
end
end
end
Pages Controller:
class PagesController < ApplicationController
before_action :verify_account!, only: :dashboard
def home; end
def dashboard
#assignments = current_account.assignments.all.decorate
#invitation = Invitation.new
end
end
View:
<% #assignments.each do |assignment| %>
<tr class="assignment-rows">
<td><%= link_to assignment.name, account_assignment_path(assignment) %></td>
<td><%= assignment.assigned_workers %></td>
<td><%= assignment.status %></td>
</tr>
<% end %>
Error message:
Draper Docs:
https://github.com/drapergem/draper
If you read the docs ;) and look at the decorate_collection section:
https://github.com/drapergem/draper#collections
which states:
Note: In Rails 3, the .all method returns an array and not a query. Thus you cannot use the technique of Article.all.decorate in Rails 3. In Rails 4, .all returns a query so this techique would work fine.
So if you're using Rails 3 - you need to use decorate_collection... or you could (and probably should) upgrade to rails 4
Before
#assignments = current_account.assignments.all.decorate
After
#assignments = current_account.assignments.decorate.all
Related
I like to think I'm not a complete newbie to rails, but I cannot get a simple Show action to work. I'm baffled. Ruby 2.2.2p95, also tried it on my production build, Ruby 2.0.0p643, both Rails 4.2.1.
The model is Players. Players have 2 params, :name and :position. When I index Players, it works fine. But when I try and view the show.html.erb, I get the classic error: undefined method `name' for nil:NilClass.
The Players model:
class Player < ActiveRecord::Base
has_one :team, through: :ownership
has_one :ownership
validates :name, presence: true
validates :position, presence: true
def last_name
self.name.split(' ')[1]
end
end
The method:
def show
#player = Player.find(params[:id])
end
For troubleshooting, I've reduced the show.html.erb to this:
<%= render 'welcome/menu' %>
<%= #player.name %>
A rake routes shows:
player GET /players/:id(.:format) players#show
If I try and view the player with id 269, this what the log shows
Started GET "/players/269" for ::1 at 2015-09-08 21:58:12 -0400
Processing by PlayersController#show as HTML
Parameters: {"id"=>"269"}
Rendered welcome/_menu.html.erb (0.7ms)
Rendered players/show.html.erb within layouts/application (5.7ms)
Completed 500 Internal Server Error in 11ms (ActiveRecord: 0.0ms)
ActionView::Template::Error (undefined method `name' for nil:NilClass):
2:
3: <%= render 'welcome/menu' %>
4:
5: <%= #player.name %>
6:
app/views/players/show.html.erb:5:in `_app_views_players_show_html_erb___175427692043794239_70253361597080'
What in the world am I missing here?
EDIT:
This is the important part of the index.html.erb view. It works fine:
<% #players.each do |player| %>
<tr>
<td></td>
<td><%= link_to player.name, player %></td>
<td><%= player.position %></td>
<td><%= player.id %>
Players Controller:
class PlayersController < ApplicationController
def index
#players = Player.all.sort_by{ |x| [x.position, x.last_name] }
end
def not_picked
#players = Player.all.select { |m| m.team == nil}.sort_by{ |x| [x.position, x.last_name] }
end
end
def show
#player = Player.find(params[:id])
end
private
def player_params
params.require(:player).permit(:name, :position)
end
You have an extra end after not_picked method, is that a typo? If not, it literally ends the controller and your show method is just not called at all.
Because find method will return ActiveRecord::RecordNotFound when record not found. So i guess the Player record is exist in your database.
I suggest you p #player after #player = Player.find(params[:id]), then do the same thing in the first line at show.html.erb to show what the #player is.
Maybe there is something change your #player to a nil.
You code looks fine, but my guess is something in your code making the #player variable nil
I would suggest the followings, steps with pry debugger
1 - a debugger after #player in controller
def show
#player = Player.find(params[:id])
binding.pry
end
check your code to see , if you get a #player object
2 - add a debugger to show view
<% binding.pry %>
<%= #player.name %>
and see if you still have the #player, by this you can understand when u r missing the value and work on that.
My guess is, some filter in controller might be wiping out your variable.
I have a review model that I would like to lock out the edit and destroy button after 30 minutes and if only the correct user. Right now I just have an if statement around the button but you can still get to it by putting in the full URL. Where/ how would I go about doing this? I am new to Ruby on Rails and any help is useful. Thanks in advance!
Edit: All I have is below in the index but the problem is that I can still get to it through the URL and I don't know how to make those inaccessible after that.
<% if signed_in? %>
<% if current_user.id = review.user_id %>
<% if !review.has_time_passed? %>
<td><%= link_to 'Edit', edit_property_review_path(review.property, review) %></td>
<% if !review.comments.any? %>
<td><%= link_to 'Destroy', [review.property, review], :confirm => 'Are you sure?', :method => :delete %></td>
<% end %>
<% end %>
<% end %>
<% end %>
My has_time_passed method:
def has_time_passed?
created_at < 30.minutes.ago
end
There's at least 2 pieces to what I think you're describing:
You need to ensure the view template hides any edit and destroy links after 30 minutes.
You need to add logic to the relevant controller actions (edit and destroy) to ensure they'll refuse to make any changes after 30 minutes.
As far as the view logic goes, it sounds like you're pretty close and this shouldn't be too difficult. One if statement phrasing that comes to mind (similar to what you pasted above, but a tiny bit simpler):
if review.created_at <= 30.minutes.ago
Then, in the controller, you'll also want to ensure the action only makes changes within the time limit. So for example you might use the same if statement:
def edit
if review.created_at <= 30.minutes.ago
redirect_to some_other_path, alert: "Sorry bro, this review is too old to be edited."
else
# do stuff
end
end
That's just a very rough-draft example. Once you have everything working, then extract the logic into a method on the model (to reduce redundancy) and so forth.
Good luck!
My recommendation would be to introduce an authorization framework like Pundit or Cancan.
That way you separate the logic that interacts with the model from the controller, for which it's a good idea to keep it as minimalistic as possible.
Both Pundit and CanCan have great tutorials that show how to achieve similar scenarios to what you are trying to achieve.
In Pundit, for example, your policy would like somewhat like this:
class MyModelPolicy
attr_reader :user, :my_model
def initialize(user, model)
#user = user
#my_model = model
end
def destroy?
user == my_model.user && my_model.created_at < 30.minutes.ago
end
end
I think you want something at the controller level.
# app/controllers/reviews_controller.rb
class ReviewsController
before_action :validate_change, only: [:edit, :update, :destroy]
def edit
# edit stuff
end
def destroy
# destroy stuff
end
private
def validate_change
if #review.created_at < 30.minutes.ago
redirect_to request.env['HTTP_REFERER']
end
end
end
i am trying to create a list of leads and create a link to show each one details. so i create a controller like that :
class LeadsController < ApplicationController
def index
#leads = Leads.all
end
def show
#leads = Leads.find(params[:id])
end
def delete
end
private
def lead_params
params.require(:lead).permit(:name,:familyname,:email,:mmobile)
end
end
and a route like below :
Rails.application.routes.draw do
root 'pages#home'
get 'pages/home' => 'pages#home'
get 'leads/index'
resource :leads
get 'leads/:id/show'=> 'leads#show',:as => :leads_show end
but there are one problem and one question :
the question is when i write { Leads.find(params[:id]) } the editor doesn't recognize params[:id] . why?
and when i want to see http://127.0.0.1:3000/leads/index
i see the error like that :
undefined method `lead_path' for #<#:0x36da5f8>
Extracted source (around line #8):
<td><%= lead.familyname %></td>
6 <td><%= lead.mobile %></td>
7 <td><%= lead.email %></td>
8 <td><%= link_to 'show' , lead %></td>
</tr>
<% end %>
<p>Find me in app/views/leads/index.html.erb</p>
It should be
#lead = Lead.all
#lead = Lead.find(params[:id])
Model Name should be always singular, and ccontroller name will be plural
It should be resources :leads, and it will create all the seven actions like show, update, create..etc
And you don't have to call http://127.0.0.1:3000/leads/index this http://127.0.0.1:3000/leads, this will call index page
Can you post your params, so that we can see why you are not getting params[:id]
Here's the problem I've been working on for the last few days:
I have task and completed_task models:
class Task < ActiveRecord::Base
belongs_to :user
has_many :completed_tasks
end
class CompletedTask < ActiveRecord::Base
belongs_to :task
end
I have a form that says:
<% #tasks.each do |task| %>
<td><%= link_to task.description, task_path(task) %></td>
<td><%= task.user.first_name %></td>
<td><%= task.value %></td>
<td><%= task.task_type %></td>
<td><%= task.frequency %></td
<td><%= task.active %></td>
<td><%= task.due_by %></td>
<%= button_to "Task Completed", new_completed_task_path(:completed =>[:task_id =>
task.id, :task_value => task.value}) %>
<%end%>
In my completed_task_controller, I have:
def new
#completed_task = CompletedTask.new(params[:completed])
end
def create
#completed_task = CompletedTask.new(completed_task_params)
end
When I click on the button to complete a task, I want it to create a record in the completed_tasks table but the params from the parent table are not flowing from the new action to the create action. I'm guessing it has to do with the strong parameters which I have set as:
private
def set_completed_task
#completed_task = CompletedTask.find(params[:id])
end
def completed_task_params
params.require(:completed_task).permit(:task_id, :task_value)
end
Here is the error I am getting:
ActiveModel::ForbiddenAttributesError
Extracted source (around line #19):
def new
#completed_task = CompletedTask.new(params[:completed])
end
Any ideas???
When you call the new method, at that point nothing has been returned from the form (it hasn't even been dsiplayed, yet)
You should just do
def new
#completed_task = CompletedTask.new
end
When the form is returned, then the create method would typically do
def create
#completed_task = CompletedTask.new(completed_task_params)
if #completed_task.save
# stuff to do when the record is saved, maybe redirect to show page or index
else
# stuff to do if record is not saved... probably redisplay new format with errors
end
end
EDIT: to clarify, the method completed_task_params (which you correctly coded) essentially flags the form attributes as acceptable. Had you done CompletedTask.new(params[:completed_task]) strong parameters would've been unhappy as the attributes weren't flagged as permitted.
Im working to create a getting started controller, that guides a new users through uploading a photo, finding friends, inviting people etc.
GettingStarted has no model itself, it just guides users through a wizard. A user could fully bypass this gettingstarted process without breaking the site. It's just a guide...
What I've done so far is:
Create a Route, Controller and Model:
Route:
resources :getting_started
namespace :getting_started do
resource :users, :only => [:edit, :update]
end
Controller:
class GettingStartedController < ApplicationController
def index
#current_step = current_step
end
protected
def current_step
current_step || steps.first
return 1
end
def steps
%w[step1 step2 step3]
end
end
Model
class GettingStarted < ActiveRecord::Base
attr_writer :current_step
attr_accessor :current_step
def current_step
#current_step || steps.first
return 1
end
def steps
%w[step1 step2 step3]
end
def next_step
self.current_step = steps[steps.index(current_step)+1]
end
def previous_step
self.current_step = steps[steps.index(current_step)-1]
end
def first_step?
current_step == steps.first
end
def last_step?
current_step == steps.last
end
end
View:
<%= #current_step.inspect %>
<% form_for #gettingstarted do |f| %>
<table>
<tbody>
<tr>
<td>
<%= link_to image_tag current_user.profile_pic.url(:large), :class => 'getting-started-profile-pic' %>
</td>
<td>
Upload a photo
</td>
</tr>
<table>
<tbody>
<% end %>
Right now I'm stuck on the issue that I need GettingStarted to guide users through existing models, not be a model itself. And I'm getting undefined method `model_name' for NilClass:Class
Suggestions, thoughts on the above?
Thanks
your GettingStarted model doesn't have to inherit from ActiveRecord::Base but it does, you are getting error because Base is expecting a table in your db called GettingStarteds, or something. If you want to keep the content dynamic, meaning saving it in the db so you can change it, then you are pretty close, you could use a natural language model like 'steps' and the steps have an order associated with them, then you can look up the step based on its order in the getting started controller. you can also use a vanilla workflow with a steps controller, and then rename the route in the routes with the :as => option
if the steps are static you might want to explore some of the static page model libraries available like high voltage https://github.com/thoughtbot/high_voltage