Rails 3.1 template handlers - ruby-on-rails

I have a ruby gem, poirot, which enables the use of mustache templates in Rails. The template handler I have was extending from ActionView::Template::Handler, however this appears to be deprecated in Rails 3.1.
I have re-factored the handler to comply with the deprecation warnings. In doing this I am now unable to pass locals, or the view context, to the template. I can't seem to find out how to get this working with Rails 3.1.
module Poirot
class Handler
attr_reader :template
def initialize(template)
#template = template
end
def self.call(template, *args)
self.new(template).call
end
def call
view_path = "#{template.virtual_path}_view"
abs_view_path = Rails.root.join('app/views', view_path)
view_class = begin
view_path.classify.constantize
rescue NameError => e
Poirot::View
end
"#{view_class}.new(self, '#{template.source.gsub(/'/, "\\\\'")}').render.html_safe"
end
end
end
In my code above for the handler I get passed the template, which is an instance of ActionView::Template. But I'm not sure how to get the view context, which should include the locals etc
Can someone point me in the right direction?

Okay I have a solution, I'm not sure it is the best, it feels a little hacky to me!
In my view class I have managed to get access to the locals by doing the following:
locals = view_context.send(:view_renderer).send(:_partial_renderer).instance_variable_get("#locals") || {}
This feels a little messy as both view_renderer and _partial_renderer are private, and there is no proper accessor to the locals ivar.
I'm still hoping there is a better way to do this!

I spent about 4 hours investigating source code to find a solution, and now it's seems very simple:
just add "local_assigns" where you are want to eval it and use.
For example:
"#{view_class}.new(self, '#{template.source.gsub(/'/, "\\\\'")}', local_assigns).render.html_safe"
this string will be evaluted inside the module context - ActionView::CompiledTemplates and local_assigns will be accessible there.

Related

simple ruby method helper

