Using mixins during code parsing in Ruby - ruby

I want my Configurable module to include a config command to whatever app includes it.
edit: see the update on my reasoning a little further down
I get that error:
cli.rb:2:in '<module:Configurable>': undefined method 'desc' for Configurable:Module (NoMethodError)
I want to do the following in a commandline:
$ app something
> I did something!
$ app config
> You configured the app!
> I did something!
So here's the code:
# app.rb
require 'thor'
require_relative './cli'
class App < Thor
include Configurable
# def initialize ...
desc "something", "The Cli app does something"
def something
puts "I did something!"
end
end
# cli.rb
module Configurable
desc 'config', "You can configure the app"
def config
puts "You configured the app!"
# You can even call App 'something' method
something
end
end
As the above points out, when I comment out the desc 'config' .. line, it builds and runs, although Thor doesn't add the config command.
Thanks for your help!
UPDATE
I downloaded Thor's repo, and added traces when it encountered a desc command. So I tried calling Thor.desc instead, and I could see that it was loaded in Thor's system, but still didn't appear in commands list. So I played around and ended up with the following:
module Configurable
Thor.desc 'config', 'You can configure the app'
def config
puts "You configured the app!"
# You can even call App 'something' method
something
end
def self.included(klass)
puts "The module was included"
klass.desc "another", "another one"
end
def another
puts "Another!"
end
end
This way, I tested if calling desc before or after made a difference, but it didn't.
At this point, I would say it's Thor's limitations, and I can't achieve what I want with Thor and mixins.

