getting in-out from ruby methods - ruby-on-rails

I am using jruby to run bunch of ruby scripts, though I am using pure ruby part of it.
It sometimes gets difficult to follow from output what exactly is happening or where exactly something went wrong.
I wanted to get something like this in my std out for every method:
entered in method A
out of method A
Now I can surely go and put those comments in every method ..which feels very wrong. Is there a way to run ruby in a little verbose more to get this information in my log. Hopefully I would avoid using a lot of gems etc .. since these are on some managed servers and I will have to spend some time to just get more s/f on it. Hoping something would be avail as part of jruby itself
Thanks!

You could use this code:
module InOutHook
module ClassMethods
def setup_hooks(*syms)
syms.each do |sym| # For each symbol
str_id = "__#{sym}__hooked__"
unless private_instance_methods.include?(str_id)
alias_method str_id, sym # Backup original method
private str_id # Make backup private
define_method sym do |*args| # Replace method
puts ">>> #{self.class}\##{sym} >>>"
ret = __send__ str_id, *args # Invoke backup
puts "<<< #{self.class}\##{sym} >>>"
ret
end
end
end
end
end
def InOutHook.included(base)
base.extend(ClassMethods)
end
end
class TestClass
def test1
puts "test!"
end
def test2(v)
puts "Value is #{v}"
end
include InOutHook
setup_hooks(:test1, :test2)
end
# works on existing classes too:
class Array
include InOutHook
setup_hooks(:[])
end
tc = TestClass.new
tc.test1
tc.test2(10)
ary = [1,2,3]
puts ary[1..2]
In case you want to add a hoot to every method, just add a splat asterisk:
setup_hooks(*[].methods)

Related

ruby instance_eval will go stac level too deep when calling proc inside it