I am just learning ruby and wanting to practice writing little helper methods for rails as a good way of revising the basics.
all I would like to do is provide a counter for scoped objects.
so in my view i write this
=stats_counter_for(with_pending_state)
'pending_state' being a particular scope of the model.
def stats_counter_for(object_state)
Photo.object_state.count
end
so i want to pass this through to provide a count of all items with a pending state.
so eventually I can do
=stats_counter_for(with_active_state)
=stats_counter_for(with_inactive_state)
(the equals is from the haml view)
update error message
undefined local variable or method `with_pending_state' for #<#<Class:0x007fbce1118230>:0x007fbce1123770>
=link_to '/ Pending Approval', pending_admin_entries_path
=stats_counter_for(with_pending_state)
=link_to '/ Rejected', rejected_admin_entries_path
Where am I going wrong here? I am sure this is incredibly simple.
You can use the send method:
def stats_counter_for(state)
Photo.send("with_#{state}_state").count
end
So in your views you can use it like that:
= stats_counter_for(:active) # or as a string 'active'
= stats_counter_for(:inactive)

Cannot access helper method?

Here is a strange issue.
In my application helper, there is a method named test, and an other method named article_url. I have the article resource, and generate its url_helpers. In the test method, i call article_url, but it access the url_helper method instead of the method i defined in helper.
My project runs well in Rails 3.0, but when i upgrade to 3.2, it raise the issue. And under 3.2, development mode runs well but production raise issue.
Please forgive my poor english :-)
Sorry, code like this:
# application_helper.rb
module ApplicationHelper
...
def article_url(article, html_suffix = true)
if article.redirect_to.present?
article.redirect_to
else
url = "#{Settings.host}/articles/#{article.created_at.strftime("%Y-%m-%d")}/#{article.id}"
url += ".html" if html_suffix
url
end
end
...
def render_article_list(article)
link_to(xxx, article_url(article), {:target => "_blank"})
end
end
Rename your helper method. If you have a helper named article_url and a resource named article then article_url will be ambiguous between the routes and the helper. If you don't think this is the issue, post some code to help us narrow down the problem.

Rails Footnotes partials count is always zero

I'm using the rails-footnotes gem in my Rails 3.2 applications, but I can't seem to get the footnotes to register the existence of any partials: it always shows a zero partial count.
To be able to know how many and what partials are being displayed on a view easily like this is, I think, immensely useful, so I would really like to get this working (the rest works great), but I'm not sure what the problem is and am hoping someone else has had the same problem and was able to resolve it. Is there a setting I've potentially missed?
I don't think it's relevant, but I'm using OSX 10.6.8 and had some issues getting the gem to work with Sublime Text 2 properly, but they did get resolved (details in this StackOverflow answer).
Update:
It seems that the issue only exists for haml templates, as I am getting expected output for erb templates. It would seem that only erb templates are counted/recognized...?
Update 2:
#DonHopkins' answer below got all my Haml templates to register with Rails Footnotes. I put it in my config file as follows:
config/initializers/rails_footnotes.rb
if defined?(Footnotes) && Rails.env.development?
Footnotes.run! # first of all
Footnotes::Notes::LogNote::LoggingExtensions.module_eval do
def add(*args, &block)
super
logged_message = args[2] + "\n"
Footnotes::Notes::LogNote.log(logged_message)
logged_message
end
end
# ... other init code
Footnotes::Filter.prefix = 'subl://open?url=file://%s&line=%d&column=%d'
end
I had a similar problem, although I am using erb templates, not haml. I fixed it with a monkey patch to rails-footnotes.
Looking at the rails-footnotes code (version 3.7.9), it looked to me like the problem is in this method:
module Footnotes
module Notes
class LogNote < AbstractNote
...
module LoggingExtensions
def add(*args, &block)
logged_message = super
Footnotes::Notes::LogNote.log(logged_message)
logged_message
end
end
...
end
end
end
The add method is assuming that super returns the message that is being logged, but in my testing super was returning a boolean value. To solve the problem, I created a file called footnotes_patch.rb with the following:
Footnotes::Notes::LogNote::LoggingExtensions.module_eval do
def add(*args, &block)
super
logged_message = args[2] + "\n"
Footnotes::Notes::LogNote.log(logged_message)
logged_message
end
end
If you want to try the solution, put that file in config/initializers, then restart your application.

Is there a way to prevent serialized attributes in rails from getting updated even if there are not changes?

This is probably one of the things that all new users find out about Rails sooner or later. I just realized that rails is updating all fields with the serialize keyword, without checking if anything really changed inside. In a way that is the sensible thing to do for the generic framework.
But is there a way to override this behavior? If I can keep track of whether the values in a serialized fields have changed or not, is there a way to prevent it from being pushed in the update statement? I tried using "update_attributes" and limiting the hash to the fields of interest, but rails still updates all the serialized fields.
Suggestions?
Here is a similar solution for Rails 3.1.3.
From: https://sites.google.com/site/wangsnotes/ruby/ror/z00---topics/fail-to-partial-update-with-serialized-data
Put the following code in config/initializers/
ActiveRecord::Base.class_eval do
class_attribute :no_serialize_update
self.no_serialize_update = false
end
ActiveRecord::AttributeMethods::Dirty.class_eval do
def update(*)
if partial_updates?
if self.no_serialize_update
super(changed)
else
super(changed | (attributes.keys & self.class.serialized_attributes.keys))
end
else
super
end
end
end
Yes, that was bugging me too. This is what I did for Rails 2.3.14 (or lower):
# config/initializers/nopupdateserialize.rb
module ActiveRecord
class Base
class_attribute :no_serialize_update
self.no_serialize_update = false
end
end
module ActiveRecord2
module Dirty
def self.included(receiver)
receiver.alias_method_chain :update, :dirty2
end
private
def update_with_dirty2
if partial_updates?
if self.no_serialize_update
update_without_dirty(changed)
else
update_without_dirty(changed | (attributes.keys & self.class.serialized_attributes.keys))
end
else
update_without_dirty
end
end
end
end
ActiveRecord::Base.send :include, ActiveRecord2::Dirty
Then in your controller use:
model_item.no_serialize_update = true
model_item.update_attributes(params[:model_item])
model_item.increment!(:hits)
model_item.update_attribute(:nonserializedfield => "update me")
etc.
Or define it in your model if you do not expect any changes to the serialized field once created (but update_attribute(:serialized_field => "update me" still works!)
class Model < ActiveRecord::Base
serialize :serialized_field
def no_serialize_update
true
end
end
I ran into this problem today and ended up hacking my own serializer together with a getter and setter. First I renamed the field to #{column}_raw and then used the following code in the model (for the media attribute in my case).
require 'json'
...
def media=(media)
self.media_raw = JSON.dump(media)
end
def media
JSON.parse(media_raw) if media_raw.present?
end
Now partial updates work great for me, and the field is only updated when the data is actually changed.
The problem with Joris' answer is that it hooks into the alias_method_chain chain, disabling all the chains done after (like update_with_callbacks which accounts for the problems of triggers not being called). I'll try to make a diagram to make it easier to understand.
You may start with a chain like this
update -> update_with_foo -> update_with_bar -> update_with_baz
Notice that update_without_foo points to update_with_bar and update_without_bar to update_with_baz
Since you can't directly modify update_with_bar per the inner workings of alias_method_chain you might try to hook into the chain by adding a new link (bar2) and calling update_without_bar, so:
alias_method_chain :update, :bar2
Unfortunately, this will get you the following chain:
update -> update_with_bar2 -> update_with_baz
So update_with_foo is gone!
So, knowing that alias_method_chain won't let you redefine _with methods my solution so far has been to redefine update_without_dirty and do the attribute selection there.
Not quite a solution but a good workaround in many cases for me was simply to move the serialized column(s) to an associated model - often this actually was a good fit semantically anyway.
There is also discussions in https://github.com/rails/rails/issues/8328.

RSpec mock_model and inherited_resources

I'm trying to write spec for inherited_resources controller. I decided to mock all integration with the database using rspec's mock_model. Unfortunately I can't write spec for create and update action because I'm getting the following error: https://gist.github.com/936947
Can someone help me with this issue?
I was having the same issue using flexmock.
the cause is that it does not use the update_attributes method to make the routing decision. It checks the resource.errors to see whether it is empty.
So to get it to respond properly, we will need to mock out the errors method as well.
Here is the pertinent code #line 248 in lib/inherited_resources/base_helpers.rb
def respond_with_dual_blocks(object, options, &block) #:nodoc:
args = (with_chain(object) << options)
case block.try(:arity)
when 2
respond_with(*args) do |responder|
blank_slate = InheritedResources::BlankSlate.new
if object.errors.empty?
block.call(responder, blank_slate)
else
block.call(blank_slate, responder)
end
end
when 1
respond_with(*args, &block)
else
options[:location] = block.call if block
respond_with(*args)
end
end
The failure messages are about the inability to access named routes from inside the controller, so I'm not sure that this has anything to do with mock_model. Have you tried the same examples using real models?

Resources