Extending Enumerable in Rails 3 - ruby

UPDATE TO QUESTION
Here is what I have done based on some research and findings.
STEP 1 - I have this module in my Rails 3 project and place it in my lib folder
# lib/enumerable.rb
module Enumerable
def sum
return self.inject(0){|acc,i|acc +i}
end
def average
return self.sum/self.length.to_f
end
def sample_variance
avg=self.average
sum=self.inject(0){|acc,i|acc +(i-avg)**2}
return(1/self.length.to_f*sum)
end
def standard_deviation
return Math.sqrt(self.sample_variance)
end
end
STEP 2 - According to this blog article, in Rails 3 your lib folder will not get loaded automatically. In order to load this module you need to go to your config / application.rb and type this in:
config.autoload_paths += %W(#{config.root}/lib)
STEP 3 - Then in your model my understanding is you type this in to get the module picked up.
class MyModel < ActiveRecord::Base
include Enumerable
end
STEP 4 - I then try restart the rails server and try this out and I get false when I would expect it to be true.
MyModel.respond_to?('sample_variance')
# false, when it should be true
What am I doing wrong? Should I not be getting true?

Your inclusion of the main Enumerable module (not your extension) undoubtedly worked, and you can test it by simply checking for any of the methods that were mixed in. The problem is, your 'Include Enumerable' may not have included your file, but rather the main module.
One suggestion is to rename the file name for your extension, and have it loaded through an initializer with a
require 'my_enumerable.rb'
That way you for sure get both Enumerable and your extension to Enumerable loaded.

If I understand what you're driving at, you're trying to use Enumerable's sum method in ActiveRecord. You can do that by converting the current object to an array, then calling Enumerable's sum method on that array.
One more thing: you don't need to use return like you are using it. Ruby will return the last calculated thing from your method. You don't need to use self like that either -- in Ruby, self is the current object.
So if you have a method:
def charlie
inject{|i, j| i + j + 1}
end
and you call it like this:
(1..2).charlie
self is the current object (1..2).
The output will be 4, with no self or return.
I highly recommend Dave Thomas' lecture on Ruby metaprogramming, I tried to find it, but I could not, it's out there on the web somewhere.

You might want to take a look at this:
http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_modules.html
You can include a module in a class, and thereby make that module's methods available to that class.
If you include Enumerable into a Rails model, then its methods would be available to that model. But since Enumerable's methods are already available to certain types of objects inside your Rails project, and those objects are available to be instantiated from inside your model, I don't see why you might do that, because Enumerable's methods are working just fine for the purposes they were designed.
Anyway, you might find that one of the following might work for you:
-- use Activerecord's sum method
-- convert your object to an array, and use Enumerable's sum method
-- write your own method, but don't call it sum, because you don't want to confuse yourself.
Try commenting out the second occurrence of module Neuone in the following snippet, and see what happens. Then try commenting out the Charlie.one method, and see what happens.
module Neuone
def one
'neuone one'
end
def two
'neuone two'
end
end
module Neuone
def two
'neuone two two'
end
end
class Charlie
include Neuone
def one
'charlie one'
end
end
c = Charlie.new
p c.one
p c.two

Related

RSpec test method is called on `main` object

Sometimes we call methods on the ruby main objects. For example we call create for FactoryBot and we call _() for I18n.
What's a proper way to test these top level methods got called in RSpec?
For example, I want to test N_ is called, but it would not work because the self in Rspec and self in the file are different.
# spec
describe 'unfound_translations' do
it 'includes dynamic translations' do
expect(self).to receive(:N_)
load '/path/to/unfound_translations.rb')
end
end
# unfound_translations.rb
N_('foo')
However this does not pass.
Ok, I get your problem now. Your main issue is that self in it block is different that self inside unfound_translations.rb. So you're setting expectations on one object and method N_ is called on something completely different.
(Edit: I just realized, when reading the subject of this question again, that you already was aware of it. Sorry for stating the obvious... leaving it so it may be useful to others)
I managed to have a hacky way that is working, here it is:
# missing_translations.rb
N_('foo')
and the spec (I defined a simple module for tests inside it for simplicity):
module N
def N_(what)
puts what
end
end
RSpec.describe 'foo' do
let(:klass) do
Class.new do
extend N
end
end
it do
expect(klass).to receive(:N_)
klass.class_eval do
eval(File.read('missing_translations.rb'))
end
end
end
What it does it's creating an anonymous class that. And evaluating contents of missing_translations.rb inside means that klass is the thing that receives N_ method. So you can set expectations there.
I'm pretty sure you can replace extend N module with whatever module is giving you N_ method and this should work.
It's hacky, but not much effort so maybe good enough until more elegant solution is provided.

