I want to override how rails creates a view *.html.erb
In ActionView package I already Tried to do it. Doing it
class ERB
class Compiler # :nodoc:
..
class Buffer # :nodoc:
def compile(s)
...
#It stores in a buffer each ruby chunk in the views inside of a Buffer.
end
end
end
...
# Here it is where is called compile method.
# The thing is that if my view is made up of several *.html.erb files such as partials this method will be invoked each time.
#INVOKED PER EACH html.erb file
def initialize(str, safe_level=nil, trim_mode=nil, eoutvar='_erbout')
puts ">>> initialize"
#safe_level = safe_level
# ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.update(:default => "%d %b %Y")
compiler = ERB::Compiler.new(trim_mode)
# raise "need a block"
# ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.update(:default => nil)
set_eoutvar(compiler, eoutvar)
#src = compiler.compile(str)
#filename = nil
end
end
end
I would like to find out when the process on the bottom starts. I mean which class, file, etc I have to override to see where Rails starts invoking all html.erb for an specific view.
I think that I should be here:
require 'delegate'
require 'optparse'
require 'fileutils'
require 'tempfile'
require 'erb'
module Rails
module Generator
module Commands
class Create
# Generate a file for a Rails application using an ERuby template.
# Looks up and evaluates a template by name and writes the result.
#
# The ERB template uses explicit trim mode to best control the
# proliferation of whitespace in generated code. <%- trims leading
# whitespace; -%> trims trailing whitespace including one newline.
#
# A hash of template options may be passed as the last argument.
# The options accepted by the file are accepted as well as :assigns,
# a hash of variable bindings. Example:
# template 'foo', 'bar', :assigns => { :action => 'view' }
#
# Template is implemented in terms of file. It calls file with a
# block which takes a file handle and returns its rendered contents.
def template(relative_source, relative_destination, template_options = {})
puts "EEEEEEEEEEEEEEEEEEEEEe"
file(relative_source, relative_destination, template_options) do |file|
# Evaluate any assignments in a temporary, throwaway binding.
vars = template_options[:assigns] || {}
b = template_options[:binding] || binding
vars.each { |k,v| eval "#{k} = vars[:#{k}] || vars['#{k}']", b }
# Render the source file with the temporary binding.
ERB.new(file.read, nil, '-').result(b)
end
end
end
end
end
end
But I do not find any trace from the puts method.
All renaming are placed in a file called /lib/*_extensions.erb and in /config/initializers/extensions.rb I have the next:
Dir[File.dirname(__FILE__) + "/../../lib/*_extensions.rb"].each do |fn|
require fn
end
I do not want to reveal why I am doing this.
Thanks
I wanted to use a before_render it does not exist at least in Rails 2.3.4.
So if you want to do it, I did as the next:
class ApplicationController < ActionController::Base
helper :all # include all helpers, all the time
protect_from_forgery # See ActionController::RequestForgeryProtection for details
# Scrub sensitive parameters from your log
# filter_parameter_logging :password
protected
def render(options = nil, extra_options = {}, &block) #:doc:
puts "BEFORE render"
puts "Setting %H:%M:%S"
ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!(:default => "%H:%M:%S")
# call the ActionController::Base render to show the page
super
puts "AFTER render"
puts "Resetting"
ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!(:default => nil)
end
end
Related
I am getting `const_missing': uninitialized constant Disc::Foo1 (NameError) while executing the code
disc.rb
module Disc
MAPPING = {
:new => {
:first => Foo1,
:second => Foo2
},
:old => {
:third => Foo3,
:fourth => Foo4
}
def run
# :new and :first is example but will be any value decided based on the logic dynamically.
cls = MAPPING[:new][:first]
cls.new.execute
end
}
end
foo1.rb
class Foo1
def initialize
end
def execute
puts "f1"
end
end
foo2.rb
class Foo2
def initialize
end
def execute
puts "f2"
end
end
foo3.rb
class Foo3
def initialize
end
def execute
puts "f3"
end
end
foo4.rb
class Foo4
def initialize
end
def execute
puts "f4"
end
end
I am new to ruby. I tried to find the solution and found that I have to add require clause and tried many things but didn't work.
Can someone please help me out here?
Ruby can't autoload files — you need to explicitly tell it to load them. Just by sitting in the same directory they will not do anything. This is because, unlike some other languages, there is no predictable set mapping between file names and their contents, so Ruby can't know where the class you wanted is. You have to
require './foo1'
require './foo2'
require './foo3'
require './foo4'
Alternately, you could do it a bit smarter:
%w(./foo1 ./foo2 ./foo3 ./foo4).each do |file|
require file
end
Now that you know about require... Saying that Ruby can't autoload files was a lie to tell people new to Ruby. Ruby can totally autoload files. But you still have to tell it what they are and where to find them. Just by sitting in the same directory they will not do anything. You have to
autoload(:Foo1, './foo1.rb')
autoload(:Foo2, './foo2.rb')
autoload(:Foo3, './foo3.rb')
autoload(:Foo4, './foo4.rb')
The files will not be loaded, unless you try to access the named classes; when you do so, Ruby will remember you told it where to find those classes, and load the corresponding file. This saves the program start time and memory, as only the actually needed files will be loaded.
You can also do this instead:
# for each `./foo*.rb` file
Dir.glob('./foo*.rb').each do |file|
# turn it into the class name; e.g. `./foo1.rb' -> :Foo1
classname = file[%r{\./(.*)\.rb$}, 1].capitalize.to_sym
# then tell Ruby that that classname can be found in that file
autoload(classname, file)
end
However, since you explicitly have the classes in your MAPPING, autoloading will not help you — all of the foo files will be autoloaded at the moment MAPPING is defined, so there is no benefit to using autoload here. If you stored names instead of class objects themselves, and used Object.const_get to look up the class object constant, then it would work:
autoload(:Foo1, './foo1.rb')
# `./foo1.rb` is not yet loaded
MAPPING = {
new: {
first: :Foo1,
# ...
},
# ...
}
# `./foo1.rb` is still not loaded:
# we haven't used the `Foo1` constant yet
# (we did use `:Foo1` symbol, but that is a completely different thing)
cls = Object.const_get(MAPPING[:new][:first])
# `./foo1.rb` is now loaded, and `cls` is `Foo1`
I have a case, when the text from the DB field should be "evaled" in the sandbox mode - with whitelist of methods and constants, allowed to invoke.
Gem https://github.com/tario/shikashi fits to this perfectly, but it seems to me, that it's abandoned.
I even can't use run Basic Example 2 (only Basic Example 1 works fine):
require "rubygems"
require "shikashi"
include Shikashi
def foo
# privileged code, can do any operation
print "foo\n"
end
s = Sandbox.new
priv = Privileges.new
# allow execution of foo in this object
priv.object(self).allow :foo
# allow execution of method :times on instances of Fixnum
priv.instances_of(Fixnum).allow :times
#inside the sandbox, only can use method foo on main and method times on instances of Fixnum
s.run(priv, "2.times do foo end")
Because it fails with an Error Cannot invoke method foo on object of class Object (SecurityError)
This gem uses another gem evalhook that looks for me complicated in order to fix the issue. There are another gems like this one but they are even more abandoned.
As far as I understood using $SAFE is not a good idea, because it has vulnerabilities.
Are there another approaches for such feature? Maybe manipulating with Binding object?
My problem was not so hard to solve without any gems. The idea is that you have whitelist of methods and constants WHITELIST and this class checks, if all methods and constants are in the whitelist
# gem install 'parser'
require 'parser/current'
class CodeValidator
attr_reader :errors, :source
WHITELIST = {:send => [:puts, :+, :new], :const => [:String]}
class Parser::AST::Node
def value
return children[1] if [:send, :const].include? type
fail NotImplementedError
end
end
def initialize(source)
#errors = []
#source = source
end
def valid?
!insecure_node?(root_node)
end
private
def exclude_node?(node)
blacklisted_node_types.include?(node.type) && !WHITELIST[node.type].include?(node.value)
end
def blacklisted_node_types
WHITELIST.keys
end
def insecure_node?(node)
return !!add_error_for_node(node) if exclude_node?(node)
node.children.each { |child_node| return true if child_node.class == Parser::AST::Node && insecure_node?(child_node) }
false
end
def root_node
#root_node ||= Parser::CurrentRuby.parse source
end
def add_error_for_node(node)
errors << "#{node.type} not allowed: #{node.value}"
end
end
c = CodeValidator.new("s = 'hello ' + String.new('world'); puts s.inspect")
p c.valid? # => false
p c.errors # => ["send not allowed: inspect"]
I have several of my partials in a folder named partials, and I render them into my view using render 'partials/name_of_my_partial' and that's okay.
Anyhow, is it possible to set up things in a way than I could just use render 'name_of_my_partial' and rails automatically check this partials folder?
Right now, I'm having a Missing partial error.
In rails 3.0 this a bit of a challenge, but looking into it I found that in rails 3.1, they've changed how path lookup works, making it much simpler. (I don't know what exact version they changed it though, it may have been much earlier).
In 3.1, this is relatively simple because they've introduced a way to send multiple prefixes to the path lookup. They are retrieved via the instance method _prefixes.
It's easy to tack on an arbitrary prefix to this, for all controllers, by simply overriding it in the base controller (or in a module you include in your base controller, whichever).
So in 3.1.x (where lookup uses multiple prefixes):
class ApplicationController
...
protected
def _prefixes
#_prefixes_with_partials ||= super | %w(partials)
end
end
Prior to this change, a single prefix was used for lookup, which made this a lot more complicated. This may have not been the best way, but I solved this problem in the past by rescuing from missing template errors with an attempt to look up the same path with my "fallback" prefix.
In 3.0.x (where lookup uses a single path prefix)
# in an initializer
module YourAppPaths
extend ActiveSupport::Concern
included do
# override the actionview base class method to wrap its paths with your
# custom pathset class
def self.process_view_paths(value)
value.is_a?(::YourAppPaths::PathSet) ?
value.dup : ::YourAppPaths::PathSet.new(Array.wrap(value))
end
end
class PathSet < ::ActionView::PathSet
# our simple subclass of pathset simply rescues and attempts to
# find the same path under "partials", throwing out the original prefix
def find(path, prefix, *args)
super
rescue ::ActionView::MissingTemplate
super(path, "partials", *args)
end
end
end
ActionView::Base.end(:include, YourAppPaths)
I haven't been able to find other resources than this SO question on this topic, so I'm posting about my efforts here.
In Rails 3.2+ (Also tested in 4.2), one can access and modify lookup_context.prefixes from within a controller action. So, to modify lookup of template & partial prefixes to include another path you can do this:
class MyObjectsController < ApplicationController
def show
# WARNING: Keep reeding for issues with this approach!
unless lookup_context.prefixes.first == "partials"
lookup_context.prefixes.prepend "partials"
end
end
end
This way, if there is a show.html.erb template in the app/views/partials/ folder then it will be rendered. And the same goes for any partials referenced in show.html.erb.
What's going on here?
The method lookup_context returns an object of type ActionView::LookupContext, which is the object responsible to hold all information required to lookup templates in ActionView. Of note, it also gives you access to lookup_context.view_paths, which is not what is being asked for in this question but sounds like it should be.
WARNING
Modification of the lookup_context.prefixes array is cached for all future requests. Therefore, in order to use it problem-free, it's best to make sure we also remove any prefixes we add.
So, is there an easy way to do this?
Sure. For my own projects, I've created a module which I can include in any controller that needs this ability (or just include it in ApplicationController). Here's my code:
# Helper methods for adding ActionView::LookupContext prefixes on including
# controllers. The lookup_context.prefixes collection is persisted on cached
# controllers, so these helpers take care to remove the passed-in prefixes
# after calling the block.
#
# #example Expected Usage:
# around_action only: :show do |_, block|
# prepend_lookup_context_prefixes("my_optional_name_space/my_objects", &block)
# end
#
# around_action only: %i[index edit update] do |_, block|
# append_penultimate_lookup_context_prefixes("my_optional_name_space/my_objects", &block)
# end
module PrefixesHelper
# Prepends the passed in prefixes to the current `lookup_context.prefixes`
# array, calls the block, then removes the prefixes.
#
# #param [Array<String>] prefixes
def prepend_lookup_context_prefixes(*prefixes, &block)
lookup_context.prefixes.prepend(*prefixes)
block.call
remove_lookup_context_prefixes(*prefixes, index: 0)
end
# Sets the penultimate (2nd-to-last) prefixes in the current
# `lookup_context.prefixes` array, calls the block, then removes the prefixes.
#
# #param [Array<String>] prefixes
def append_penultimate_lookup_context_prefixes(*prefixes, &block)
lookup_context.prefixes.insert(-2, *prefixes)
block.call
remove_lookup_context_prefixes(*prefixes.reverse, index: -2)
end
# Removes the passed in prefixes from the current `lookup_context.prefixes`
# array. If index is passed in, then will only remove prefixes found at the
# specified index in the array.
#
# #param [Array<String>] prefixes
# #param [Integer] index
def remove_lookup_context_prefixes(*prefixes, index: nil)
prefixes.each do |prefix|
if index
if lookup_context.prefixes[index] == prefix
lookup_context.prefixes.delete_at(index)
end
else
lookup_context.prefixes.delete(prefix)
end
end
end
end
As mentioned in the comments in this module, the expected usage for this module is to call the methods contained within via an around_filter call in your controller. This way, the module will take care of removing any prefixes it adds after the controller action has yielded.
For example:
around_filter only: :show do |_, block|
prepend_lookup_context_prefixes("my_optional_name_space/my_objects", &block)
end
Or:
around_filter only: %i[index edit update] do |_, block|
append_penultimate_lookup_context_prefixes("my_optional_name_space/my_objects", &block)
end
I've also included the PrefixesHelper module posted here into a gem that I use to add a few nice extensions such as these to my Rails apps. In case you'd like to use it too, see here: https://github.com/pdobb/core_extensions
Note: I've abandoned this approach due to some weird behavior and incompatibility with the multi_fetch_fragments gem
If you'd like to prefix your partials for an STI collection, I've come up with the following monkey patch (for Rails 3.2, Ruby 2.2+).
config/initializers/partial_renderer_prefix.rb
module ActionView
class PartialRenderer
def merge_prefix_into_object_path(prefix, object_path)
# Begin monkey patch
if #options.key?(:prefix)
prefixes = [#options[:prefix]]
return (prefixes << object_path).join("/")
end
# End monkey patch
if prefix.include?(?/) && object_path.include?(?/)
prefixes = []
prefix_array = File.dirname(prefix).split('/')
object_path_array = object_path.split('/')[0..-3] # skip model dir & partial
prefix_array.each_with_index do |dir, index|
break if dir == object_path_array[index]
prefixes << dir
end
(prefixes << object_path).join("/")
else
object_path
end
end
end
end
In a view, use it like this:
<%= render #order_items, prefix: 'orders/timeline' %>
Where you have app/views/orders/timeline/product_order_items/_product_order_item.html.erb and app/views/orders/timeline/subscription_order_items/_subscription_order_item.html.erb partials available for each of your STI models.
Would anyone be kind enough to dissect the merge! method? Its usage of conditions and variable assignment looks rather terse, and I'm having a difficult time following it. Would love to hear a Ruby-savvy developer break this apart.
module ActiveRecord
class Errors
def merge!(errors, options={})
fields_to_merge = if only=options[:only]
only
elsif except=options[:except]
except = [except] unless except.is_a?(Array)
except.map!(&:to_sym)
errors.entries.map(&:first).select do |field|
!except.include?(field.to_sym)
end
else
errors.entries.map(&:first)
end
fields_to_merge = [fields_to_merge] unless fields_to_merge.is_a?(Array)
fields_to_merge.map!(&:to_sym)
errors.entries.each do |field, msg|
add field, msg if fields_to_merge.include?(field.to_sym)
end
end
end
end
module ActiveRecord
class Errors
def merge!(errors, options={})
# This method has two major sections.
# 1. Figure out which fields we want to merge, based on the options.
# First check to see if we're supposed to whitelist or blacklist
# any fields.
# fields_to_merge is simple being assigned the return value of
# the if/elsif/else block.
#
fields_to_merge = if only=options[:only]
# Whitelist
# Implies that options[:only] looks like :only => :foo, but that
# seems short-sighted and should probably be refactored to work
# like the blacklist, complete with to_sym conversions.
only
elsif except=options[:except]
# Blacklist
# You can pass in a single field or an array.
# :except => [:foo, :bar, :yourmom] or :except => :foo
# In the latter case, this is about to be converted to [:foo] so that
# it's an array either way.
except = [except] unless except.is_a?(Array)
# For each array element, convert that element to a symbol.
# "foo" becomes :foo
# This is a short hand for "map" which is equivalent to the following:
# except.map!{|field| field.to_sym}
except.map!(&:to_sym)
# The "entries" method comes from Enumerable and works like to_a.
# In the case of a hash, you end up with an array of arrays.
# Assuming there are already errors, the output of that will look
# something like:
# [['user', 'can't be blank'], ['some_other_field', 'has problems']]
#
# Mapping this to :first means "send the 'first' message to each array
# element. Since the elements are themselves arrays, you end up
# with ['user', 'some_other_field']
#
# Now we do a 'select' on that array, looking for any field names that
# aren't in our 'except' array.
errors.entries.map(&:first).select do |field|
!except.include?(field.to_sym)
end
else
# No filters. Just dump out an array of all field names.
errors.entries.map(&:first)
end
# Make sure that we have an array, which at this point is only possible
# if we're using an :only => :foo.
fields_to_merge = [fields_to_merge] unless fields_to_merge.is_a?(Array)
# Make sure that they are all symbols (again with the :only, see the
# note above about refactoring).
fields_to_merge.map!(&:to_sym)
# 2. Now we know what fields we care about, yank in any errors with a
# matching field name from the errors object.
errors.entries.each do |field, msg|
# 'add' is a method on ActiveRecord::Errors that adds a new error
# message for the given attribute (field).
# In this case, any error from the errors object passed into this
# method with a field name that appears in the 'fields_to_merge'
# array is added to the existing errors object.
#
add field, msg if fields_to_merge.include?(field.to_sym)
end
end
end
end
In Ruby on Rails, where does one put the code from this snippet in http://gist.github.com/376389? I want to extend ActiveRecord::Errors with the code that's available there so I can merge error messages.
Is this something for ApplicationController? or for lib?
Paste from github.com
# monkey patch gleaned from http://dev.rubyonrails.org/attachment/ticket/11394/merge_bang_errors.patch
module ActiveRecord
class Errors
def merge!(errors, options={})
fields_to_merge = if only=options[:only]
only
elsif except=options[:except]
except = [except] unless except.is_a?(Array)
except.map!(&:to_sym)
errors.entries.map(&:first).select do |field|
!except.include?(field.to_sym)
end
else
errors.entries.map(&:first)
end
fields_to_merge = [fields_to_merge] unless fields_to_merge.is_a?(Array)
fields_to_merge.map!(&:to_sym)
errors.entries.each do |field, msg|
add field, msg if fields_to_merge.include?(field.to_sym)
end
end
end
end
You can just drop that into any ruby file in your initializers directory (create a new one, name it whatever you want). Rails runs all of the files in there every time it is booted, and will extend Errors at that time.
This question is stale but if you want to do something similar in rails 3.x you can drop this in a file in your config/initializers/ directory:
# monkey patch gleaned from http://dev.rubyonrails.org/attachment/ticket/11394/merge_bang_errors.patch
module ActiveModel
class Errors
def merge!(errors, options={})
return if errors.blank? || errors.first.size < 2
fields_to_merge = if only=options[:only]
only
elsif except=options[:except]
except = [except] unless except.is_a?(Array)
except.map!(&:to_sym)
errors.entries.map(&:first).select do |field|
!except.include?(field.to_sym)
end
else
errors.entries.map(&:first)
end
fields_to_merge = [fields_to_merge] unless fields_to_merge.is_a?(Array)
fields_to_merge.map!(&:to_sym)
errors.entries.each do |field, msg|
add field, msg if fields_to_merge.include?(field.to_sym)
end
end
end
end