Rspec - Working directory - ruby

I have a class that laod config file from config/config.yml.
class Example
def initialize
config = YAML.load_file('config/config.yml')
end
end
I have created proyect/spec/config/config.yml example file but when I run the test it try load file proyect/config/config.yml.
How I change rspec working directory?

Why not point the config to the right path?
class Example
def initialize
config = YAML.load_file('spec/config/config.yml')
end
end
According to this, you can set the load_path like so
$LOAD_PATH.unshift 'spec'
or
$:.unshift 'spec'
Also according to that,
As much as I like removing unnecessary requires in specs I prefer verbosity over magic -- anything that gives the code reader more clarity as to what's necessary for a particular example group.

Related

Rails and MiniTest: How to write tests for something in app/lib/mymodule (Class not found)?

Rails 5.2 here.
I want to test a class defined in app/lib/legacy/export.rb:
# app/lib/legacy/export.rb
module Legacy
class Export
def initialize ; end
end
end
However, a test in test/services/legacy_export_test.rb
# test/services/legacy_export_test.rb
require 'test_helper'
class LegacyExportTest < ActiveSupport::TestCase
test 'can be initialized' do
Legacy::Export.new
end
end
will spit out NameError: uninitialized constant Legacy::Export.
It works well if I put the class definition in app/lib/export.rb (and remove the module definition).
I can also reference this class in Controllers and in the rails console (rails c).
Trying to reference the class starting with the top-level-"namespace" (::Legacy::Export) does not help either. I find answers to questions how to reference lib folders (and subdirectories) in the test/ folder, but this is not what I need.
require 'lib/legacy/export will tell me cannot load such file, as will require 'legacy/export'.
I assumed that the (Auto-)Load-stuff of Rails and MiniTest are the same, but obviously there is some additional configuration to be done.
What has to be done? Where would I find this information?
The problem is that your class namespace / class path doesn't match how Rails autoloading works out of the box.
When you use a class that wasn't previously declared, Rails by default will look on specific paths (defined on config.autoload_paths)
app/controllers
app/controllers/concerns
app/models
app/models/concerns
...
When you use User for the first time, as it's not defined (yet) it will loop over those paths and try to require app/controllers/user.rb, app/controllers/concerns/user.rb, app/models/user.rb, until it founds the User class
if your class is namespaced as Legacy::Export, then it will look for app/models/legacy/export.rb, app/models/concerns/legacy/export.rb, app/controllers/legacy/export.rb, etc.
That's why it can't find your class: Your file is located on app/lib, that's not within the paths Rails use to look for.
There are different solutions:
Option #1
Require the file explicitly. (The Ruby way)
require_relative '../../app/lib/legacy/export'
Option #2
Add app/lib to autoload_path (in config/application.rb)
(The Rails Way)
module YourApp
class Application < Rails::Application
# ...
config.autoload_paths << Rails.root.join("app/lib")
end
end
Option #3
Adapt namespace to match what autoloading expects (instead of changing the configuration)
Example: move you file to something like app/models/legacy/export.rb

How to parse and dump Ruby config files?

In this blog post he gives this example of a Ruby config file.
config do
allow ['server.com', `hostname`.strip]
vhost 'api.server.com' do
path ‘/usr/local/api’
end
vhost 'www.server.com' do
path '/usr/local/web'
end
%w{wiki blog support}.each do |host|
vhost "#{host}.server.com" do
path "/usr/local/#{host}"
end
end
end
I think of a hash after a config file have been loaded, but maybe that is not how this type of configs are intended for...
Update
If I execute it, I get
$ ruby config.rb
config.rb:2:in `<main>': undefined method `config' for main:Object (NoMethodError)
Question
What Ruby code is needed to parse and dump the content of this config file?
That config example is not directly loadable and, if I understand the blog post author correctly, it's not meant to be either so there's no easy way of loading/parsing that example.
The key part is in the blog post where he states "build simple DSLs to design semantically robust config files without the underlying ruby being conspicuous" (my emphasis). The 'underlying ruby' I take to mean the code that enables the DSL elements you're seeing such as 'config' and 'vhost'.
Your original question was, however, what code is required to load that config - below is a sample of something would work, full implementation is up to you and tbh I'm pretty sure there are cleaner, "better" ways of doing the same.
class AppConfig
attr_accessor :hosts
def allow(hosts)
#hosts = hosts
end
def vhost(hostname)
end
def process_config(&block)
instance_eval(&block)
end
end
def config(&block)
config = AppConfig.new
config.process_config &block
puts "Hosts are: #{config.hosts}"
end
load 'config.rb'

