Avoiding dependency load order - ruby

I am writing a gem that looks as such:
lib/my_gem.rb:
require 'base64'
require 'ostruct'
require 'my_gem/utils.rb'
require 'my_gem/base.rb'
...
This has been fine until recently when the gem has added more functionality and the lib/my_gem directory has grown and grown.
Now, I'm having to be really careful to require my classes and modules in a very specific order because something in utils requires that base.rb be loaded first. However, something in base.rb requires that app.rb be loaded before that.
So it turns into:
# require all standard libraries first
require 'base64'
require 'ostruct'
require 'my_gem/app.rb' # be sure this is loaded before base!
require 'my_gem/base.rb' # be sure this is loaded before utils!
require 'my_gem/utils.rb' # be sure this is loaded before some other class!
I end up having a mess in this file all due to order of dependencies and I feel like there has to be a better way?

Try using Kernel#autoload:
require 'base64'
require 'ostruct'
autoload :SomeModule, 'my_gem/app.rb'
autoload :AnotherModule, 'my_gem/base.rb'
autoload :SomeClass, 'my_gem/utils.rb'
The idea is that the source file is not loaded until the module/class defined in it is used, therefore you don't need to take care of the order of requiring source files.

Related

How to require file from `gem` which are not under `lib` directory?

I want to write spec for my rubocop custom cop. This gem has handy helpers defined here. I want to require it. How to achieve what?
I've tried to use Gem.find_files, and this gives me ability to require any file in that gem, but only under lib directory.
For example:
# this requires ...gems/rubocop-0.29.1/lib/rubocop/formatter/formatter_set.rb
require Gem.find_files('rubocop/formatter/formatter_set.rb').first
# but I need ...gems/rubocop-0.29.1/spec/support/cop_helper.rb
The following describes why I need it. I have spec/rubocop/my_custom_cop_spec.rb
require 'spec_helper'
require ? # What I should I write?
RSpec.describe RuboCop::Cop::Style::MyCustomCop do
it 'some test' do
inspect_source(cop, 'method(arg1, arg2)') # This helper I want to use from rubocop spec helpers
end
end
When I try plain require:
require 'rubocop/spec/support/cop_helper'
I receive error:
/home/user/.gem/ruby/2.0.0/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:274
:in `require': cannot load such file -- rubocop/spec/support/cop_helper
I found a solution that I think is a little more syntactically elegant:
gem_dir = Gem::Specification.find_by_name("my_gem").gem_dir
require "#{gem_dir}/spec"
I was so blinded, I already have path to file and able to get relative from it.
require 'pathname'
rubocop_path = Pathname.new(Gem.find_files('rubocop.rb').first).dirname
rubocop_path # => ...gems/rubocop-0.29.1/lib
require "#{rubocop_path}/../spec/support/cop_helper.rb"

Monkey Patching Mongoid models contained in a Ruby gem

I have a Ruby gem which gets used across multiple projects that contains some Mongoid models.
I'm currently trying to reuse them in a project and monkey patch some extra methods. However, when I require the project and run the tests. I get a NoMethodError. Any ideas where I'm going wrong?
Here's my main project file:
require 'bundler'
Bundler.require(:default)
require 'mongoid-elasticsearch'
Mongoid::Elasticsearch.prefix = ENV["MONGOID_ENVIRONMENT"]
require 'mongoid_address_models/require_all' # This is where I include my gem
Mongoid.load!(File.join(File.dirname(__FILE__), "..", "config", "mongoid.yml"), ENV["MONGOID_ENVIRONMENT"] || :development)
# Here are my monkey patched models
require 'models/street'
require 'models/locality'
require 'models/town'
require 'models/postcode'
require 'sorting_office/address'
module SortingOffice
end
And this is an example of one of the monkey patched models
class Postcode
REGEX = /([A-PR-UWYZ01][A-Z01]?[0-9IO][0-9A-HJKMNPR-YIO]\s?[0-9IO][ABD-HJLNPQ-Z10]{2})/i
def self.calculate(address)
postcode = UKPostcode.new(address.match(REGEX)[0])
where(name: postcode.norm).first
end
end
When I call Postcode.calculate(address) (for example), I get a NoMethodError
I think I've nailed this now. Rather than putting the monkey patch inside lib/models, I moved them to lib/sorting_office/models and required them like so:
require 'sorting_office/models/street'
require 'sorting_office/models/locality'
require 'sorting_office/models/town'
require 'sorting_office/models/postcode'
I'd still be interested to know WHY this worked though

Environment configuration for Sinatra error

I have an environment config file at config/environment.rb with the following:
require 'rubygems'
require 'bundler'
Bundler.setup
require 'sinatra'
require 'sinatra/base'
require 'sinatra/reloader'
and in my config.ru I have:
require File.expand_path('../config/environment', __FILE__)
require 'slim'
require 'coffee-script'
require 'padrino-helpers'
require 'sinatra/twitter-bootstrap'
I am getting the error :
Errno::ENOENT at /profile
No such file or directory - /Users/myusername/projects/accounts/config/views/profile.slim
and this only goes away when I remove require 'sinatra' from the config/environment.rb file and into config.ru. Can anyone explain why this happens? I assumed that the require File.expand_path('../config/environment', __FILE__) will simply include all requires from that file into config.ru but that doesn't seem to be the case. It now thinks my views live inside the config folder.
I followed the suggestion given here: How do I make Rake tasks run under my Sinantra app/environment? but again, moving require 'sinatra' into the environment breaks the app.
The error looks like it is from Sinatra being unable to find a Slim template when rendering a response, because it’s looking for a views directory under the config directory. By default Sinatra looks for the views dir relative to the application file, which (again by default) is the file that calls require 'sinatra'. In your case the require line is in config/environment.rb so Sinatra treats that as the app file and looks for the views dir below it.
I’m assuming you have an actual application file that you haven’t shown. The simplest solution is probably to explicitly set the application file setting in there:
set :app_file, __FILE__
Depending on your setup you might want to specify the view directory directly instead:
set :views, 'path/to/views'

Ruby 'require' unable to load such a file

I want to require file all_class from file log_in_test.rb.
I tried all kinds of require and require_relative, for example
#require "../../ib4b-template/features/page-object/all_class"
require_relative "page-object/all_class"
and they are not working. Can anyone kindly give me a hand?
Try this:
require_relative 'all_class' # without 'page-object/'
The most common way to solve this problem is using File.dirname(__FILE__):
require File.join(File.dirname(__FILE__), '..', 'all_class')
Use
require_relative '../page_object/all_class'
relative to support/log_in_test.rb, all_class.rb lies in above path.
require doesn't find if the directory structure is not in the Ruby's library path.
require_relative './page_object/all_class.rb'
or
require_relative './page_object/all_class'
the '.rb' part is optional
You should use require_relative instead of require when you are working on a project and want to require one file from one of the directories into another.
You should use require to add gem libraries to your project.
Also, a good practice to use when working on a larger practice is to make a environment.rb file in the config folder and have that require all the the models and then having the executables requiring that environment file.
require File.join(File.dirname(__FILE__), '..', '..','page-object', 'all_class')

Do we need any database.yml file for Ruby on rails application using Redis

I have created a Ruby on Rails application without active records:-
$ rails new rails_using_redis_demo_app --skip-active-record
Now in order to perform database operation to i have to include database.yml file containing the necessary details regarding the database adapter.
I have also include follwing in my gemfile:-
gem 'redis'
I also found that redis actually stores the data in a .rdb file with key value pair, which can be accessed with the help of keys present. Am I doing things in a right manner.
Have you removed all occurences to ActiveRecord in your Rails config files ?
Perhaps you should check your application.rb file.
A typical application.rb file includes
require 'rails/all'
That is what you should write instead to fully remove ActiveRecord
# Pick the frameworks you want:
# require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "active_resource/railtie"
require "sprockets/railtie"
# require "rails/test_unit/railtie"
Hope that helps...

Resources