I have read:
Concerns, Decorators, Presenters, Service Objects, Helpers, Help me Decide
and trying to figure out the difference between presenters, view objects, decorators, exhibits, and helpers.
I have multiple active record models that I need to display in a view using the show method.
Examples of what I need to display are:
ClassModule SomeTypeOfPattern
def name
User.name
end
def car_name
User.car.listing.car_name
end
def car
User.car
end
def car_marketing
User.car.marketing
end
# AND 20 to 30 other similar delegations/methods from 4 related tables
end
So if I delegate these relationships, what should the class/module be called? A presenter? Decorator? View Object? I am so confused by all these terms, but want to follow convention.
The example you are showing looks like a Presenter to me.
A presenter is an object that presents other information with its own interface.
If you changed what you have just a little, you could use it like this:
presenter
class UserCarPresenter
attr_reader :user
def initialize(user)
#user = user
end
def name
user.name
end
def car_name
user.car.listing.car_name
end
def car
user.car
end
def car_marketing
user.car.marketing
end
end
controller
class CarsController < ApplicationController
def show
#user = UserPresenter.new(user)
end
end
view
<h1><%= #user.name %></h1>
<h2><%= #user.car_name %></h1>
Related
Many presenters, have something of the form of
class MyClassPresenter < SimpleDelegator
def something_extra
end
end
view_obj = MyClassPresenter.new my_class_instance
I want to transverse the instance:
view_obj.nested_obj.nested_obj.value
This means creating multiple presentation objects, which in effect start just copying the models.
class MyClassPresenter < SimpleDelegator
def something_extra
end
def nest_obj
AnotherPresenter.new(__get_obj__.nest_obj)
end
end
To demonstrate a real world example a bit better
class UserPresenter < SimpleDelegator
def home_page_url
"/home/#{__get_obj__.id}"
end
end
class MyController < ApplicationController
def show
#user = UserPresenter.new(current_user) # devise's current_user
end
end
/my/show.html.slim
/! Show profile comments
- for comment in #user.profile.comments
| Comment on:
= comment.created_at
= comment.content
The main object being passed in is #user, however, the presenter doesn't cover that far. I could create #comments, but I would like the code to be more flexible to however the front-end engineers wanted to take it.
How have other people handled multiple layers for a presenter?
Would the code look something like this? (ugh)
/! Show profile comments
- for comment in #user.profile.comments
- display_comment = CommentPresenter.new(comment)
| Comment on:
= display_comment.comment.created_at
= display_comment.content
-daniel
Ok, I'm quite confused and a little stuck here, I'm trying to pass data to my Model via attr_accessor but I cant find the right way. Here is my setup so far:
class ApplicationController < ActionController::Base
before_filter :current_league
protected
def current_league
#current_league ||= Conf.all.order('updated_at ASC').last.league
end
end
class HerosController < ApplicationController
def index
#heros = Hero.all.order(:name)
end
end
class Hero < ActiveRecord::Base
attr_accessor :current_league
def some_method
puts current_league
end
end
<% #heros.each do |hero| %>
<tr>
<td><%= hero.some_method %></td>
</tr>
<% end %>
Now how do I set the #current_league inside my model? I know I can have an attr_accessor inside my model, but this only applies to an instance of this model but the index action doesn't create an instance as far as I know. Maybe someone can point me in the right direction. Thanks in advance.
The #heros variable is a collection of Hero instances. You can loop through them and set it if you want.
class HerosController < ApplicationController
def index
#heros = Hero.all.order(:name).each do |hero|
hero.current_league = current_league
end
end
end
While it works, I don't find this answer to be all that elegant. Granted, I don't know the full extend of the thing you are making, but based on the code here I would create a composite object. Something like this:
class HeroInLeague
attr_reader :league, :hero
def initialize(league, hero)
#league = league
#hero = hero
end
def some_method
# ...
end
end
Then you can create these objects inside your controller:
class HerosController < ApplicationController
def index
#heros_in_league = Hero.all.order(:name).map { |hero|
HeroInLeague.new(current_league, hero)
}
end
end
Now you've created a place for methods to go that are related the combination of heros and leagues. Why is this important? Well, with the previous approach you'd probably end up with methods on Hero that don't make any sense when there is no current league. (like the some_method method). That makes the Hero class a bit of a mess. Now you've created a place to put some_method and all its friends.
You can use delegators to make the interface of HeroInLeague a bit more friendly, so you don't have to do hero_in_league.hero.foo, but can call hero_in_league.foo directly.
I have a very big question about Rails. Suppose that we need to create a web site about blogs, we allow user registration and the user has their own management interface which make it possible for them to add, delete, edit, and select the article and comment. The operation on the article and comment might be used in other positions in the future.
So we have a article model and a comment model.
Now we create a users controller:
class UserController < ApplicationController
def articleList
end
def articleSave
end
def articleUpdate
end
def articleDestroy
end
def articleEdit
end
def articleAdd
end
def commentList
end
def commentDestroy
end
def commentEdit
end
end
But it din't look good, and when the user management control has many features, this user controller will be very big. Should I create an article controller and comment controller to process the request? Just separated into the article controller is like this:
class ArticleController < ApplicationController
def list
end
def save
end
def update
end
def destroy
end
def edit
end
def add
end
end
Comment controller is as follows:
class CommentController < ApplicationController
def list
end
def destroy
end
def edit
end
def update
end
end
I don't know how to organize the structure.
I would have separate controllers for each of the Users Articles and Comments, but nest them with Comments inside Articles, and Articles inside Users. This seems to fit the relationship between the concepts as you've described them
See
http://www.sentia.com.au/blog/when-to-use-nested-controllers-in-your-rails-apps
and
http://guides.rubyonrails.org/routing.html#nested-resources
for more on nesting controllers.
Currently I am using the Last.fm api to return concert data (returns a hash) in my controller, and in the view cycling through this hash to return the data I want. I want this concert data to become more dynamic and put everything into a model. How do I do this? Should I do this in the controller or somehow in the model?
Here is an example of my code
# app/controllers/events_controller.rb
class EventsController < ApplicationController
def index
#events = #lastfm.geo.get_events("Chicago",0,5)
respond_with #events
end
end
# app/views/events/index.html.erb
<% #events.each do |event| %>
Headliner: <%= event["artists"]["headliner"] %>
<% end %>
In this example I would would want and Event Model with headliner as a parameter, and put all 5 of the events into this model.
I believe it's a good idea to have a model. There are several advantages as I can see
1 - You could access data as OO way as other objects
2 - if you have some business logics (Ex: calculations) you could do it in the model itself with out messing up your view
3 - it's clean and DRY
Example model class would be (this is not a working model, but just to give you an idea):
class Event
attr_accessor :headliner
def self.event_list(limit = 5)
lastfm.geo.get_events("Chicago",0,limit)
end
end
So you can clean-up your view as
<% Event.each do |event| %>
Headliner: event.headliner
<% end %>
It's difficult to thoroughly answer this question without more knowledge about the last.fm API. As a general rule, you want to keep most of your complex logic and relationship data in the model.
For example, you already know you need an Event model, but it also looks like you might need an Artist model as well. You might end up with something like this:
# app/models/artist.rb
class Artist
attr_accessor :name
def initialize(name)
self.name = name
end
end
# app/models/event.rb
class Event
attr_accessor :artists
def initialize(attributes)
#attributes = attributes
self.artists = attributes['artists'].map do |artist_name|
Artist.new(artist_name)
end
end
def headliner
#headliner ||= self.artists.detect do |artist|
#attributes['headliner'] == artist.name
end
end
end
# app/controllers/events_controller.rb
class EventsController < ApplicationController
def index
#events = #lastfm.geo.get_events('Chicago', 0, 5).map do |event_attributes|
Event.new(event_attributes)
end
respond_with #events
end
end
You might also want to look into ActiveModel, which has some helpful features for models that aren't database-backed and can't inherit from ActiveRecord::Base.
I'm trying give a "Welcome Message" to my users with that:
#welcome_controller.rb
class WelcomeController < ApplicationController
def hi
#current_user
if (#current_user)
#welr = '¡Bienvenido' + current_user + ' a nuestra web!'
else
#weli = "¡Bienvenido invitado, no dude en registrarse!"
end
end
end
#hi.html.erb Only the call
<%= hi %>
When I initialize my server the controller give me this message:
undefined local variable or method `hi' for
I have tried many wways of repairing this but I can't.
You need to define hi as a helper_method in your controller. Something like
class WelcomeController < ApplicationController
helper_method :hi
def hi
# your stuff here...
end
end
See http://apidock.com/rails/AbstractController/Helpers/ClassMethods/helper_method for more info
That's not how you use controller methods. In Rails, methods defined on a controller are used to 'set up' the data needed for a particular view, or to handle a given request. They're not supposed to be called directly by a view.
For what you're trying to do, you need to add a helper method to WelcomeHelper. So, assuming you want http://yourapp.dev/welcome/ to output the message above, this is what you'd need:
# app/controllers/welcome_controller.rb
class WelcomeController < ApplicationController
def index
# Explicitly defining the `index` method is somewhat redundant, given
# that you appear to have no other logic for this view. However, I have
# included it for the sake of example.
end
end
# app/views/welcome/index.html.erb
<%= greeting %>
# app/helpers/welcome_helper.rb
class WelcomeHelper
# All methods in WelcomeHelper will be made available to any views
# that are part of WelcomeController.
def welcome
if (#current_user)
# You may need to change this to something like `#current_user.name`,
# depending on what #current_user actually is.
'¡Bienvenido' + #current_user + ' a nuestra web!'
else
"¡Bienvenido invitado, no dude en registrarse!"
end
end
end
This article may help you :
Ruby on Rails: Accessing Controller Methods from Your View
Just write:
<% #controller.hi %>