Load Error in require statement when using classes in multiple files

I'm pretty new to Ruby and facing a pretty basic problem i guess. I'm probably missing out on some basic concepts and constructs. So this is what i'm trying to do,
I'm writing a sinatra project, and i have a classes which are written in different files. The structure looks something of this sort,
project_name
- api.rb
- base.rb
- settings.rb
In my api.rb file i have defined a class and some methods, it also calls some methods form base.rb and base.rb calls some methods from settings.rb
In api.rb
require 'sinatra'
require 'json'
require 'uri'
require 'base' --> This is the base.rb which is resulting in error
module XX
class Api
def some_method
base = Base.new
base.setup
# some more code
end
end
end
In base.rb, it has the following code
require 'settings'
module XX
class Base
def setup
# some code
end
def some_method
#some code
end
end
end
When i just run ruby api.rb, i'm getting an error in the require statement, unable to load such file-- base (LoadError).
What is it that i'm missing here? Also, how is it that ruby know whether it a gem or a file required..does it check to see if the require is a file in the project and then goes on to check for a gem ? How is this process done in ruby?
Any help is much appreciated!

Ruby script, best method to store login information for an API?

I'm currently working on a script (command line tool) for work to help me manage expose consoles.
At first I was passing three arguments to the script each time I used it to login into the consoles, for example:
$ nexose-magic.rb -u user -p password -i 192.168.1.2 --display-scans
It's not very efficient, so I created a config.yml file that stores console information in a hash.
$ nexpose-magic.rb -c console --display-scans
I believe the tool will be useful to admins out there, so I'd like to share it in a gem. I can't figure out how to get my config.yml file to work with a gem install..It can't find the config.yml file! It's easy to point it at the relative path in my development directory, but once I create a gem that relative path isn't so relative anymore. How do I point nexpose-magic.rb at the config.yml file?
Is there a better way to handle something like this?
You can create a gem that include a configure class. This class has a load method that will take a directory as an argument. Then, you can pass the directory where you are currently working.
A nice way of preparing your gem is to create a Configuration singleton class in your gem:
require 'singleton'
class Configuration
include Singleton
attr_accessor :config, :app_path
def load(app_path)
#app_path = app_path
#load the config file and store the data
#config = YAML.load_file( File.join(#app_path,'config','config.yml'))
end
end
In your main class :
module MyFancyGem
class << self
#define a class method that configure the gem
def configure(app_path)
# Call load method with given path
config.load(app_path)
end
# MyFancyGem.config will refer to the singleton Configuration class
def config
MyFancyGem::Configuration.instance
end
end
end
Usage:
-Working directory
- my_new_script
- Gemfile
- config/
- config.yml
In my_new_script :
require 'bundler'
Bundler.setup(:default)
require 'my_fancy_gem'
MyFancyGem.configure(File.join(File.dirname(__FILE__),"./")) #here, you define the path
MyFancyGem.hello_world
I hope that's clear enough. I was actually about to write a blog post to explain this particular point (I hope in a more complete version of it). Let me know if you're interested !

RSpec locating resource path

I have a little project (non Rails) and I'm using RSpec for testing. In order to load models I'm using:
require_relative "../lib/checkout"
However I'm encountering the problem with loading config files, for instance, the test no longer locates my "items.csv" when the following:
CSV.foreach("items.csv") do |row|
Note that the problem occurs only when spec is run from the spec directory, i.e:
rspec checkout_spec.rb
Running it from the project root is fine:
rspec spec/checkout_spec.rb
Any help would be appreciated.
CSV.foreach uses the current directory to find the file. You should probably use File.expand_path to get to an absolute path to items.csv to avoid this problem.
Edited to add an example
Assuming that the file is at root/items.csv, and the ruby file with this code is at root/lib/file.rb, you could write
path = File.expand_path File.join(File.dirname(__FILE__), '..', 'items.csv')
CSV.foreach path do |row|
# rest of code...

Resources