Ruby module help - ruby-on-rails

In the code below, I am able to call BackgroundJob.starting(job_script) just fine. However, I keep getting no method error for starting when I try to call JobScriptHelper.starting(RemoveBotReferralCodes), for example. The JobScriptHelper is in the lib folder, while RemoveBotReferralCodes is in a peer folder called script. Any idea what's going on?
module JobScriptHelper
def starting(job_script)
puts "#{Time.now.strftime('%c')}: #{job_script.name} - starting"
end
end
require 'job_script_helper'
class BackgroundJob < ActiveRecord::Base
extend JobScriptHelper
end
#!/usr/bin/env ruby
require File.expand_path('../../../config/boot', __FILE__)
require File.join(File.expand_path('../../../config/environment', __FILE__))
require 'job_script_helper'
class RemoveBotReferralCodes
def self.remove
# ....
end
end
JobScriptHelper.starting(RemoveBotReferralCodes)

To be able to call JobScriptHelper.starting() I believe your method definition should be
def JobScriptHelper.starting(job_script)
puts "#{Time.now.strftime('%c')}: #{job_script.name} - starting"
end

Related

Unable to extend ActiveRecord from within gem

I'd like my gem to add a method to my AR classes that would enable some functionality:
class User < ApplicationRecord
enable_my_uber_features :blah
end
For this, within my_uber_gem/lib/uber_gem.rb, I attempt the following:
# my_uber_gem/lib/uber_gem.rb
require "active_support"
# ...
require "uber/extensions"
# ...
ActiveSupport.on_load(:active_record) do
class ActiveRecord::Base
include Uber::Extensions
end
end
In uber/extensions.rb I have:
# my_uber_gem/lib/uber/extensions.rb
require 'active_support/concern'
module Uber
module Extensions
extend ActiveSupport::Concern
instance_methods do
def foo
p :bar
end
end
class_methods do
def enable_my_uber_features(value)
p value
end
end
end
end
Now I'd expect to see a blah in the console when instantiating an User, however I get an error:
[...] `method_missing': undefined method `enable_my_uber_features' for User:Class (NoMethodError)
I've tried including Uber::Extensions into ApplicationRecord instead of ActiveRecord::Base, no luck.
I've also tried extend Uber::Extensions, no luck.
I've also tried defining a module ClassMethods from within Uber::Extensions the extending/including, still no luck.
I've also followed this guy's guide by the letter: https://waynechu.cc/posts/405-how-to-extend-activerecord, still no luck.
Am I doing something wrong?
The way I've done this is:
# in my_uber_gem/lib/active_record/base/activerecord_base.rb
require 'uber/extensions'
class ActiveRecord::Base
include Uber::Extensions
def baz
p "got baz"
end
end
# and in the my_uber_gem/lib/my_uber_gem.rb
module MyUberGem
require_relative './active_record/base/activerecord_base.rb'
end
# and finally define the extension in my_uber_gem/lib/uber/extensions.rb
require 'active_support/concern'
class Uber
module Extensions
extend ActiveSupport::Concern
def foobar(val)
p val
end
end
end
# now you can do
User.first.foobar("qux") #=> "qux"
User.first.baz #=> "got baz"
Turns out Rails was caching my local gem I was working on, and any changes I made to any gem files were only visible in a new app or after rebuilding the entire machine.
I was able to turn this off so it will reload the gem on every console reload:
# config/environments/development.rb
config.autoload_paths += %W(#{config.root}/../my_uber_gem/lib)
ActiveSupport::Dependencies.explicitly_unloadable_constants << 'MyUberGem'

Rails: trouble including modules via a plugin

I'm trying to create a rails plugin and the problem I'm facing is that the app won't include my modules when migrating the plugin.
Here's what I have so far:
1. A file lib/patch/settings_helper_patch.rb with extension code
2. An init.rb file with require_dependency 'patch/settings_helper_patch'
3. Some code in settings_helper_patch.rb which is as follows:
module ValidateIssuePatch
module Patch
module SettingsHelperPatch
def self.included(base)
base.send(:include, InstanceMethods)
end
module InstanceMethods
def issue_options
#some code here
end
end
end
end
end
unless SettingsHelper.included_modules.include?(ValidateIssuePatch::Patch::SettingsHelperPatch)
SettingsHelper.send(:include, ValidateIssuePatch::Patch::SettingsHelperPatch)
end
After I migrate the plugin, I wish to use the issue_options method, but I get undefined local variable or method error.
If I run SettingsHelper.included_modules.include?(ValidateIssuePatch::Patch::SettingsHelperPatch) from the console, I get uninitialized constant Patch::SettingsHelperPatch.
However, if I call ValidateIssuePatch from the console, I get => ValidateIssuePatch in response.
Can anyone tell me what is the magic I'm missing here?
Firstly, if your module is only going to have instance methods, I would recommend using the following easy-to-follow syntax:
module ValidateIssuePatch
module Patch
module SettingsHelperPatch
def issue_options
# code
end
end
end
end
SettingsHelper.include(ValidateIssuePatch::Patch::SettingsHelperPatch)
Secondly, the reason why ValidateIssuePatch might be defined is that some other file has it which is being required properly. This file isn't being executed in any way. I would raise an error somewhere that, when raised, will verify that the code is / isn't being executed. Something like the following:
module ValidateIssuePatch
module Patch
module SettingsHelperPatch
raise "All good" # remove this afterwards
def issue_options
# code
end
end
end
end
SettingsHelper.include(ValidateIssuePatch::Patch::SettingsHelperPatch)
Chances are that the error won't be raised and it'll confirm that your file isn't being required - either not at all or not in the right order.
To further verify this, simply open up your console and do the following with your existing code:
ValidateIssuePatch::Patch::SettingsHelperPatch #=> error
require path_of_file
ValidateIssuePatch::Patch::SettingsHelperPatch #=> no more error
Finally, why do you check for the module already being included in SettingsHelper? (referring to the unless condition) Your code should be including the module only once, not "maybe only once".

Include plugin in module in Rails

This is a definite newb question:
I have a module defined in my lib/ directory that I call from a sweeper and as a rake task. Inside the module I want to reference my spawn plugin. Just including methods from that plugin doesn't work (undefined method error), nor does any version of require or include that I've tried. What do I need to do to include it?
Thank you!
EDIT:
Here is my latest effort:
require 'vendor/plugins/spawn/init.rb'
module MyModule
include Spawn
def self.my_method
spawn(:method => :thread, :nice => 9) do # also tried Spawn::spawn
...
end
end
end
The error I get is:
undefined method 'spawn' for MyModule:Module #spawn or undefined method 'spawn' for Spawn:Module # Spawn::spawn
The including has to be done from within the plugin. When you "reopen" a module (which is in the lib/ dir), remember to require the original file before making changes to it (the module).
EDIT:
Considering that the plugin is third party, you could try:
Spawn.module_eval do
module_function :spawn
public :spawn
end
module MyModule
def self.my_method
Spawn.spawn(...)
end
end

Cannot include module in model

I'm using
Ruby version 1.8.7
Rails version 3.0.3
I have a method called alive in every model of my rails app:
def alive
where('deleter is null')
end
I don't want to copy this code in every model so I made a /lib/life_control.rb
module LifeControl
def alive
where('deleter is null')
end
def dead
where('deleter is not null')
end
end
and in my model (for example client.rb) I wrote:
class Client < ActiveRecord::Base
include LifeControl
end
and in my config/enviroment.rb I wrote this line:
require 'lib/life_control'
but now I get a no method error:
NoMethodError in
ClientsController#index
undefined method `alive' for
#<Class:0x10339e938>
app/controllers/clients_controller.rb:10:in
`index'
what am I doing wrong?
include will treat those methods as instance methods, not class methods. What you want to do is this:
module LifeControl
module ClassMethods
def alive
where('deleter is null')
end
def dead
where('deleter is not null')
end
end
def self.included(receiver)
receiver.extend ClassMethods
end
end
This way, alive and dead will be available on the class itself, not instances thereof.
I'm aware this is a pretty old question, the accepted answer did work for me, but that meant me having to re-write a lot of code because i have to change the module to a nested one.
This is what helped me with my situation and should work with most of today's applications.(not sure if it'll work in the ruby/rails version in the question)
instead of doing include use extend
So as per the question, the sample code would look like:
class Client < ActiveRecord::Base
extend LifeControl
end
Just put this line in application.rb file
config.autoload_paths += Dir["#{config.root}/lib/**/"]
Edited:
This line is working fine for me.
I want to suggest one more thing, ruby 1.8.x is not compatible with rails 3.x.
So just update your ruby for version 1.9.2
Following is my POC
In lib folder:
lib/test_lib.rb
module TestLib
def print_sm
puts "Hello World in Lib Directory"
end
end
In model file:
include TestLib
def test_method
print_sm
end
And In application.rb
config.autoload_paths += Dir["#{config.root}/lib/**/"]
Now you can call test_method like this in controller:
ModelName.new.test_method #####Hello World in Lib Directory

