How to share code between two controllers - ruby-on-rails

I have an instance variable #posts_by_month defined in two controllers and is used in two views:
Posts controller:
class PostsController < ApplicationController
def index
#posts = Post.all
#posts_by_month = Post.all.group_by { |post| post.created_at.strftime("%L") }
respond_to do |format|
format.html # index.html.erb
format.json { render json: #posts }
end
end
.
.
end
Archives controller:
class ArchivesController < ApplicationController
def index
#posts_by_month = Post.all.group_by { |post| post.created_at.strftime("%B") }
end
end
Posts index view:
<h1>Listing posts</h1>
.
.
.
<div>
<% #posts_by_month.each do |millisecond, posts| %>
<p><%= millisecond %>milliseconds <%= posts.count %></p>
<% end %>
</div>
Archives index view:
<% #posts_by_month.each do |monthname, posts| %>
<%= monthname %>
<ul>
<% posts.each do |post| %>
<h3><%= post.title %></h3>
<p><%= post.body %></p>
<% end %>
</ul>
<% end %>
I have two questions. Is there any way I can define the #posts_by_month instance variable so that I can have it available to both views without repeating it in each respective controller?
Secondly, is there any way that the millisecond part of <p><%= millisecond %>milliseconds <%= posts.count %></p> can be made into a link that leads to the archive view?
Note: In my app millisecond will be replaced by month as in the archive view.

When an action was executed, aka rendered, the instance is over. There is no more instance variable.
The instance variables in View are not real instance variables. View and Controller are in different classes, how can they share instance? The reality is, what Rails does is to copy those instance variable from Controller instance to View instance exactly.
So the answer in your question is: No.
But you can still dry your code by a private method in application controller, to share with PostsController and ArchiveController.
class ApplicationController
private
def posts_by_time(arg)
Post.all.group_by { |post| post.created_at.strftime(arg) }
end
end
class PostsController < ApplicationController
def index
#posts = posts_by_time "%L"
# ...
end
end
class ArchievesController < ApplicationController
def index
#posts = posts_by_time "%B"
# ...
end
end

Yes you can reduce duplicacy of same variable. One way is to use filters:
Define a mthod inside application controllers:
class ApplicationController < ActionController::Base
private
def find_post_by_month
#posts_by_month = Post.all.group_by { |post| post.created_at.strftime("%L") }
end
end
Then inside archive and posts controllers:
class ArchivesController < ApplicationController
before_filter :find_post_by_month, :only => :index
...
end
class PostsController < ApplicationController
before_filter :find_post_by_month, :only => :index
...
end
This will give you value in #posts_by_month variable.
And, for making link of mentioned text, you should use this code:
<p><%= link_to "#{millisecond} milliseconds", path %></p> # Replace path with your url

Related

Faraday rails started with it

I have to show data from this website: https://baconipsum.com/json-api/ , but I don't know where to write the code of it in my app. What code do I have to write for controllers and in views?
Setup faraday:
bacon = Faraday.new("https://baconipsum.com/") do |f|
f.response :json # automatically parse responses as json
end
Send a request in the controller:
#bacon = bacon.get("api/", type: 'all-meat', sentences: 1).body # => ["Leberkas frankfurter chicken tongue."]
Use it in the view:
<% #bacon.each do |meat| %>
<p>
<%= meat %>
</p>
<% end %>
https://lostisland.github.io/faraday/usage/
Update
There are many ways to set it up. Very simple set up could look like this:
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
private
# NOTE: this method will be accessible in any controller
# that inherits from ApplicationController
def baconipsum
# NOTE: memoize for a bit of performance, in case you're
# making multiple calls to this method.
#baconipsum ||= Faraday.new("https://baconipsum.com/") do |f|
f.response :json
end
end
end
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
def show
#article = Article.find(params[:id])
# I'm not quite sure what you're trying to do with it;
# change this to fit your use case.
#bacon = baconipsum.get("api/", type: 'all-meat').body
end
end
# app/views/articles/show.html.erb
<% #bacon.each do |meat| %>
<p> <%= meat %> </p>
<% end %>

How can show the data related to logged in user in rails

I am trying display the task related to logged in user but on my html page nothing show except the tag data
task_controller.rb
class TaskController < ApplicationController
def all_task
if current_user.present?
#all_task = Task.find_by_user_id(#current_user.id)
render template: 'task/allTask'
end
end
end
routes.rb
get 'all_task' => 'task#all_task'
task.erb
<p>All Task</p>
<% if user_signed_in? %>
<%#all_task.daily_task %>
<%#all_task.date %>
<%#all_task.created_at %>
<%end %>
Start by setting up an assocation between users and tasks:
class User < ApplicationRecord
# ...
has_many :tasks
end
Then setup the route and controller:
get '/user/tasks', to: 'users/tasks#index', as: :user_tasks
# app/controllers/users/tasks_controller.rb
module Users
class TasksController < ApplicationRecord
before_action :authenticate_user!
# display all the tasks belonging to the currently signed in user
# GET /user/tasks
def index
#tasks = current_user.tasks
end
private
# You don't need this if your using Devise
def authenticate_user!
unless current_user
redirect_to '/path/to/your/login',
notice: 'Please sign in before continuing'
end
end
end
end
Note that when you have a route like this that displays resources that belong to the current user you should use a callback to bail early and redirect the user to sign in instead of using if current_user.present? and giving a response which is meaningless to the user. This code should be DRY:ed into your ApplicationController (even better yet is to not reinvent the auth wheel).
You can link to the users tasks with:
<% if current_user.present? %>
<%= link_to 'My tasks', user_tasks_path %>
<% end %>
In your view you need to iterate across the returned tasks:
# app/views/users/tasks/index.html.erb
<p>All Tasks</p>
<% if #tasks.any? %>
<% #tasks.each do |task| %>
<%= task.daily_task %>
<%= task.date %>
<%= task.created_at %>
<% end %>
<% else %>
<p>You don't have any tasks.</p>
<% end %>
You can cut duplication here by using partials.
Can you make sure if the instance variable #current_user is defined? If not, try the following:
class TaskController < ApplicationController
def all_task
if current_user.present?
#all_task = Task.find_by_user_id(current_user.id)
render template: 'task/allTask'
end
end
end
instead of
class TaskController < ApplicationController
def all_task
if current_user.present?
#all_task = Task.find_by_user_id(#current_user.id)
render template: 'task/allTask'
end
end
end

Undefined method link_to_edit using Draper decorator

I've got a User and Post models, which are related to each other in a classical way -- User has_many :posts and Post belongs_to :user. In my users#show, where I display a user's profile, I also have a list of all posts he has made. Also, I wanted to have links to edit and delete each post respectfully. So, I made up with this:
<% #user.posts.each do |post| %>
<h1><%= link_to post.title, post_path(post) %></h1>
<% if #user == current_user %>
<%= link_to 'Edit', edit_post_path(post) %>
<%= link_to 'Delete', post_path(post), method: :delete %>
<% end %>
<% end %>
But surely placing this logic into view results in a mess, so I decided to use Draper and write decorators for that. As we are going to check rights for posts#edit and posts#delete methods, I came up with a decorator for Post model and tried to use it in PostsController. Here it goes:
class PostDecorator << Draper::Decorator
delegate_all
def link_to_edit
if object.user == current_user
h.link_to 'Edit', h.edit_post_path(object)
end
end
def link_to_delete
if object.user == current.user
h.link_to 'Delete', h.post_path(object), method: :delete
end
end
end
Then, in my PostsController:
# ... class definition
before_action :set_post, only: [:show, :edit, :update, :destroy]
# ... other controller methods
def edit; end
def update
if #post.update(post_params)
#post.save
redirect_to post_path(#post)
else
render 'edit'
end
end
def destroy
#post.destroy
redirect_to feed_path
end
private
# Using FriendlyId gem to have neat slugs
def set_post
#post = Post.friendly.find(params[:id]).decorate
end
But every time I try to render my User profile with list of his posts, with the use of my new helpers <%= post.link_to_delete %> and <%= post.link_to_edit %> instead of that conditional mess, it just returns me the following error:
What am I doing wrong?
You probably figured this out in the meantime but here's an answer for others: You were calling #post = ....decorate in your controller but you are using #user.posts.each { |post| ... } in your view. The objects fed to that block are not decorated. Only #post is.
In your view you should have done something like #user.posts.each { |raw_post| post = raw_post.decorate } and so on. Obviously, with ERB syntax. Or #user.decorated_posts.each ... where
class User < ActiveRecord::Base
...
def decorated_posts
# this will load every post associated with the user.
# if there are a lot of them you might want to only load a limited scope of them
posts.map(&:decorate)
end
...
end

How does rails pass instance variable from controller to views?

I created a simple rails app in rails using scaffolding method for restaurants.
This is the show and edit controller method for restaurants_controller.rb. Notice how they are just blank methods:
# GET /restaurants/1
# GET /restaurants/1.json
def show
end
# GET /restaurants/1/edit
def edit
end
This is restaurants/show.html.erb:
<p id="notice"><%= notice %></p>
<%= image_tag #restaurant.image_url %>
<p>
<strong>Name:</strong>
<%= #restaurant.name %>
</p>
<p>
<strong>Address:</strong>
<%= #restaurant.address %>
</p>
...
and restaurants/edit.html.erb:
<h1>Editing Restaurant</h1>
<%= render 'form', restaurant: #restaurant %>
<%= link_to 'Show', #restaurant, class: "btn btn-link" %> |
<%= link_to 'Back', restaurants_path, class: "btn btn-link" %>
Here is my question: my current understanding (could be wrong) is that we define the instance variable, in this case, #restaurant in restaurant_controllers.rb, and Rails automatically connects the variables defined in the controller to views. For example, index method in restaurant controller:
def index
#restaurants = Restaurant.all
end
when I call #restaurants in index.html.erb, Rails brings up #restaurants from index method to be used in views.
Where does rails get the #restaurant instance variable in show.html.erb and edit.html.erb from even though show and edit method in restaurants_controller.rb are empty methods? I am using Rails 5.
So rails acheives this by applying this to generated scaffolds
class TestsController < ApplicationController
before_action :set_test, only: [:show, :edit, :new, :update, :destroy]
private
def set_test
#test = Test.find(params[:id])
end
end
that dose the same as adding
#test = Test.find(params[:id]) to every action if you don't need to..
that then, sets the instance variable on the actions defined by the before_action
im no rails pro, so there may be a much better answer to this, but i have learned to not question "rails magic".

Presenter show page

I've started moving all my view logic to presenters and I'm getting the following error NameError at /bids
uninitialized constant Bid::ActiveRecord_Associations_CollectionProxyPresenter on my index view. The presenter works in all my other views for this model except index. I've added commented code of fixes I've tried.
Code:
Index view:
<% present #bids do |bid_presenter| %>
<% end %>
# tried #bids to match controller
# <% present #bids do |bid_presenter| %>
# <% end %>
bid_presenter.rb:
class BidPresenter < BasePresenter
presents :bid
# tried :bids
# presents :bids
end
base_presenter.rb:
class BasePresenter
def initialize(object, template)
#object = object
#template = template
end
private
def self.presents(name)
define_method(name) do
#object
end
end
# h method returns the template object
def h
#template
end
def method_missing(*args, &block)
#template.send(*args, &block)
end
end
bids_controller.rb:
def index
#bids = current_user.bids
end
Have you tried:
<% #bids.each do |bid| %>
<% present bid do |bid_presenter| %>
<% end %>
<% end %>
The Presenter is presenting an instance of the model; your code is trying to present an ActiveRecord::Collection or something.

Resources