How to render rails partial from presenter layer? - ruby-on-rails-3

Well, I'm into this situation as well now using rails 3.2.1
Following is the presenter in app/presenters/form_presenter.rb
class FormPresenter
def render_form
ActionView::Base.new.render partial: "passions/add_form"
end
end
From the view I'm calling,
...
= AddFormPresenter.new.render_form
...
But it blows with the following error:
13:14:12 ActionView::MissingTemplate - Missing partial passions/passion_concept_add_form with {:locale=>[:en], :formats=>[:html, :text, :js, :css, :ics, :csv, :png, :jpeg, :gif, :bmp, :tiff, :mpeg, :xml, :rss, :atom, :yaml, :multipart_form, :url_encoded_form, :json, :pdf, :zip], :handlers=>[:erb, :builder, :slim, :coffee, :rabl]}. Searched in:
...
There is this similar question at RAILS-3.1 render method for ActionView::Base but its not helpful.
How to render this partial from the presenter layer?

Well, I just did it by grabbing the view context using a before filter. My reference was this: https://github.com/amatsuda/active_decorator/blob/master/lib/active_decorator/view_context.rb
So something like:
class FormPresenter
def render_form
FromPresenter.view_context.render partial: "passions/add_form"
end
class << self
def view_context
Thread.current[:view_context]
end
def view_context=(view_context)
Thread.current[:view_context] = view_context
end
end
module Controller
extend ActiveSupport::Concern
included do
before_filter do |controller|
FormPresenter.view_context = controller.view_context
end
end
end
end
and in application_controller.rb
class ApplicationController < ActionController::Base
...
include FormPresenter::Controller
...
end

This isn't typical of the presenter pattern. Presenters are for centralizing complicated data and logic needed to simpify the view's rendering task. Here you are rendering inside the presenter. Is this really what you intend?
Say the answer is yes. Then just creating a new ActionView::Base is asking for trouble because initializing it is non-trivial as shown here. Something strange is going on with class or some other kind of nesting. Where did the passion_concept_ prefix come from in the error message? It looks like you're not telling us all we need about your app.
You may find joy by telling the presenter explicitly where it's rendering:
class FormPresenter
def self.render_form(view)
view.render partial: "passions/add_form"
end
end
Then in the view:
= FormPresenter.render_form(self)
(Here again the explanation is not clear. What is AddFormPresenter?) I don't have a machine where I can try this at the moment, but it ought to be more debuggable than what you've got.

Related

Validating Child Object with ActiveModel Validations