A better way to call methods on an instance

My question has a couple layers to it so please bear with me? I built a module that adds workflows from the Workflow gem to an instance, when you call a method on that instance. It has to be able to receive the description as a Hash or some basic data structure and then turn that into something that puts the described workflow onto the class, at run-time. So everything has to happen at run-time. It's a bit complex to explain what all the crazy requirements are for but it's still a good question, I hope. Anyways, The best I can do to be brief for a context, here, is this:
Build a class and include this module I built.
Create an instance of Your class.
Call the inject_workflow(some_workflow_description) method on the instance. It all must be dynamic.
The tricky part for me is that when I use public_send() or eval() or exec(), I still have to send some nested method calls and it seems like they use 2 different scopes, the class' and Workflow's (the gem). When someone uses the Workflow gem, they hand write these method calls in their class so it scopes everything correctly. The gem gets to have access to the class it creates methods on. The way I'm trying to do it, the user doesn't hand write the methods on the class, they get added to the class via the method shown here. So I wasn't able to get it to work using blocks because I have to do nested block calls e.g.
workflow() do # first method call
# first nested method call. can't access my scope from here
state(:state_name) do
# second nested method call. can't access my scope
event(:event_name, transitions_to: :transition_to_state)
end
end
One of the things I'm trying to do is call the Workflow#state() method n number of times, while nesting the Workflow#event(with, custom_params) 0..n times. The problem for me seems to be that I can't get the right scope when I nest the methods like that.
It works just like I'd like it to (I think...) but I'm not too sure I hit the best implementation. In fact, I think I'll probably get some strong words for what I've done. I tried using public_send() and every other thing I could find to avoid using class_eval() to no avail.
Whenever I attempted to use one of the "better" methods, I couldn't quite get the scope right and sometimes, I was invoking methods on the wrong object, altogether. So I think this is where I need the help, yeah?
This is what a few of the attempts were going for but this is more pseudo-code because I could never get this version or any like it to fly.
# Call this as soon as you can, after .new()
def inject_workflow(description)
public_send :workflow do
description[:workflow][:states].each do |state|
state.map do |name, event|
public_send name.to_sym do # nested call occurs in Workflow gem
# nested call occurs in Workflow gem
public_send :event, event[:name], transitions_to: event[:transitions_to]
end
end
end
end
end
From what I was trying, all these kinds of attempts ended up in the same result, which was my scope isn't what I need because I'm evaluating code in the Workflow gem, not in the module or user's class.
Anyways, here's my implementation. I would really appreciate it if someone could point me in the right direction!
module WorkflowFactory
# ...
def inject_workflow(description)
# Build up an array of strings that will be used to create exactly what
# you would hand-write in your class, if you wanted to use the gem.
description_string_builder = ['include Workflow', 'workflow do']
description[:workflow][:states].each do |state|
state.map do |name, state_description|
if state_description.nil? # if this is a final state...
description_string_builder << "state :#{name}"
else # because it is not a final state, add event information too.
description_string_builder.concat([
"state :#{name} do",
"event :#{state_description[:event]}, transitions_to: :#{state_description[:transitions_to]}",
"end"
])
end
end
end
description_string_builder << "end\n"
begin
# Use class_eval to run that workflow specification by
# passing it off to the workflow gem, just like you would when you use
# the gem normally. I'm pretty sure this is where everyone's head pops...
self.class.class_eval(description_string_builder.join("\n"))
define_singleton_method(:has_workflow?) { true }
rescue Exception => e
define_singleton_method(:has_workflow?) { !!(puts e.backtrace) }
end
end
end
end
# This is the class in question.
class Job
include WorkflowFactory
# ... some interesting code for your class goes here
def next!
current_state.events.#somehow choose the correct event
end
end
# and in some other place where you want your "job" to be able to use a workflow, you have something like this...
job = Job.new
job.done?
# => false
until job.done? do job.next! end
# progresses through the workflow and manages its own state awareness
I started this question off under 300000 lines of text, I swear. Thanks for hanging in there! Here's even more documentation, if you're not asleep yet.
module in my gem

