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
Related
Guys today I'm trying to create global method for all my project models in rails 4
I created something like that under this path lib/query.rb
module Query
def custom my_query
self.where(my_query)
end
end
then added this code in this file lib/application.rb to allow rails to load the files under this path
# Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += %W(#{config.root}/lib)
then included my method in my model by using this command
include Query
now should every thing ready to use my custom method , but when I tried to call my method in the controller like that
def index
#users= Users.custom(params[:query])
end
I got the error
undefined method `custom'
what I should do now ??
why i got this error ??
I think you should use concern for your module. Add your file in app/models/concerns.
# app/models/concerns/query.rb
module Query
extend ActiveSupport::Concern
included do
#you can use a scope
scope :my_query, ->(just_a_param){ .... }
end
module ClassMethods
#or a method
def self.another_query
where(....)
end
end
end
Of course you need to include the module in your model. As concern erd default in rails, you no longer need to change config autoload paths.
As a class method, you'll need the "self."
def self.custom my_query
self.where(my_query)
end
EDIT: If you want this in all ActiveRecord models, you can add it as an initializer
#config/initializers/active_record_extensions.rb
class ActiveRecord::Base
def self.custom my_query
self.where(my_query)
end
end
If you just want this on a single class, a concern would work.
In your example, there is no reference given between your class Users and your method custom. First: if Users refers to a Ruby on Rails class it is probably called User (see also comment of japed). So change the call. Next, your User class must inherited from ActiveRecord else it would not be aware of the existence of 'where'. For details check your app/models/user.rb
Then Swards' suggestion should work for you. Stop your application and restart. Now it should work.
Guys I found the true way to make it
First my impropriety was the include that I set in the model
It should be extend Query
then it will work well
so the true code will be
create your method file under this path lib/query.rb
then set this code in it
module Query
def custom my_query
self.where(my_query)
end
end
then added this code in this file lib/application.rb
# Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += %W(#{config.root}/lib)
then extend the method in the model by using this command
extend Query
and in your controller query you can use the method like that
def index
#users= Users.custom(params[:query])
end
This is my solution, not exactly the 'Rails way', but using some sort of decorator pattern:
#user = CustomQuery.find_for(User.find(params[:search])).perform!
class CustomQuery
attr_reader :params, :klass
def initialize(klass)
#params = params
#klass = klass
end
def self.find_for(params)
CustomQuery.new(params)
find_model_for(params.tap {})
end
def perform!
return params unless params.nil?
klass.all
end
def find_model_for(klass)
#klass = klass
end
end
While I'm not sure about the process to create a global method, I can tell that your Ruby code is not valid:
def custom my_query
self.where(my_query)
end
It would need to be:
def custom (my_query)
self.where(my_query)
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 :-)
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
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;