I run into really interesting problem with ruby today. I have a module/rails concern :
module BreakCacheModule
extend ActiveSupport::Concern
module ClassMethods
def breakable_cache_for(method_name, &block)
method_name = method_name.to_sym
set_breakable_cache_proc(method_name, block)
define_breakable_cache_method(method_name)
end
def define_breakable_cache_method(method_name)
instance_eval(
"def #{method_name}
# here will be Rails.cache.fetch block around line below once I figure out this problem
#_break_cache_procs[#{method_name}].call # !!! this line will cause an issue
end"
)
end
def set_breakable_cache_proc(method_name, block)
#_break_cache_procs ||={}
#_break_cache_procs.merge!({method_name => block})
#_break_cache_procs
end
end
end
and in my model
class Client < ActiveRecord::Base
include BreakCacheModule
breakable_cache_for :all_client_ids do
Client.pluck :id
end
end
so sofar so good but once I go into console (Or run my spec) problems will show up
Client.all_client_ids
# SystemStackError: stack level too deep
but when I do:
Client.instance_variable_get("#_break_cache_procs")[:all_client_ids].call
#=> [1,2,3]
The functionality is exactly the same, only difference is that I'm not calling proc from instance_eval.
Am I missing something ??
my ruby ruby-2.0.0-p247 and Rails 3.2.14
Thank you for help
Update after accepting
Just to sum this up, I was getting actually this error from two places:
"def #{method_name}
Rails.cache.fetch(#{method_name.to_s}) do # method_name should be in quotes
#_break_cache_procs[#{method_name}].call # missing colon ...look on answer
end
end"
so the code should look like this
instance_eval(
"def #{method_name}
Rails.cache.fetch('#{method_name.to_s}') do
#_break_cache_procs[:#{method_name}].call
end
end"
)
The string passed to instance_eval is actually:
"def all_client_ids
#_break_cache_procs[all_client_ids].call
end"
You may have noticed the problem: a colon is missing.
This should be the correct code:
def define_breakable_cache_method(method_name)
instance_eval(
"def #{method_name}
# here will be Rails.cache.fetch block around line below once I figure out this problem
#_break_cache_procs[:#{method_name}].call # !!! this line will cause an issue
end"
)
end

How do I cache a method with Ruby/Rails?

I have an expensive (time-consuming) external request to another web service I need to make, and I'd like to cache it. So I attempted to use this idiom, by putting the following in the application controller:
def get_listings
cache(:get_listings!)
end
def get_listings!
return Hpricot.XML(open(xml_feed))
end
When I call get_listings! in my controller everything is cool, but when I call get_listings Rails complains that no block was given. And when I look up that method I see that it does indeed expect a block, and additionally it looks like that method is only for use in views? So I'm guessing that although it wasn't stated, that the example is just pseudocode.
So my question is, how do I cache something like this? I tried various other ways but couldn't figure it out. Thanks!
an in-code approach could look something like this:
def get_listings
#listings ||= get_listings!
end
def get_listings!
Hpricot.XML(open(xml_feed))
end
which will cache the result on a per-request basis (new controller instance per request), though you may like to look at the 'memoize' helpers as an api option.
If you want to share across requests don't save data on the class objects, as your app will not be threadsafe, unless you're good at concurrent programming & make sure the threads don't interfere with each other's data access to the shared variable.
The "rails way" to cache across requests is the Rails.cache store. Memcached gets used a lot, but you might find the file or memory stores fit your needs. It really depends on how you're deploying and whether you want to prioritise cache hits, response time, storage (RAM), or use a hosted solution e.g. a heroku addon.
As nruth suggests, Rails' built-in cache store is probably what you want.
Try:
def get_listings
Rails.cache.fetch(:listings) { get_listings! }
end
def get_listings!
Hpricot.XML(open(xml_feed))
end
fetch() retrieves the cached value for the specified key, or writes the result of the block to the cache if it doesn't exist.
By default, the Rails cache uses file store, but in a production environment, memcached is the preferred option.
See section 2 of http://guides.rubyonrails.org/caching_with_rails.html for more details.
You can use the cache_method gem:
gem install cache_method
require 'cache_method'
In your code:
def get_listings
Hpricot.XML(open(xml_feed))
end
cache_method :get_listings
You might notice I got rid of get_listings!. If you need a way to refresh the data manually, I suggest:
def refresh
clear_method_cache :get_listings
end
Here's another tidbit:
def get_listings
Hpricot.XML(open(xml_feed))
end
cache_method :get_listings, (60*60) # automatically expire cache after an hour
You can also use cachethod gem (https://github.com/reneklacan/cachethod)
gem 'cachethod'
Then it is deadly simple to cache method's result
class Dog
cache_method :some_method, expires_in: 1.minutes
def some_method arg1
..
end
end
It also supports argument level caching
There was suggested cache_method gem, though it's pretty heavy. If you need to call method without arguments, solution is very simple:
Object.class_eval do
def self.cache_method(method_name)
original_method_name = "_original_#{method_name}"
alias_method original_method_name, method_name
define_method method_name do
#cache ||= {}
#cache[method_name] = send original_method_name unless #cache.key?(method_name)
#cache[method_name]
end
end
end
then you can use it in any class:
def get_listings
Hpricot.XML(open(xml_feed))
end
cache_method :get_listings
Note - this will also cache nil, which is the only reason to use it instead of #cached_value ||=
Late to the party, but in case someone arrives here searching.
I use to carry this little module around from project to project, I find it convenient and extensible enough, without adding an extra gem. It uses the Rails.cache backend, so please use it only if you have one.
# lib/active_record/cache_method.rb
module ActiveRecord
module CacheMethod
extend ActiveSupport::Concern
module ClassMethods
# To be used with a block
def cache_method(args = {})
#caller = caller
caller_method_name = args.fetch(:method_name) { #caller[0][/`.*'/][1..-2] }
expires_in = args.fetch(:expires_in) { 24.hours }
cache_key = args.fetch(:cache_key) { "#{self.name.underscore}/methods/#{caller_method_name}" }
Rails.cache.fetch(cache_key, expires_in: expires_in) do
yield
end
end
end
# To be used with a block
def cache_method(args = {})
#caller = caller
caller_method_name = args.fetch(:method_name) { #caller[0][/`.*'/][1..-2] }
expires_in = args.fetch(:expires_in) { 24.hours }
cache_key = args.fetch(:cache_key) { "#{self.class.name.underscore}-#{id}-#{updated_at.to_i}/methods/#{caller_method_name}" }
Rails.cache.fetch(cache_key, expires_in: expires_in) do
yield
end
end
end
end
Then in an initializer:
# config/initializers/active_record.rb
require 'active_record/cache_method'
ActiveRecord::Base.send :include, ActiveRecord::CacheMethod
And then in a model:
# app/models/user.rb
class User < AR
def self.my_slow_class_method
cache_method do
# some slow things here
end
end
def this_is_also_slow(var)
custom_key_depending_on_var = ...
cache_method(key_name: custom_key_depending_on_var, expires_in: 10.seconds) do
# other slow things depending on var
end
end
end
At this point it only works with models, but can be easily generalized.
Other answers are excellent but if you want a simple hand-rolled approach you can do this. Define a method like the below one in your class...
def use_cache_if_available(method_name,&hard_way)
#cached_retvals ||= {} # or initialize in constructor
return #cached_retvals[method_name] if #cached_retvals.has_key?(method_name)
#cached_retvals[method_name] = hard_way.call
end
Thereafter, for each method you want to cache you can put wrap the method body in something like this...
def some_expensive_method(arg1, arg2, arg3)
use_cache_if_available(__method__) {
calculate_it_the_hard_way_here
}
end
One thing that this does better than the simplest method listed above is that it will cache a nil. It has the convenience that it doesn't require creating duplicate methods. Probably the gem approach is cleaner, though.
I'd like to suggest my own gem https://github.com/igorkasyanchuk/rails_cached_method
For example:
class A
def A.get_listings
....
end
end
Just call:
A.cached.get_listings

Ruby on Rails: Passing argument to singleton

I have a Rails app that repeatedly talks to another Web server through a wrapper, and I'd like to stick the wrapper in a Singleton class so it's not recreated for every request. Easy enough, I thought:
class AppWrapper < Wrapper
include Singleton
end
...
wrapper = AppWrapper.instance "url"
Only it doesn't work:
wrong number of arguments (0 for 1)
/usr/lib/ruby/1.8/singleton.rb:94:in `initialize'
/usr/lib/ruby/1.8/singleton.rb:94:in `new'
/usr/lib/ruby/1.8/singleton.rb:94:in `instance'
Wrapper.initialize needs an argument, and apparently it's not getting passed through, since line 94 in question says
#__instance__ = new # look Ma, no argument
How do I work around this? Redefining initialize in AppWrapper doesn't seem to help, and
mucking around with Wrapper to separate "set URL" from "initialize" seems suboptimal.
Passing argument to singleton
class Parameterized_Singleton
def initialize(a)
#pdf = a
puts #pdf
end
def self.instance(p)
begin
##instance =Parameterized_Singleton.new(p)
private_class_method :new
rescue NoMethodError
# return ##instance # or you can return previous object
puts "Object Already Created"
exit
end
return ##instance
end
def scanwith(b)
puts "scan"
end
def show_frequence_distribution
puts "fd"
end
def show_object_number(a)
puts "no"
end
end
Parameterized_Singleton.instance(20).show_object_number(10)
Parameterized_Singleton.instance(10).show_object_number(20)
Are you sure you need a singleton and not a factory . Refer this
I asked this question while I was still getting my head around Ruby, and it seems so naive now. The easy solution is to just store the Wrapper object in a member variable and use ||= to initialize it only if it hasn't been set yet:
class WrapperUserClass
def initialize
#wrapper = nil # Strictly speaking unnecessary, but it's a bit clearer this way
end
def wrapper
#wrapper ||= Wrapper.new(foobar)
end
def do_something
wrapper.booyakasha
end
end
Since you mention something about editing Wrapper as a solution, can't you just use Wrapper directly and do this?
class Wrapper; include Singleton; end
If not, you could use something like this, which will just make sure AppWrapper.new isn't called more than once:
class AppWrapper
def self.new(*args)
class << app_wrapper = Wrapper.new(*args)
include Singleton
end
app_wrapper
end
end
If you need the singleton "Klass.instance" method, you'll have to take either take out the parameter in Wrapper#initialize, or just redefine Singleton#instance to take arguments optionally and passes them to the call to new on line 94.

How to alias ActiveRecord class methods dynamically in a rails plugin?

I'm having trouble removing some duplication I've introduced in a rails plugin.
The code below modifies the find and calculate methods of ActiveRecord in the same way, but I've been unable to remove the duplication.
The find and calculate methods below make use of the super keyword which is one hurdle as the super keyword can only be used to call a method sharing the same name as the calling method, so I can't move the super keyword to a method shared by find and calculate.
So next I tried aliasing the find and calculate class methods from the superclass ActiveRecord, however, I've not been able to get the syntax right for the aliasing. If someone could show me that, it would be a great help.
If you've got a better way entirely of doing this I'd love for you to post that too.
Below I've trimmed the code down a little to highlight the problem:
module Geocodable #:nodoc:
def self.included(mod)
mod.extend(ClassMethods)
end
module ClassMethods
def acts_as_geocodable(options = {})
extend Geocodable::SingletonMethods
end
end
module SingletonMethods
def find(*args)
some_method_1
super *args.push(options)
some_method_2
end
# TODO: Remove duplication of find above and calculate below.
def calculate(*args)
some_method_1
super *args.push(options)
some_method_2
end
end
end
Your best way to refactor this code is to leave find and calculate unchanged, and add apply the wrapping using a class-level function.
Here's rough sketch, without your module and mixin logic:
class A
def find x
puts 'finding'
end
def calculate x
puts 'calculating'
end
end
class B < A
def self.make_wrapper_method name
define_method name do |*args|
puts "entering"
result = super *args
puts "exiting"
result
end
end
make_wrapper_method :find
make_wrapper_method :calculate
end
Note that this will need to be modified if B has already overridden find or calculate.
To use this code, first make your version work correctly, then modify it to use define_method. (And if you need extremely high performance, you may need to use one of the *_eval functions to create the wrappers instead of define_method.)
This is the option I went for in the end, thanks to emk for guidance to get to this point!
module Geocodable
def self.included(mod)
mod.extend(ClassMethods)
end
module ClassMethods
def acts_as_geocodable(options = {})
geoify_query_methods
end
private
# This is where the duplication has been removed
def geoify_query_methods
class << self
[:calculate, :find].each do |method_name|
define_method method_name do |*args|
some_method_1
super *args.push(options)
some_method_2
end
end
end
end
end
end
To just alias the find method:
module SingletonMethods
def find(*args)
some_method_1
super *args.push(options)
some_method_2
end
alias :calculate :find
end

Rails: I can't call a function in a module in /lib - what am I doing wrong?

I have a module saved in /lib as test_functions.rb that looks like this
module TestFunctions
def abc
puts 123
end
end
Going into ruby script/runner, I can see that the module is loading automatically (good ol' convention over configuration and all that...)
>> TestFunctions.instance_methods
=> ["abc"]
so the method is known, let's try calling it
>> TestFunctions.abc
NoMethodError: undefined method `abc' for TestFunctions:Module from (irb):3
Nope. How about this?
>> TestFunctions::abc
NoMethodError: undefined method `abc' for TestFunctions:Module from (irb):4
Test
Nope again.
defined?(TestFunctions::abc) #=> nil, but
TestFunctions.method_defined? :abc #=> true
Like I said at the top, I know I'm being dumb, can anyone de-dumb me?
If you want Module-level functions, define them in any of these ways:
module Foo
def self.method_one
end
def Foo.method_two
end
class << self
def method_three
end
end
end
All of these ways will make the methods available as Foo.method_one or Foo::method_one etc
As other people have mentioned, instance methods in Modules are the methods which are available in places where you've included the Module
I'm going to try to summarise the various answers myself, since each had something valuable to say, but none really got to what I now realise is probably the best response:
I was asking the wrong question because I was doing it wrong.
For reasons I can no longer explain, I wanted a set of completely stand-alone functions in a library, which represented methods I was trying to DRY out of my classes. That can be achieved, using things like
module Foo
def self.method_one
end
def Foo.method_two
end
class << self
def method_three
end
end
def method_four
end
module_function :method_four
end
I could also include my module, either within a class, in which case the methods become part of the class or outside, in which case they are defined on whatever class I'm running inside (Object? Kernel? Irb, if I'm interactive? Probably not a great idea, then)
The thing is, there was no good reason not to have a class in the first place - I'd somehow got on to a train of thought that took me down an seldom-used and frankly slightly weird branch line. Probably a flashback to the days before OO became mainstream (I'm old enough that up to today I've spent a lot more years writing procedural code).
So the functions have moved into a class, where they seem pretty happy, and the class methods thus exposed are being cheerfully used wherever necessary.
You can also use module_function like so:
module TestFunctions
def abc
puts 123
end
module_function :abc
end
TestFunctions.abc # => 123
Now you can include TestFunctions in class and call "abc" from within TestFunctions module.
I messed with this for a while and learned several things. Hopefully this will help someone else out. I am running Rails 3.2.8.
My module (utilities.rb) looks like this and is in the /lib directory of my rails app:
module Utilities
def compute_hello(input_string)
return "Hello #{input_string}"
end
end
My test (my_test.rb) looks like this and is in the /test/unit directory of my rails app:
require "test_helper"
require "utilities"
class MyTest < ActiveSupport::TestCase
include Utilities
def test_compute_hello
x = compute_hello(input_string="Miles")
print x
assert x=="Hello Miles", "Incorrect Response"
end
end
Here are a few things to note: My test extends ActiveSupport::TestCase. This is important because ActiveSupport adds /lib to the $LOAD_PATH. (seehttp://stackoverflow.com/questions/1073076/rails-lib-modules-and)
Secondly, I needed to both "require" my module file, and also "include" the module. Lastly, it is important to note that the stuff that gets included from the module essentially gets placed in the test class. So... be careful that the module that you include doesn't start with "test_". Otherwise, Rails will attempt to run your module method as a test.
You can't call a method in a Module directly. You need to include it in a class. Try this:
>> class MyTest
>> include TestFunctions
>> end
=> MyTest
>> MyTest.new.abc
123
=> nil
You need to include the module
include Testfunctions
Then 'abc' will return something.
You need to prefix your function with the module name because modules are not classes:
Your /lib/test_functions.rb:
module TestFunctions
def TestFunctions.abc
puts 123
end
end
Your code using the module method:
require 'test_functions'
TestFunctions.abc
Today you can do it using module_function notation.
module TestFunctions
def abc
puts 123
end
end
Now TestFunctions.abc prints "123"
A little more about module_function: https://apidock.com/ruby/Module/module_function
Try this code:
service = Class.new { extend TestFunctions }
service.method_four

Resources