I have two plain Ruby classes, Account and Contact. I am using Simple Form's simple_form_for and simple_fields_for to create nested attributes. I am looking to fulfill the following validation requirements:
An associated Contact must exist for the new Account
The associated Contact must be valid (i.e., account.contact.valid?)
It looks like ActiveModel no longer includes the validates_associated method, as using that method results in an undefined method error. I considered requiring ActiveRecord::Validations, but this led down a stretch of various errors (e.g., undefined method `marked_for_destruction?')
I also considered defining validate on the Account class and calling valid? on the associated object, but that only prevented the form from submitting if there was also an error on the parent object.
validate do |account|
account.contact.valid?
# required for form to fail
errors.add(:base, "some error")
end
Is there something I'm not aware of to solve this? Thanks.
I recently (7 years after this question has been asked!) faced the same issue and solved it by implementing the AssociatedValidator based on the ActiveRecord one.
I simply included it in config/initializers folder:
module ActiveModel
module Validations
class AssociatedValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if Array(value).reject { |r| valid_object?(r) }.any?
record.errors.add(attribute, :invalid, **options.merge(value: value))
end
end
private
def valid_object?(record)
record.valid?
end
end
module ClassMethods
def validates_associated(*attr_names)
validates_with AssociatedValidator, _merge_attributes(attr_names)
end
end
end
end
now you can use validates_associated in ActiveModel too.
class Person
include Virtus
include ActiveModel::Model
attribute :address, Address, :default => Address.new
validate :address_valid
private
def address_valid
errors.add(:base, 'address is not valid') unless address.valid?
end
end
class Address
include Virtus::ValueObject
include ActiveModel::Validations
attribute :line_1, String
attribute :line_2, String
validates :line_1, :presence => true
validates :line_2, :presence => true
end
The errors show up in the form if you pass an object to simple_fields_for:
= form.simple_fields_for person.address do |af|
= af.input :line_1
Another option is overriding valid?:
def valid?
super & address.valid?
end
Note its & not && so the conditions are not short circuited if the first returns false.

How to make ActsAsTaggableOn::Tag inherit methods from Tag model

I'm using acts-as-taggable-on 2.0.6 for tagging in a Rails 3.0.9 app. I've successfully added db-backed attributes to the Tag model, for example each tag has an RSS feed so I can call #post.tags.first.feed_url to grab the feedburner URL from my database.
But while I can add attributes to the Tag model, it seems I can't add instance methods. I created the instance method:
class Tag < ActiveRecord::Base
...
def subscribable?
!feed_url.blank?
end
...
end
But when I call #post.tags.first.subscribable? I get the error:
NoMethodError: undefined method `subscribable?' for #<ActsAsTaggableOn::Tag:0x00000100d32290>
Is there a way to tell ActsAsTaggableOn::Tag objects to inherit model methods from the Tag model?
You need to add this /config/initializers/tag_monkey_patch.rb
class ActsAsTaggableOn::Tag
def subscribable?
!feed_url.blank?
end
end
I'm wondering the exact same thing. As far as i've tried, it isn't working for me..
class Tag < ActsAsTaggableOn::Tag
def total_count
...
end
end
When I call it in a dirty chained form example, like so:
my_model.my_instance.tag_counts_on(:tags).first.total_count
I get a
undefined method `total_count' for #<ActsAsTaggableOn::Tag id: 1, name: "first">
From what I understand, you shouldn't be declaring a new "Tag" model like this, but should should be overriding the class like this:
class ActsAsTaggableOn::Tag
def total_count
...
end
end

Helpers in controller - Rails 3

I migrated from rails 2.x to 3.x. Now when calling a controller method throws
undefined method `my_helper_method' for nil:NilClass
MyController.rb
class MyController < ApplicationController
def foo
#template.my_helper_method
end
end
MyControllerHelper.rb
class MyControllerHelper
def my_helper_method
puts "Hello"
end
end
ApplicationController
class ApplicationController < ActionController::Base
helper :all
end
How to get this working?
This is actually answered in another SO post: Rails 3: #template variable inside controllers is nil
Essentially, you can replace #template with view_context
#template is an object, in your case nil. If this object doesn't has the method (my_helper_method) in it, you cannot call it (especially not if it is nil).
Methods defined in helpers are called like regular methods. But not in controllers, they are called in views. Your helper :all just makes all helpers available to the views.
So, in your view: my_helper_method :arg1, :arg2
IF you need a method for your object (#template), you need to give your object this method.
Example:
class Template < ActiveRecord::Base
def my_helper_method
# do something on a template instance
end
end
class MyController < ApplicationController
def foo
#template = Template.first
#template.my_helper_method # which actually isn't a helper
end
end
What helpers do:
module MyHelper
def helper_method_for_template(what)
end
end
# in your view
helper_method_for_template(#template)
Mixing in a helper (be aware of having a mess in your code when mixing view helpers with views and models)
class Template < ActiveRecord::Base
include MyHelper
# Now, there is #template.helper_method_for_template(what) in here.
# This can get messy when you are making your helpers available to your
# views AND use them here. So why not just write the code in here where it belongs
# and leave helpers to the views?
end

how to access a method in a model from a controller in rails

I want to put logic in the model rather than controller.
class UsersController < ApplicationController
def somemethod
d = User.methodinmodel
end
end
class User < ActiveRecord::Base
def methodinmodel
"retuns foo"
end
end
I get an error that there is no methodinmodel for the User model.
Why?
If you want to be able to call methodinmodel on the User class in general rather than a specific user, you need to make it a class method using self:
class User < ActiveRecord::Base
def self.methodinmodel
"returns foo"
end
end
Your current method definition would only work if you called it on a user:
#user = User.create!
#user.methodinmodel # Works.
User.methodinmodel # Doesn't work.
Using the new implementation using self would allow you to call it like:
User.methodinmodel # Works.

setting the "view-name" of a controller

I would like to centralize similar actions of some controllers and wrote a controller from which the other controllers inherites. This works fine.
# calling Configurations#index will render configurations/index.html.erb
# while 'configurations' being the internal controller_path used to look for the view
class ConfigurationsController < EditorController
end
class EditorController < ApplicationController
def index
render 'index'
end
end
But now I would like to centralise the views to the "base"-controller one's, so if an inheriting controller is called, the controller_path used should be the base-controller one's.
Is there a way, to rewrite a controllers name or controller_path?
I looked at the source of AbstractController::Base and found that (line 90)
def controller_path
#controller_path ||= name.sub(/Controller$/, '').underscore unless anonymous?
end
So I just need to set #controller_path from my base-controller don't I ? This doesn't change anything:
#just does the same as above
class EditorController < ApplicationController
#controller_path = 'editor'
def index
render 'index'
end
end
So is there a way to set the controller_path manually?
great thanks in advance!
damn I found it on my own!
I just overwrote the controller_path method:
class EditorController < ApplicationController
def controller_path
'editor'
end
#...
end
this will ever use the view-folder 'editor' for any inheriting controller.