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.
Related
I working on project using Rails 4.1.6 now. And I have strange problem. Method to_param for my models (Product, Category) sometimes not calling. I use it for SEO-friendly urls.
Here is my to_param method for Category model:
def to_param
puts 'I am in Category to_param'
"#{id}-#{title.to_slug.normalize.to_s}"
end
I use puts for find out is this method working or no. So, when my urls looks good (/categories/39-средства-дезинфекции) I can see the string 'I am in Category to_param' on my server console. This is correct case and all it's great.
But sometimes I have urls like /categories/39 for the same objects. When I look into console for this case, I don't see any prints from my to_param method form Category model.
These two cases I have on the same pages, same views and using the same helpers for category url (category_path).
Most complicated for this situation is that I can't reproduce this bug and don't see any regularity. For the same objects I have correct urls most of times, but sometimes it's not. If I restart rails server and refresh browser with clear cache – problem may out and urls will be correct again.
During my debug and research I found source code for base class. But I can't see there any reasons for the situation described above.
def to_param(method_name = nil)
if method_name.nil?
super()
else
define_method :to_param do
if (default = super()) &&
(result = send(method_name).to_s).present? &&
(param = result.squish.truncate(20, separator: /\s/, omission: nil).parameterize).present?
"#{default}-#{param}"
else
default
end
end
end
end
Also I can tell that this problem was appear, when I used FriendlyID before, using regex for clear and build slugs, and now for babosa gem. So, I think the problem is my to_param sometimes not calling for my model.
So, I found the reason of this behaviour. Now it's resolved!
The reason was I have redefined to_param for Product and Category in my ActiveAdmin files:
before_filter do
Product.class_eval do
def to_param
puts "I am in admin Product to_param"
id.to_s
end
end
Category.class_eval do
def to_param
puts "I am in admin Category to_param"
id.to_s
end
end
end
So, when I was log in Admin panel and go to Product page – "bug" will appear on front-end views.
So, I need to remove Product.class_eval and Category.class_eval blocks from my admin classes.
I'm just getting started with Rails, so I'm using Brakeman to learn about potential vulnerabilities in my newbie code. It's throwing a high-confidence "Dynamic Render Path" warning about the following code in my show.js.erb file:
$('#media-fragment').html('<%= escape_javascript(render(params[:partial])) %>');
I actually expected this was a problem, so no surprise there. So I changed it to the following:
# controller:
def show
if legal_partial?
#allowed_partial = params[:partial]
else
raise StandardError, "unexpected partial request: #{params[:partial]}"
end
end
private
def legal_partial?
%w(screenshots video updates).include? params[:partial]
end
# ...
# show.js.erb
$('#media-fragment').html('<%= escape_javascript(render(#allowed_partial)) %>');
Although I believe the code is now safe, Brakeman is still unhappy with this. Is there a more idiomatic way to control rendering of a partial based on user input?
Update (2/5/2016):
This has been fixed as of Brakeman 3.0.3.
If the legal_partial? method is inlined like this:
def show
if %w(screenshots video updates).include? params[:partial]
#allowed_partial = params[:partial]
else
raise StandardError, "unexpected partial request: #{params[:partial]}"
end
end
Brakeman will be able to detect the guard condition and will no longer warn about the later render call.
Original answer:
Unfortunately, Brakeman does not know that if legal_partial? is a proper guard. All it knows is that params[:partial] is assigned to #allowed_partial, and that is then passed to render.
You may be able to tell that #allowed_partial will always be a safe value. At that point, you have to consider whether or not it makes sense to add complexity in order to make a tool happy.
Just as an example, you could do this:
def show
render_allowed_partial params[:partial]
end
def render_allowed_partial name
if %w(screenshots video updates).include? name
#allowed_partial = name
else
raise StandardError, "unexpected partial request: #{params[:partial]}"
end
end
It's basically the same thing, except now you are hiding the assignment of #allowed_partial from Brakeman.
(Warning: Not necessarily "best" way of doing this.)
Using brakeman 4.2.0
I had a similar issue trying to render a specific hand-positioned-and-named template. Every product of my app required that specific named template. The template name came from the controller params as params[:a_particular_slug].underscore.
I solved with something like this:
def show
if #products = Product.where(a_slug: params[:a_particular_slug])
render template: lookup_context.find(params[:a_particular_slug].underscore, ["featured_products"])
else
render_404
end
end
Here I'm looking for a template. If you need to use a partial, be aware that lookup_context.find third params set to true allows to search for partials.
You can find more about lookup_context.find here
Hope this helps.
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.
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?
SOLVED. See note 2 and 3.
I guess I just don't understand classes in rails 3 + ruby 1.9.2...
We're using actionmailer in conjunction with delayedjob. It all works fine EXCEPT I'm just trying to pretty-up some phone numbers and I'm trying to put a simple method pretty_phone (to format a phone number) SOMEWHERE that doesn't throw the error:
Class#sample_email failed with NoMethodError: undefined method `pretty_phone'
I've tried it in model_helper.rb, application_helper.rb, and in the class for what I guess is the model for our email foo_mailer.rb (FooMailer < ActionMailer::Base)
Our project setup is this:
app
controllers
widgets_controller.rb
helpers
application_helper.rb
widgets_helper.rb
mailer
foo_mailer.rb ***
models
widget.rb
views
widget
(usual edit,show, etc views)
foo_mailer
sample_email.html.haml ***
This is the simple method I'm trying to add:
# num is a string, always in format 12223334444 we want (222)333-4444
def pretty_phone(num)
return "(" + num[1,3] + ")" + num[4,3] + "-" + num[7,4]
end
foo_mailer.rb is very simple:
class FooMailer < ActionMailer::Base
helper :application **** THIS ALMOST MAKES IT WORK SEE NOTE 2
include ApplicationHelper **** AND THIS ALSO IS REQUIRED NOTE 3
default :from => "Support <support#mydomain.com>"
def sample_email(thewidget)
#widget = thewidget
send_to = #widget.contact_email
if !send_to.blank?
mail :to => send_to,
:subject => "alert regarding #{pretty_phone(#widget.userphone)}"
end
end
end
and down in our view for the email we also use #{pretty_phone(#widget.userphone)}
I'd really like to understand why I can't put that helper in application_helper or even foo_mailer.rb and have it work -- and where it SHOULD go?
(Currently I have the helper in application_help.rb and ALL of our widget erb views can use it fine... it's just the email view and/or the class file foo_mailer.rb that throw the error.)
NOTE2
by adding helper :application at the top of foo_mailer.rb NOW the pretty_phone() helper method in application_help.rb works in the foo_mailer VIEWs but NOT in the foo_mailer.rb itself. So for example where I want to to pretty_phone() in the subject line of the email it will not work. But in the actual emails (the views) it does work.
That just seems bizarre to me - any suggestions?
NOTE 3
Adding the 'include' AND the 'helper' is what was needed.
Where do I put helper methods for ActionMailer views?
i think this is exactly what you need.
the answer is posted as part of the question. thanks to all those whose comments led to finally figuring it out. I'm trying to "mark" this question as answered so people won't feel like they need to supply another answer, but somebody deleted my earlier answer. so I'm adding this back in again so in a day or two the system will let me flag this quesiton as answered.