I need to include EventMachine::Deferrable in to Faraday::Response, but only if the response is created while the reactor loop is running. I know I can include a module in to a response object rather than the class, but in my case I cannot do it because of the way the context code is written.
I have a method that is invoked when the loop is running, maybe I can do it there? the below code does not work, because It simply creates a new class in the method's namespace.
def f
module Faraday
class Response
include EventMachine::Deferrable
end
end
...
end
Any advice from the language experts?
here is how I am doing it so far. is there any better way?
module Faraday
class Response
alias :initialize_old :initialize
def initialize env=nil
initialize_old env
extend EventMachine::Deferrable if EventMachine.reactor_running?
end
end
end
Related
I wrote this module:
module Hooks
module ExecutionHooks
def before_action(hook, *method_names)
method_names.each do |method_name|
method = method(method_name)
define_singleton_method(method_name) do |*args, &block|
method(hook).call(*args)
method.call(*args, &block)
end
end
end
end
def self.included(base)
base.send(:extend, Hooks::ExecutionHooks)
end
end
This module allows other modules or classes to define a hook which should be called before a particular action similar to a before_action in Rails.
Then I included this module in my HTTParty module:
module HTTParty
include Hooks
before_action :perform_action, :get
def self.perform_action
puts "performed"
end
end
There is a class which includes the HTTParty module:
class TestClient
include HTTParty
...
end
When I try to access the get method in TestClient, it doesn't call perform_action. The get method being included here is original one, not the redefined one.
Is there a way to include the redefined get method in the TestClient class?
Your code almost works, but get isn't actually defined directly on HTTParty, which you didn't expect, and HTTParty's included class method adds get to your class through another path.
HTTParty has a module called HTTParty::ClassMethods that contains get, etc. It puts them in two places: on HTTParty itself so you can call HTTParty.get, and on any class with include HTTParty, via the included hook. When you open up module HTTParty and include Hooks, you're inserting hooks on HTTParty.get, which is a different lookup chain that when you call TestClient.get. Leaving your Hooks::ExecutionHooks module alone, I recommend making a HookedHTTParty module instead of monkeypatching HTTParty. That will make it more clear what's going on and avoid the complexity of HTTParty's internals, which we shouldn't really be fiddling with.
# hooked_httparty.rb
require 'httparty'
require 'hooks'
module HookedHTTParty
module ClassMethods
def global_perform(*args)
puts "Running global perform with args #{args.inspect}"
end
end
def self.included(base)
base.include(HTTParty)
base.include(Hooks)
base.extend(HookedHTTParty::ClassMethods)
base.before_action :global_perform, :get
end
end
This makes sure HTTParty and Hooks are available on base, and then extends it with the global_perform hook on every get. The primary different from your initial code is that before_action gets called on base (TestClient) instead of on HTTParty, so we catch the right get method. You'll also notice global_perform accepts *args, since you're calling it that way when you generate the hook.
Because we include Hooks, you now also have access to before_action in TestClient itself, so you can also define more specific before_actions:
class TestClient
include HookedHTTParty
before_action :local_perform, :get
def self.local_perform(*args)
puts "Running local perform with args #{args.inspect}"
end
end
Running get looks like this:
> TestClient.get 'https://www.stackoverflow.com'
Running local perform with args ["https://www.stackoverflow.com"]
Running global perform with args ["https://www.stackoverflow.com"]
=> #<HTTParty::Response:0x7fa523a009e0 ... >
If you really need anything including HTTParty to get your hooks (perhaps because you don't have control of the thing including it), you might need to monkeypatch HTTParty::ClassMethods directly, since that's the bottleneck where get is defined, but that's getting into even darker territory. As long as you're injecting code around, you could also OtherThing.include(HookedHTTParty) to make it more explicit and keep it just a bit more encapsulated.
I know that in Cucumber that I can create a module and then include that module into the World object and all the methods that I have created within the newly created module are available globally
module MyModule
def my_method
end
end
World(MyModule)
Now anywhere in my cucumber tests I can call my_method and it will work
The issue I see here and an issue I have come across is duplication of method names, as the application gets bigger or other testers/developers work on the application.
So if I was to wrap everything up in its own module and create module methods like so
module MyModule
def self.my_method
page.find('#element')
end
end
World(MyModule)
MyModule.my_method
# This will return undefined variable or method 'page' for MyModule module
So being new to using modules I have read that you can extend other modules so that you can use those methods within another module
So to access the page method I would need to access Capybara::DSL
module MyModule
extend Capybara::DSL
def self.my_method
page.find('#element')
end
end
World(MyModule)
MyModule.my_method now works, but my question is rather than extend individual namespaces for every module that I need access to is there a way to extend/include everything or is this a bad practice?
Another example of where things fail are when I try to access instances of a class
module SiteCss
def login_page
Login.new
end
end
class Login < SitePrism::Page
element :username, "#username"
end
module MyModule
extend Capybara::DSL
def self.my_method
page.find('#element')
login_page.username.set('username')
end
end
World(MyModule)
So with this example if I was it try and call login_page.username I would get
undefined method `login_page`
I'm really looking for the correct way to be doing this.
In conclusion I am trying to understand how to use custom modules and classes in cucumber without having to load everything into the World object.
Yes, it's not pretty to extend a module multiple times, if that's really what you want to do, I can suggest a way you can improve it a bit:
base class which will inherit your page object framework and extend(include is probably the correct option here):
module Pages
class BasePage < SitePrism::Page
extend Capybara::DSL
end
end
Now your login class:
module Pages
class Login < BasePage
element :username, "#username"
def yourmethod
page.find('#element')
username.set('username')
end
end
end
Now the bits you are probably interested in, expose yourmethod to cucumber world:
module SiteCss
def page_object
#page_object ||= Pages::Login.new
end
end
World(SiteCss)
Now you should have access to yourmethod in a cleaner way...
Usage:
#page_object.yourmethod
Although the most important suggestion I could give you is run from SitePrism... Create your own page object framework... don't fall into SitePrism trap...
I'm trying to understand how this works and would be great if someone could help me. I've got a module which looks like the below:
module Activity::Track
extend ActiveSupport::Concern
module ClassMethods
def track_now(name, options={})
add_activity activity(verb: name)
end
end
protected
def activity(options={})
end
def add_activity(activity_item)
end
end
What I'm doing is calling the track_now method from my model, which is fine and works well but I can't call the add_activity and activity methods. I don't understand how I can call them from within the ClassMethods module. Can someone explain how I can do this?
I have seen this in another project but the protected methods were called by creating a define_method and then calling this via send :after_create :define_method. How does this work?
It seems you want to call an instance method from a class method. I don't recommend you doing that, but if you really want, you can try as following:
def track_now(name, options={})
self.new.instance_eval {add_activity activity(verb: name)}
end
In the end I created a new file track_now.rb which was a duplicate of track.rb but made all the methods a ClassMethod.
Does anyone know how I can use the protected methods from the instance and class methods from both files, this would be a great benefit?
I've defined a Sinatra helper in the usual way:
module Sinatra
module FooHelper
# code goes here
end
end
In my helper, among other things, I'd like to add a method to Numeric:
module Sinatra
module FooHelper
class ::Numeric
def my_new_method
end
end
end
end
However, in the interests of being unobtrusive, I only want to do add this method if my Sinatra helper is actually included in an application; if nobody runs helpers Sinatra::FooHelper, I don't want to affect anything (which seems like a reasonable thing to expect of a Sinatra extension).
Is there any hook that's fired when my helper is included, that would enable me to add my method only once that happens?
You can use the Module#included method to do this. I think you will need to modify your class definition slightly to use class_eval. I've tested the following and it works as expected:
module Sinatra
module FooHelper
def self.included(mod)
::Numeric.class_eval do
def my_new_method
return "whatever"
end
end
end
end
end
I have a situation where I can access a module's functions from one file but not another. These files are both in the same directory. I'll try to recreate the code the best I can:
Directory Structure:
init.rb
lib/FileGenerator.rb
lib/AutoConfig.rb
lib/modules/helpers.rb
lib/AutoConfig.rb
#!/usr/bin/env ruby
require 'filegenerator'
require 'modules/helpers'
class AutoConfig
include Helpers
def initialize
end
def myFunction
myhelper #here's the module function
end
def mySecondFunction
FileGenerator.generatorFunction # call to the FileGenerator
end
end
lib/FileGenerator.rb
#!/usr/bin/env ruby
require 'modules/helpers'
class FileGenerator
include Helpers
def initialize
end
def self.generatorFunction
myhelper #here's the module function that doesn't work
end
end
lib/modules/helper.rb
#!/usr/bin/env ruby
module Helpers
def myhelper
#Do Stuff
end
end
The AutoConfig file is the main workhorse of the app. When it calls to the myhelper module function it gives me no problems what-so-ever. The AutoConfig part way through calls the FileGenerator.generatorFunction.
The FileGenerator.generatorFunction also contains this same module function, but for some reason when I run the program I get the following error:
filegenerator.rb:26:in `generatorFunction': undefined method `myhelper' for FileGenerator:Class (NoMethodError)
I've been at this now for several hours trying many different combinations and can't figure out where I'm going wrong. Any help would be appreciated.
generatorFunction is a class method. It doesn't see instance-level methods. And myhelper (brought in by include Helpers) is an instance method. To remedy that, you should extend Helpers instead. It works like include, but makes class methods.
class FileGenerator
extend Helpers
end
BTW, the name generatorFunction is not in ruby style. You should name methods in snake_case (generator_function).