Globally Access Variables for All Cucumber Steps from inside Hooks - ruby

currently I am loading all YAML files inside Cucumber Hooks like this before- the intention is to ensure we dont code individually for loading YAML files.
#all_Yaml_Files = Dir.entries(#projectData_Path).select {|f| !File.directory? f};
#all_Yaml_Files.each do |file_Name|
file_Full_Path = #projectData_Path + '/' + file_Name
instance_variable_set("#" + file_Name.split('.')[0], YAML.load_file(file_Full_Path))
end
However this code is inside a Before Cucumber Hook. So When the instance variables are loaded they are not available for next scenario. How can I make them run only once and then be available for all scenarios and all features - ie globally. Currently its loading everytime for a scenario.
Before do
....
end
Note: By doing above , we can directly access a YAML_FileName by #YAML_FileName variable without writing code. Thats my intention. Same happens with all excel files, json files etc etc. They are all called from inside Hooks.

EDIT: added the "quick" solution
Quick solution
You are using class variables (#var) now; these are subject to some Cucumber magic so they are cleaned out after each scenario, as you noticed. This makes them rather useful as well, giving you a "clean slate" while allowing you to transfer some state from one step to the next.
This is all just Ruby, so you can of course use global ruby variables ($var) instead. This still requires you to change your existing code.
If you want to keep your class variables #var and still have them to stick around, you could do hooks to save and restore them in globals, i.e.:
Around do |scenario, block|
#var = $var
begin
block.call
ensure
$var = #var
end
end
You'll want to make sure this hook is encountered early enough so it fires before any other hooks you might have.
I don't like to use globals in any language, so I try not to use either #var or $var in Cucumber. Instead, I prefer to do it the OOP way... hence:
"Clean" solution (PORO)
I use a singleton module for such things:
# support/000_cucu_globals.rb
class CucuGlobals
include Singleton
def initialize
#somevalue = 'hello world'
...
end
attr_reader :somevalue
end
Elsewhere:
puts CucuGlobals.instance.somevalue + "!"
Adjust/extend/get creative, as necessary. If you want to avoid typing so much, feel free to use a shorter class name (like G for global). You can also make your accessors class methods instead to even skip the instance in between. This is all just Ruby (not Cucumber) at this point, so there is not Cucumber magic involved anymore.
Hint: you want to load this module pretty early. I'm sure there is a good way to do that "cleanly", but I opted to just name it in such a way that is found first, by Cucumber (000_...).

Related

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

Shadowing a top-level constant within a binding

I would like to shadow ENV within a templating method, so that I can raise an error if keys are requested which are not present in the real ENV. Obviously I don't want to shadow the constant elsewhere - just within a specific method (specific binding). Is this even possible?
Explainer: - I know about the existence of Hash#fetch and I use it all the time and everywhere. However, I want to use this in an ERB template generating a config file. This config file is likely to be touched by more people than usual, and not everyone is familiar with the Ruby behavior of returning a nil for a missing Hash key. I am also working on a system where, of late, configuration mishaps (or straight out misconfigurations, or misunderstandings of a format) caused noticeable production failures. The failures were operator error. Therefore, I would like to establish a convention, within that template only, that would cause a raise. Moreover, I have a gem, strict_env, that does just that already - but you have to remember to use STRICT_ENV instead of just ENV, and every "you have to" statement for this specific workflow, in this specific case, raises a red flag for me since I want more robustness. I could of course opt for a stricter templating language and use that language's logic for raising (for example, Mustache), but since the team already has some familiarity with ERB, and Rails endorses ERB-templated-YML as a viable config approach (even though you might not agree with that) it would be nice if I could stick to that workflow too. That's why I would like to alter the behavior of ENV[] locally.
ERB#result takes an optional binding:
require 'erb'
class Foo
ENV = { 'RUBY_VERSION' => '1.2.3' }
def get_binding
binding
end
end
template = "Ruby version: <%= ENV['RUBY_VERSION'] %>"
ERB.new(template).result
#=> "Ruby version: 2.1.3"
b = Foo.new.get_binding
ERB.new(template).result b
#=> "Ruby version: 1.2.3"
You can use ENV.fetch(key) to raise when the key is not present.
Other than that you could create a class and delegate to ENV, such as:
class Configuration
def self.[](key)
ENV.fetch(key)
end
end
But raising an error from #fetch instead #[] is more Ruby-like since this is the same behaviour for Hash.
Finally you could monkey patch ENV, but this is usually not a good thing to do:
def ENV.[](key)
fetch(key)
end
As far as I know you can't use refinements to localise this monkey patch because ENV is an object, not a class and its class is Object.

Ruby: instantiate objects from files

Overview:
main.rb
items/
one.rb
two.rb
three.rb
Every file in items/ should have a human readable description (serialization is out), like so (but maybe a DSL would be better?):
class One < BaseItem
name "Item one"
def meth
"something"
end
main.rb should be able to instantiate all objects from the items/ directory. How could this be accomplished? Not familiar with Ruby, I see the object model allows for some pretty cool things (those class hooks, etc), but I'm having trouble finding a way to solve this.
Any input way appreciated.
EDIT:
Shoot, I may have missed the gist of it - what I didn't mention was the stuff in the items/ dir would be dynamic — treat items as plugins, I'd want main.rb to autodetect everything in that dir at runtime (possibly force a reload during execution). main.rb has no prior knowledge of the objects in there, it just knows what methods to expect from them.
I've looked at building DSLs, considering defining (in main.rb) a spawn function that takes a block. A sample file in items/ would look something like:
spawn do
name "Item name"
def foo
"!"
end
end
And the innards of spawn would create a new object of the base type and pass the block to instance_eval. That meant I'd need to have a method name to set the value, but incidentally, I also wanted the value to be accessible under name, so I had to go around it renaming the attr.
I've also tried the inherit route: make every item file contain a class that inherits from a BaseItem of sorts, and hook into it via inherited ... but that didn't work (the hook never fired, I've lost the code now).
EDIT2:
You could look at what homebrew does with its formulas, that's very close to what I'd want - I just didn't have the ruby prowess to reverse engineer how it handles a formula.
It all boils down to requiring those files, and make sure that you implemented the functionality you want in them.
If you want a more specific response, you need to ask a more specific question.
I am no expert on object persistence, but answer to your specific question is, that you have 2 good choices: One is YAML, and the other is Ruby itself: a DSL written by you or someone else, and specific to your business logic.
But I think that more general answer would require reviewing object persistance in Ruby more systematically. For example, ActiveRecord::Base descendants persists as database tables. There are other ways, I found eg. this http://stone.rubyforge.org/ by googling. This is my problem as well, I'm facing the same question as you in my work.
What you are asking for looks and smells a lot like a normal Ruby script.
class One < BaseItem
name "Item one"
def meth
"something"
end
We'd close the class definition with another end statement. name "Item one" would probably be done inside the initialize method, by setting an instance variable:
attr_reader :name
def initialize(name)
#name = name
end
Typically we wouldn't call the folder "items", but instead it would be "lib", but otherwise what you are talking about is very normal and expected.
Instantiating all items in a folder is easily done by iterating over the folder's contents, requiring the files, and calling the new method for that item. You can figure out the name by mapping the filename to the class name, or by initializing an instance at the end of the file:
one = One.new("item one")
You could keep track of the items loaded in an array or hash, or just hardwire them in. It's up to you, since this is your code.
It sounds like you haven't tried writing any Ruby scripts, otherwise you would have found this out already. Normal Ruby programming books/documentation would have covered this. As is, the question is akin to premature optimization, and working with the language would have given you the answer.

ruby module_function vs including module

In ruby, I understand that module functions can be made available without mixing in the module by using module_function as shown here. I can see how this is useful so you can use the function without mixing in the module.
module MyModule
def do_something
puts "hello world"
end
module_function :do_something
end
My question is though why you might want to have the function defined both of these ways.
Why not just have
def MyModule.do_something
OR
def do_something
In what kind of cases would it be useful to have the function available to be mixed in, or to be used as a static method?
Think of Enumerable.
This is the perfect example of when you need to include it in a module. If your class defines #each, you get a lot of goodness just by including a module (#map, #select, etc.). This is the only case when I use modules as mixins - when the module provides functionality in terms of a few methods, defined in the class you include the module it. I can argue that this should be the only case in general.
As for defining "static" methods, a better approach would be:
module MyModule
def self.do_something
end
end
You don't really need to call #module_function. I think it is just weird legacy stuff.
You can even do this:
module MyModule
extend self
def do_something
end
end
...but it won't work well if you also want to include the module somewhere. I suggest avoiding it until you learn the subtleties of the Ruby metaprogramming.
Finally, if you just do:
def do_something
end
...it will not end up as a global function, but as a private method on Object (there are no functions in Ruby, just methods). There are two downsides. First, you don't have namespacing - if you define another function with the same name, it's the one that gets evaluated later that you get. Second, if you have functionality implemented in terms of #method_missing, having a private method in Object will shadow it. And finally, monkey patching Object is just evil business :)
EDIT:
module_function can be used in a way similar to private:
module Something
def foo
puts 'foo'
end
module_function
def bar
puts 'bar'
end
end
That way, you can call Something.bar, but not not Something.foo. If you define any other methods after this call to module_function, they would also be available without mixing in.
I don't like it for two reasons, though. First, modules that are both mixed in and have "static" methods sound a bit dodgy. There might be valid cases, but it won't be that often. As I said, I prefer either to use a module as a namespace or mix it in, but not both.
Second, in this example, bar would also be available to classes/modules that mix in Something. I'm not sure when this is desirable, since either the method uses self and it has to be mixed in, or doesn't and then it does not need to be mixed in.
I think using module_function without passing the name of the method is used quite more often than with. Same goes for private and protected.
It's a good way for a Ruby library to offer functionality that does not use (much) internal state. So if you (e.g.) want to offer a sin function and don't want to pollute the "global" (Object) namespace, you can define it as class method under a constant (Math).
However, an app developer, who wants to write a mathematical application, might need sin every two lines. If the method is also an instance method, she can just include the Math (or My::Awesome::Nested::Library) module and can now directly call sin (stdlib example).
It's really about making a library more comfortable for its users. They can choose themself, if they want the functionality of your library on the top level.
By the way, you can achieve a similar functionality like module_function by using: extend self (in the first line of the module). To my mind, it looks better and makes things a bit clearer to understand.
Update: More background info in this blog article.
If you want to look at a working example, check out the chronic gem:
https://github.com/mojombo/chronic/blob/master/lib/chronic/handlers.rb
and Handlers is being included in the Parser class here:
https://github.com/mojombo/chronic/blob/master/lib/chronic/parser.rb
He's using module_function to send the methods from Handlers to specific instances of Handler using that instance's invoke method.

Avoiding globals with dynamically loaded ruby files

I am in a position at the moment where I have a plugins folder where there there could be 1 or 100 plugins to be loaded.
Now the problem is, each plugin requires an instance of a class defined within the startup ruby file.
A really simplified example would be:
#startup.rb
def load_plugins
#... get each plugin file
require each_plugin
end
class MuchUsedClass
def do_something
#...
end
end
muchUsedInstance = MuchUsedClass.new
load_plugins
#some_plugin.rb
class SomePluginClass
def initialize(muchUsedInstance)
#muchUsedInstance = muchUsedInstance
end
def do_something_with_instance
#muchUsedInstance.do_something
end
end
somePluginInstance = SomePluginClass.new(muchUsedInstance)
somePluginInstance.do_something_with_instance
The main problem is that when you call require, it doesn't have any clue about what has happened before it is being required. So I find it nasty making a global variable within the startup file just to satisfy all other required files, but it seems like one of the only ways to be able to pass some data down to an included file, I could also make a singleton class to expose some of this, but that also seems a bit nasty.
As I am still new to ruby and am still looking through the statically typed glasses, I will probably be missing a decent pattern to solve this, in C# I would opt for dependency injection and just hook everything up that way...
Your example code does not have a global variable. Global variables have names that start with $. The code as you wrote it won't work, because muchUsedInstance is just a local variable and will not be shared between different Ruby files.
If you are not going to change the instance ever, you could easily store it as a constant:
MuchUsedInstance = MuchUsedClass.new
You could store it as a nested constant inside the class:
MuchUsedClass::Instance = MuchUsedClass.new
You could store it as an instance variable inside the class object, with a getter method that automatically creates it if it isn't there already:
def MuchUsedClass.instance
#instance ||= MuchUsedClass.new
end

Resources