I have a module and which have some files under the module
module Man
which has some five files called
module man
module head
def a
end
end
end
module man
module hand
def a
end
end
end
I need to access list of sub-modules that are under the module 'man' and also I need to access the list of methods in each sub-modules.
I tried doing this
array_notification_classes = Dir.entries("app/models/notifications").select {|f| !File.directory? f}
But it returned a list of submodules which is a string.
array_notification_classes = ["head.rb", "hand.rb"]
From now how should I get the list of method names from each sub-module?
Having an array of file names, e.g. array_notification_classes = ["head.rb", "hand.rb"]
array_notification_classes.each do |file_name|
require file_name
include file_name.split(".").first.classify.constantize
end
or into a class:
class Notification
end
array_notification_classes.each do |file_name|
require file_name
Notification.class_eval do
include file_name.split(".").first.classify.constantize
end
end
My array_notification_classes = ["head.rb", "hand.rb"]
array_notification_classes.each do |notification_class|
notification_class = "Notifications::#{notification_class[0..-4].classify}".constantize
notification_class_methods = notification_class.instance_methods
end
This returned all the instance methods in Notifications::Head, notifications::Hand
Related
I’m using Ruby on Rails 6.1.4.4. I want to override a method in a gem, whose signature is this
module Juixe
module Acts
module Commentable
module ClassMethods
def acts_as_commentable(*args)
…
end
end
end
end
end
So I tried creating a file, lib/ext/acts_as_commentable_extensions.rb, and including this code
require 'acts_as_commentable'
module GemExtensions
module Commentable
def hello
print "hello\n"
end
def acts_as_commentable(*args)
abcdef
end
end
end
module Juixe
module Acts
module Commentable
module ClassMethods
include GemExtensions::Commentable
end
end
end
end
Juixe::Acts::Commentable::ClassMethods.instance_method(:hello).source.display
Juixe::Acts::Commentable::ClassMethods.instance_method(:acts_as_commentable).source.display
Although the first statement prints out the correct source code from my new method “hello,” the second prints out the old source code from the original gem as opposed to my new code. How do I override this method with my own code?
Try prepend GemExtensions::Commentable instead of include, it will make Ruby to search for the method first in prepended module. More explanation here https://medium.com/#leo_hetsch/ruby-modules-include-vs-prepend-vs-extend-f09837a5b073
The following code is meant to determine which subclass of itself to execute. It cycles through everything in the ObjectSpace to find subclasses and then execute the correct one from there. In rails, this does not work, because classes in the library folder are not in the ObjectSpace. What is a way to search through a specific folder for subclasses?
def execute
ObjectSpace.each_object(Class).select do |klass|
if (klass < self.class)
klass.designations.each do |designation|
if (designation.downcase.capitalize == #action.downcase.capitalize)
command = klass.new(#sumo_params)
command.execute
end
end
end
end
end
OR -- Is there a superior solution to this problem that you would recommend?
I'd say use a module for this. So, this would make you code something like the following:
lib/base_methods.rb
module BaseMethods
def self.included(base)
##classes_including ||= []
##classes_including << base
end
def do_whatever
##classes_including.each do |class|
#....
end
end
end
lib/class_1.rb
class Class1
include BaseMethods
end
I took the easy way out for now (iterating through an array of string values):
def execute
#descendants.each do |descendant|
klass = descendant.downcase.capitalize.constantize
if (klass < self.class)
klass.designations.each do |designation|
if (designation.downcase.capitalize == #action.downcase.capitalize)
command = klass.new(#sumo_params)
command.execute
end
end
end
end
end
I have created a new library file sampler.rb inside the lib folder. Consider this as the content of the file
module Sampler
def sample_tester
"test"
end
end
I have included it in the application_controller and added a require statement in the config\initializers. When I try to access the method sample_tester from my controllers, I get the following error
undefined local variable or method `sample_tester` for #<BlogsController:0xb8fbac8>
Am I missing something?
Since it doesn't look like you are creating an instance of this, my first guess is that you need to define it as a class method so that it can be called like this: Sampler.sample_tester.
In your file you could do it one of two ways:
# first way
module Sampler
def self.sample_tester
"test"
end
end
# second way
module Sampler
class << self
def sample_tester
"test"
end
end
The second way is nicer if you want to define a number of class methods.
if you want to have your module method defined as a class method you need to use extend instead of include:
module Mod
def bla
puts "bla"
end
end
class String
include Mod
end
String.bla rescue puts $! # => undefined method `bla' for String:Class
class String
extend Mod
end
puts String.bla # => bla
I have a setup in the lib directory like so:
lib/
copy_process.rb
copy_process/
processor.rb
The copy_process.rb and processor.rb contain the module definition CopyProcess. The copy_process.rb defines the CopyFile class as well:
module CopyProcess
class CopyFile
end
end
The processor.rb is structured like so:
module CopyProcess
class Processer
end
end
In one of its methods, it creates a new copy file object:
def append_file_if_valid(file_contents, headers, files, file_name)
unless headers
raise "Headers not found"
else
files << CopyProcess::CopyFile.new()
end
end
When I used these files as part of a command line ruby program, it worked fine. However, i started putting it into a rails app, and I have written cucumber/capybara tests to hit the buttons and so forth where this is used. I initialize a Processor object from one of my AR models, and call the above method a few times. It cannot seem to find the CopyFile class, even though I have the following code in my application.rb
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
Any ideas?
===============================================================
Edit Above was solved by extracting the copy file class into it's own file under lib.
I now have another issue:
The CopyFile class refers to module-level helper methods that sit in lib/copy_process.rb, like so:
module CopyProcess
# Gets the index of a value inside an array of the given array
def get_inner_index(value, arr)
idx = nil
arr.each_with_index do |e, i|
if e[0] == value
idx = i
end
end
return idx
end
def includes_inner?(value, arr)
bool = false
arr.each { |e| bool = true if e[0] == value }
return bool
end
# Encloses the string in double quotes, in case it contains a comma
# #param [String] - the string to enclose
# #return [String]
def enclose(string)
string = string.gsub(/\u2019/, '’')
if string.index(',')
return "\"#{string}\""
else
return string
end
end
end
When I run my cucumber tests, i get the following error:
undefined method `includes_inner?' for CopyProcess:Module (NoMethodError)
./lib/copy_process/copy_file.rb:64:in `set_element_name_and_counter'
Which refers to this method here:
def set_element_name_and_counter(element_names, name)
if !CopyProcess::includes_inner?(name, element_names)
element_names << [name, 1]
else
# if it's in the array already, find it and increment the counter
current_element = element_names[CopyProcess::get_inner_index(name, element_names)]
element_names[CopyProcess::get_inner_index(name, element_names)] = [current_element[0], current_element[1]+1]
end
element_names
end
I also tried moving the copy_file.rb and other files in the lib/copy_process/ directory up a level into the lib directory. I then received the following error:
Expected /Users/aaronmcleod/Documents/work/copy_process/lib/copy_file.rb to define CopyFile (LoadError)
./lib/processor.rb:48:in `append_file_if_valid'
The line that the error states creates an instance of CopyFile. I guess rails doesn't like loading the files in that fashion, and for the former setup, I think the copy_file.rb is having issues loading the rest of the module. I tried requiring it and so forth, but no luck. You can also find my most recent code here: https://github.com/agmcleod/Copy-Process/tree/rails
First config.autoload_paths += %W(#{config.root}/lib) should be sufficient. This tells rails to start looking for properly structured files at /lib.
Second, I think that you're running into issues because CopyFile isn't where rails expects it to be. As far as I know your setup 'should' work but have you tried seperating CopyFile out into its own file under the copy_process folder? My guess is that since the copy_process folder exists, it is expecting all CopyProcess::* classes to be defined there instead of the copy_process.rb.
EDIT: You may consider opening another question, but the second half of your question is a different problem entirely.
You define methods in your module like so,
module X
def method_one
puts "hi"
end
end
Methods of this form are instance methods on the module, and they have very special restrictions. For instance, you can not access them from outside the module definition (I'm skeptical how these worked previously). Executing the above gives
> X::method_one
NoMethodError: undefined method `method_one' for X:Module
If you want to access these methods from other scopes you have a few options.
Use Class Methods
module X
def self.method_one
puts "hi"
end
end
X::hi #=> "hi"
Use Mixins
module X
module Helpers
def method_one
puts "hi"
end
end
end
class CopyFile
include X::Helpers
def some_method
method_one #=> "hi"
self.method_one #=> "hi"
end
end
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