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
Related
I have several functions in my model
#app/model/game.rb
...
def uncompress_game_files_to_s3
UncompressToS3Job.perform_now(self.files, "assets/#{self.id}/game") if self.files
end
def delete_game_files_from_s3
DeleteFromS3Job.perform_now("assets/#{self.id}/game")
end
def update_game_index_file_url
files = FindFilesOnS3Job.perform_now("index.html", "assets/#{self.id}/game")
self.update_attributes(url: files.first)
end
In all these functions, I use "assets/#{self.id}/game" for S3 key attribute. I would like to use this expression as global variable aws_game_path.
I tried to initialize it in initializer file.
#config/initializers/aws.rb
aws_game_path = "assets/#{self.id}/game"
but since it is out of the model scope, it raises an error undefined method `id'. How can I declare such variable?
I think ActiveSupport::Concern Would be best practice in this case.
Follow the below step to make available to all the model
1: Create a rb lets say active_record_global_var.rb extension file in lib as lib file load first.
2: paste the following piece of code.
module ActiveRecordExtension
extend ActiveSupport::Concern
def set_global_valiable
set_global_id = self
end
module ClassMethods
end
end
ActiveRecord::Base.send(:include, ActiveRecordExtension)
3: Create a file in config/initializers/ directory. Let say extensions.rb and put this code snipped into this file.
4: require "active_record_global_var"
5: Now call set_global_variable instances method on model object. This method would be available for all model.
For example:
User.last.set_global_valiable.id gives you the id for user
CbResume.last.set_global_valiable.id same as for CbResume Model
hope this help you !!!
If you're only using it from that model, it doesn't need to be global. Can be just a private method:
def update_game_index_file_url
files = FindFilesOnS3Job.perform_now("index.html", game_path)
self.update_attributes(url: files.first)
end
private
def game_path
"assets/#{id}/game"
end
How can I declare such varaible.
Variables don't behave this way. It has to be a method on some object. A shared one, if you want to use this logic outside of the model too. Something like this, perhaps:
module PathHelpers
def self.game_path(game)
"assets/#{game.id}/game"
end
end
class Game
def update_game_index_file_url
files = FindFilesOnS3Job.perform_now("index.html", PathHelpers.game_path(self))
end
end
I'm trying to create a module that will be included in many different classes. It needs to record the caller's path to the class file
so I can reference the path in later code. This code tries to add a method to the calling class, but fails because it just returns the current value of ##x.
# /home/eric/FindMe.rb
class FindMe
include GladeGUI
end
# /home/eric/GladeGUI.rb
module GladeGUI
def self.included(obj)
##x, = caller[0].partition(":") # this works ##x = "/home/eric/FindMe.rb"
obj.class_eval do
def my_class_file_path
return ????? # I want to return "/home/eric/FindMe.rb"
end
end
end
end
The GladeGUI module will be "included" in many different classes, so I can't just add code to the calling class. I need a way to make ##x compile into a constant value, so the method stored in the class looks like this:
def my_class_file_path
return "/home/eric/FindMe.rb"
end
How do I convert a variable to a constant in code?
Thanks.
It seems like you don't actually need it to be a "constant" - you just need some way to make the method return the correct value all the time and not allow other code to come along and change the value (with the current ##x solution, someone can just modify ##x and it will break)
The solution is to store the data in a local variable instead of a class or instance variable, and then access that local variable via a closure.
No other code will have scope to 'see' the local variable and thus it cannot be changed.
But then the problem becomes that when you use def inside a class_eval, the scope of the caller isn't captured, so the code can't see your local variable. You can use define_method instead
Here's an example
# /home/eric/GladeGUI.rb
module GladeGUI
def self.included(obj)
caller_file_path = caller[0].split(":").first
obj.class_eval do
define_method :my_class_file_path do
return caller_file_path
end
end
end
end
# /home/eric/FindMe.rb
class FindMe
include GladeGUI
end
puts FindMe.new.my_class_file_path # prints the correct path
But - what if you want my_class_file_path to be a class method rather than an instance method - use define_singleton_method instead:
module GladeGUI
def self.included(obj)
caller_file_path = caller[0].split(":").first
obj.class_eval do
define_singleton_method :my_class_file_path do
return caller_file_path
end
end
end
end
...
puts FindMe.my_class_file_path
Interesting side note: This is how you can fake "private variables" in javascript :-)
module MyModule
def self.my_method
#instance_variable ||= "some string"
end
end
Consider another module OtherModule that includes MyModule. When code in OtherModule calls my_method multiple times will #instance_variable be instantiated multiple times? Or just once?
updated answer:
if the delegate module call the method, you will get an error.
if the original module call the method, the class instance variable only instanced 1 time.
if you want to define "class methods" in an module, you can't define self.xx then simply extend/include it. you should either "extend" it, or "include it to the eigen class", e.g.
module SomeModule
def self.aim_to_be_class_method ; end;
def aim_to_be_instance_method ; end;
end
# class methods: []
# instance methods: ["aim_to_be_instance_method"]
class SomeClassIncludingTheModule
include SomeModule
end
# class methods: ["aim_to_be_instance_method"]
# instance methods: []
class SomeClassExtendingTheModule
extend SomeModule
end
# class methods: ["aim_to_be_instance_method"]
# instance methods: []
class SomeClassMadeEigenClassIncludingModule
class << self
include SomeModule
end
end
your code is an example of "class instance variables". according to the book << metaprogramming ruby>>, page 127, you can consider "class instance variables" as Java's static fields. so, I think most of the case, it should run once.
for more details, please write an unit test and see what happens? I will update my answer after a short while with my own unit test code written.
UPDATED: my unit test and result:
# all of the code is placed in a file: class_instance_variable_test.rb
# define a class so that we see its initialize process
class Apple
def initialize
puts "Apple initialized~"
end
end
# define an original_module that calls Apple.new
module OriginalModule
def self.original_method
#var ||= Apple.new
end
end
# define another module to call the original_module
module DelegateModule
include OriginalModule
end
# now the test begins
require 'test/unit'
class ClassInstanceTest < Test::Unit::TestCase
# output of this test case:
# NoMethodError: undefined method `original_method' for DelegateModule:Module
def test_if_delegate_module_could_call_original_method_by_including_original_module
DelegateModule.original_method
end
# output of this test case:
# ----- calling from original_method, 3 times called, how many times initialized?
# Apple initialized~
# ------ ends
def test_if_we_could_call_original_method_from_original_module
puts "----- calling from original_method, 3 times called, how many times initialized? "
OriginalModule.original_method
OriginalModule.original_method
OriginalModule.original_method
puts "------ ends "
end
end
How can I make this method, which outputs a yellow line in the log file, accessible from everywhere (Models, Controllers, Views) in my Rails app?
def my_log(text, file = "", line = "")
text.to_s.chomp.gsub!(/%/, "%%")
Rails.logger.debug(sprintf("\033[32m#{file}#{line}\033[0m\033[1m\033[33m#{text}\033[0m"))
end
You could define it in Kernel (NOT recommended):
module Kernel
def my_log(..)
..
end
end
... if you really want it available anywhere.
Or, place something like this in lib/util.rb:
module Util
def self.my_log(..)
..
end
end
... and make sure to require 'util' in your config/application.rb and then you can call this anywhere:
Util.my_log(..)
why not create an initializer and write this method to the rails module?
# config/initializers.rb
module Rails
def self.log_with_colour(message, level = :debug)
text.to_s.chomp.gsub!(/%/, "%%")
logger.send(level, sprintf("\033[32m#{__FILE__}#{__LINE__}\033[0m\033[1m\033[33m#{message}\033[0m"))
end
end
in your code you can then call Rails.log_with_colour("hello") or Rails.log_with_colour("Hello again", :info)
I put stuff like this in config/initializers/app_methods.rb. They don't need to be scoped inside a class or module. Feels a bit hacky but i never had any problems.
Add it as an instance and class method in Object
class Object
def self.my_log(...)
...
end
def my_log(...)
Object.my_log(...)
end
end
I created a file so I can share a method amongst many models in lib/foo/bar_woo.rb. Inside of bar_woo.rb I defined the following:
module BarWoo
def hello
puts "hello"
end
end
Then in my model I'm doing something like:
def MyModel < ActiveRecord::Base
include Foo::BarWoo
def some_method
Foo::BarWoo.hello
end
end
The interpreter is complaining that it expected bar_woo.rb to define Foo::BarWoo.
The Agile Web Development with Rails book states that if files contain classes or modules and the files are named using the lowercase form of the class or module name, then Rails will load the file automatically. I didn't require it because of this.
What is the correct way to define the code and what is the right way to call it in my model?
You might want to try:
module Foo
module BarWoo
def hello
puts "hello"
end
end
end
Also for calling you won't call it with Foo::BarWhoo.hello - that would have to make it a class method. However includeing the module should enable you to call it with just hello.
Files in subdirectories of /lib are not automatically require'd by default. The cleanest way to handle this is to add a new initializer under config/initializers that loads your library module for you.
In: config/initializers/load_my_libraries.rb Pick whatever name you want.
require(File.join(RAILS_ROOT, "lib", "foo", "bar_woo"))
Once it has been require'd, you should be able to include it at will.
The issue is twofold.
You need to use the outer Foo scope to define BarWoo
You have defined hello as an instance method, then tried to call it on the class.
Define your method using def self.hello instead of def hello
module Foo
module BarWoo
def self.hello
puts "hello"
end
end
end
You can also do
module Foo::Barwoo; end;