desc is a method of Thor's singleton class, but Configurable isn't an instance of Thor's singleton class. (Obviously.) That's why you can't call desc without a receiver, you will have to call it explicitly:
(Note: I don't have Thor, therefore, I cannot test this. It may or may not work.)
module Configurable
Thor.desc 'config', 'You can configure the app'
def config
puts "You configured the app!"
# You can even call App 'something' method
something
end
end

Related

Make module method available without scoping

I am trying to do something very similar to rspec's describe.
Say I have a module in my_mod.rb
module MyMod
def say_hello(name)
puts "hello #{name}"
end
end
And I use it in another file foo.rb
include MyMod # without the include it cannot find say_hello
say_hello "world"
My question is - how do I get this to work like rspec, i.e. require-ing the module should make the methods available, without having to do MyMod.say_hello.
Change your original foo.rb, replacing include with extend.
This way, you can use say_hello "world"
But, if you really want to use require, define the method without the module:
my_mod.rb:
def say_hello(name)
puts "hello #{name}"
end
then, in foo.rb:
require 'my_mod'
say_hello "world"

How to define a simple global variable in an rspec test that can be accesed by helper functions

I cant figure out how to use a simple global variable in an rspec test. It seems like such a trivial feature but after much goggleing I havent been able to find a solution.
I want a variable that can be accessed/changed throughout the main spec file and from functions in helper spec files.
Here is what I have so far:
require_relative 'spec_helper.rb'
require_relative 'helpers.rb'
let(:concept0) { '' }
describe 'ICE Testing' do
describe 'step1' do
it "Populates suggestions correctly" do
concept0 = "tg"
selectConcept() #in helper file. Sets concept0 to "First Concept"
puts concept0 #echos tg?? Should echo "First Concept"
end
end
.
#helpers.rb
def selectConcept
concept0 = "First Concept"
end
Can someone point out what I am missing or if using "let" is totally the wrong method?
Consider using a global before hook with an instance variable: http://www.rubydoc.info/github/rspec/rspec-core/RSpec/Core/Configuration
In your spec_helper.rb file:
RSpec.configure do |config|
config.before(:example) { #concept0 = 'value' }
end
Then #concept0 will be set in your examples (my_example_spec.rb):
RSpec.describe MyExample do
it { expect(#concept0).to eql('value') } # This code will pass
end
It turns out the easiest way is to use a $ sign to indicate a global variable.
See Preserve variable in cucumber?
This is an old thread, but i had this question today. I just needed to define a long string to stub out a command that is in multiple files as:
# in each spec file that needed it
let(:date_check) do
<<~PWSH.strip
# lots of powershell code
PWSH
end
# in any context in that file (or a shared context)
before(:each) do
stub_command(date_check).and_return(false)
end
Searched, Stack Overflow, etc, landed on this: Note the usage of the variable doesn't change at all! (Assumes all specs require 'spec_helper')
# in spec_helper.rb
def date_check
<<~PWSH.strip
# lots of powershell code
PWSH
end
# in any context in any spec file
before(:each) do
stub_command(date_check).and_return(false)
end
I suggest you define the variable in the helper file, where it can be used by other helper code, and can be accessed from your tests.
For my project, I wanted to keep all the setup stuff in spec_helper.rb, and use those settings, plus any custom variables and methods in the tests. The following, modified from the RSpec-core 3.10 docs, is not Rails-specific.
Create a new setting for RSpec.configure called my_variable, and give it a value, like this:
# spec/spec_helper.rb
RSpec.configure do |config|
config.add_setting :my_variable
config.my_variable = "Value of my_variable"
end
Access settings as a new read-only property in RSpec.configuration from your test:
# spec/my_spec.rb
RSpec.describe(MyModule) do
it "creates an instance of something" do
my_instance = MyModule::MyClass.new(RSpec.configuration.my_variable)
end
end

How do you set the WEBRick options parameter in the run method with Padrino

I don't want to monkey patch Padrino.
I still want to be able to use the command padrino start -d from the command line.
I want to get SSL up and running within padrino. Within Sinatra I just do:
Rack::Handler::WEBrick.run MyServer, MyServerOptionsWithAppropriateSSLStuffEtc
I found the file deep inside the Padrino core that handles setting these options, but I really don't want to monkey patch the application.
Ideally I'd like there to be be some way I could set the options within my Padrino::Application subclass.
So far I haven't found any documentation on how to do this, or if this is even possible.
mmm, you should be able to do the same.
In your project folder you should see config.ru
Try to edit it removing last line with:
Rack::Handler::WEBrick.run Padrino.application, MyServerOptionsWithAppropriateSSLStuff
Then from command line:
$ rackup
I know this is old, but in case anybody is trying to do this cleanly, here is what I use:
class MyApplication < ::Sinatra::Base
# ...
def self.server_settings
{ key: value, ... }
end
# ...
end
You can also inject settings prior to runtime:
MyApplication.class_exec(server_settings) do |server_params|
def self.server_settings
server_params
end
end
I frequently use the second example for injecting a custom logger into my application for specs.
For example:
module CustomLogger
def logger
settings.try(:server_settings)[:Logger] || request.logger
end
end
MyApplication.class_exec(CustomLogger) do |logger_module|
helpers logger_module
def self.server_settings
# global specified in guard/spec helper
{ Logger: $LOGGER }
end
end
class MyApplication < ::Sinatra::Base
enable :logging
get '/' do
logger.info "FOO"
end
end
MyApplication.run!
See this source link for more info about server_settings usage in Application::self.run!.

Thor Executable - Ignore Task Name

The thor wiki page, Making an Exectable, shows you how to create a thor powered CLI command that looks something like this:
bash
./mythorcommand foo
This requires you to pass in the thor task foo as the first argument.
I can also run a thor executable without any arguments using thor's default_method:
bash
./mythorcommand
However, I'd like to pass in a variable string as the first argument:
bash
./mythorcommand "somevalue"
This doesn't work because thor commands expect the first argument to the be a task name. Is there a way to ignore the task name and send the first argument to a default method?
If this functionality doesn't exist, I think it would be very useful to add a method that would pass all commandline arguments into one task/method:
class MyThorCommand < Thor
only_method :default
def default(*args)
puts args.inpsect
end
end
MyThorCommand.start
You should extend from Thor::Group and that call start method
class Test < Thor::Group
desc "Act description"
def act
puts "do smth"
end
end
Test.start
I found a rather 'strange' solution for this problem that is working quite well with me.
You add a default task to Thor. Than you add the method_missing so that you can trick Thor into passing the default method as an argument if there are parameters to your application.
Taking from your example, the solution would look like this:
class MyThorCommand < Thor
default_task :my_default
desc "my_default", "A simple default"
def my_default(*args)
puts args.inspect
end
def method_missing(method, *args)
args = ["my_default", method.to_s] + args
MyThorCommand.start(args)
end
end
MyThorCommand.start(ARGV)
If this is in the file "my_thor.rb" an execution "ruby my_thor.rb foo bar" would show '["foo", "bar"]' as a result.
Hope it helps.
Though this does not exactly solve your problem, one alternative might be using Thor.map to invoke a command by only giving an option flag:
map '-F' => 'foo'
Now you can also pass parameters
mythorcommand -F bar # => invokes foo("bar")

Require file without executing code?

Here I have two files:
file.rb
def method
puts "This won't be outputted."
end
puts "This will be outputted."
main.rb
require "./file"
When running main.rb it will load all the code inside file.rb so I will get "This will be outputted." on the screen.
Is it possible to load a file without having it to run the code?
Cause I want to load all the methods (in modules and classes too) without having to execute code outside these scopes.
Is it possible to load a file without having it to run the code?
No, everything in a ruby file is executable code, including class and method definitions (you can see this when you try to define a method inside an if-statement for example, which works just fine). So if you wouldn't execute anything in the file, nothing would be defined.
You can however tell ruby that certain code shall only execute if the file is run directly - not if it is required. For this simply put the code in question inside an if __FILE__ == $0 block. So for your example, this would work:
file.rb
def method
puts "This won't be outputted."
end
if __FILE__ == $0
puts "This will not be outputted."
end
main.rb
require "./file"
the if __FILE__ == $0 is nice, but a way more in keeping with ruby's Object Oriented approach is to put all the methods you want access to in a class (as class methods), and then call them from main.rb.
e.g.
file.rb
class MyUtils
def self.method
puts "this won't be outputted"
end
end
and then in main.rb
require "/.file.rb"
and when you want to use your utility methods:
MyUtils.method
I don't think modifying file is good idea - there are could be a lot of files like this one or these files belong to customer and a ton of another reasons.
Ruby is good at metaprogramming so why don't use this feature?
It could be like this.
Create file with fake module and put here the file.
File.open("mfile.rb","w") do |f|
f.write "module FakeModule
"
f.write File.open("file.rb").read
f.write "
end"
end
Then load this file:
require "/.mfile.rb
and accessing to the method:
FakeModule::method

Resources