Overriding object attribute access

I was wondering if it's possible to make it so that if I had something like
class Test
attr_reader :access_times
def initialize
#access_times = 0
end
def get_two
2
end
...
end
t = Test.new
That any access to t would run a particular piece of code before actually running the method?
For example, if I suddenly decided to say t.get_two, the fact that I used the . syntax would increment #access_times by 1. Or perhaps I made a check t.is_a?(Test), it would also increment #access_times by 1. Accessing any methods or attributes inherited by Test would also increment the variable by 1.
Basically I want to add some stuff to the . syntax if possible.
I am not asking whether this is good or bad code, just whether it's possible and how it would be done. I wouldn't normally use it since I could just add the increment logic to every method manually and replace all direct instance variable accessing with methods (even things like is_a? and other things inherited from Object)
a pretty hardcore-version would be to use set_trace_func: http://apidock.com/ruby/Kernel/set_trace_func
this allows you to subscribe to all the ruby events fired throughout your program, which can be a ton of calls...
i don't think that there is a build-in hook for registering to arbitrary method-calls. you could implement something with method-missing, method-chaining or delegation, but that would depend on your requirments.
If you don't need everything to be standalone, a suggestion would just be to extend ActiveModel::Callbacks. Simply extend the class and you'll have all of the functionality of a before_filter without requiring all of the other Rails stuff.
Here is a workaround according to your description. Basically it will incremental #access_times for each of the instance method, and the method also does what it does before.
class Test
attr_accessor :access_times
def initialize
#access_times = 0
end
def get_two
2
end
end
class Test
##im = instance_methods
##im.each do |m|
class_eval <<-END
alias temporary #{m}
END
define_method(m) do |*args, &block|
#access_times += 1
temporary(*args, &block)
end
end
undef :temporary
end
Test.new.get_two # => #access_times += 1 and original get_two is called: 2
While this piece of code doesn't work as expected, I'll have a look at it later. Thanks.

referring to module level variables from within module

I'm having some difficulty with referring to module-level variables in ruby. Say I have a situation like this, where I'm referring to M.a internally:
module M
##a=1
def self.a
##a
end
class A
def x
M.a
end
end
end
Now, this example works fine for me but it is failing in a slightly more complicated context (where the module is spread over a number of files installed in a local gem - but my understanding is that that should not effect the way the code is executed) with an error like this: undefined method `a' for M::M (NoMethodError).
So, is this the correct way to refer to module level variables in context? is there a simpler/more idiomatic way?
If the module is spread out over other files, you need to ensure that your initialization is run before the method is called. If they are in the same file, this should be as much as guaranteed, but if you somehow split them there could be trouble.
I've found you can usually get away with this:
module M
def self.a
#a ||= 1
end
end
If this variable is subject to change, you will need a mutator method. Rails provides mattr_accessor that basically does what you want, part of ActiveSupport.

In how many ways can methods be added to a ruby object?

When it comes to run time introspection and dynamic code generation I don't think ruby has any rivals except possibly for some lisp dialects. The other day I was doing some code exercise to explore ruby's dynamic facilities and I started to wonder about ways of adding methods to existing objects. Here are 3 ways I could think of:
obj = Object.new
# add a method directly
def obj.new_method
...
end
# add a method indirectly with the singleton class
class << obj
def new_method
...
end
end
# add a method by opening up the class
obj.class.class_eval do
def new_method
...
end
end
This is just the tip of the iceberg because I still haven't explored various combinations of instance_eval, module_eval and define_method. Is there an online/offline resource where I can find out more about such dynamic tricks?
Ruby Metaprogramming seems to be a good resource. (And, linked from there, The Book of Ruby.)
If obj has a superclass, you can add methods to obj from the superclass using define_method (API) as you mentioned. If you ever look at the Rails source code, you'll notice that they do this quite a bit.
Also while this isn't exactly what you're asking for, you can easily give the impression of creating an almost infinite number of methods dynamically by using method_missing:
def method_missing(name, *args)
string_name = name.to_s
return super unless string_name =~ /^expected_\w+/
# otherwise do something as if you have a method called expected_name
end
Adding that to your class will allow it to respond to any method call which looks like
#instance.expected_something
I like the book Metaprogramming Ruby which is published by the publishers of the pickaxe book.

Resources