Wrapping class method via alias_method_chain in plugin for Redmine

I am not sure if this problem is a general Rails problem or Redmine specific.
There is a class User which has a class method try_to_login. I wrote a module containing a method_alias_chain to wrap that method and provide additional functionality. This works fine if I go into the console and call try_to_login. My wrapper will be executed and everything is fine. However, when I run this on the server just the vanilla method is called. The wrapper is never touched. I added a logger command to the vanilla method to be sure and it is in deed being called.
Here is a simplified version of the code:
require_dependency 'principal'
require_dependency 'user'
require 'login_attempt_count'
module UserLoginAttemptLimiterPatch
def self.included(base)
base.extend ClassMethods
base.class_eval do
class << self
alias_method_chain :try_to_login, :attempt_limit
end
end
end
module ClassMethods
def try_to_login_with_attempt_limit(login, password)
user = try_to_login_without_attempt_limit login, password
#stuff here gets called via console but not via browser
user
end
def authentication_failed(login)
#important code here
end
end
end
User.send(:include, UserLoginAttemptLimiterPatch)
In addition this module is required when the plugin is loaded.
How are you requiring the module? If you are running in development mode, the User class could be reloaded after the first request which would clear out your patch and alias_method_chain.
You can get around it by doing the patch inside of a Dispatcher (which runs with every code reload):
require 'dispatcher'
Dispatcher.to_prepare do
Issue.send(:include, MyMooPatch)
end
Reference: http://theadmin.org/articles/2009/04/13/how-to-modify-core-redmine-classes-from-a-plugin/

Resources