Where to Place/Access Config File in Gem

Where to place/access config file in gem?

I'm jumping on this one a little late but I'll leave an example implementation of how I generally handle this, for future reference.

As it was mentioned, you'll normally want to allow configuration through both files and hashes. It's pretty easy and light to include both ways, so you should do it.

Something like this works for me in most scenarios:

require 'yaml'

module MyGem
# Configuration defaults
@config = {
:log_level => "verbose",
:min => 0,
:max => 99
}

@valid_config_keys = @config.keys

# Configure through hash
def self.configure(opts = {})
opts.each {|k,v| @config[k.to_sym] = v if @valid_config_keys.include? k.to_sym}
end

# Configure through yaml file
def self.configure_with(path_to_yaml_file)
begin
config = YAML::load(IO.read(path_to_yaml_file))
rescue Errno::ENOENT
log(:warning, "YAML configuration file couldn't be found. Using defaults."); return
rescue Psych::SyntaxError
log(:warning, "YAML configuration file contains invalid syntax. Using defaults."); return
end

configure(config)
end

def self.config
@config
end
end

An added best practice would be to have defaults for all your configuration keys(as in the example above). That way, you are giving the user ultimate freedom in how they can configure your library.

access configuration in gem

The standard way to configure a gem is to have an initializer in the application where the gem is used.

config/initializers/my_gem.rb:

MyGem.configure do |config|
config.namespace = 'the_namespace'
end

See this answer for an example on how you can implement the configuration API in the gem:
How to use ActiveSupport::Configurable with Rails Engine

How to find where gem files are installed

Use gem environment to find out about your gem environment:

RubyGems Environment:
- RUBYGEMS VERSION: 2.1.5
- RUBY VERSION: 2.0.0 (2013-06-27 patchlevel 247) [x86_64-darwin12.4.0]
- INSTALLATION DIRECTORY: /Users/ttm/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0
- RUBY EXECUTABLE: /Users/ttm/.rbenv/versions/2.0.0-p247/bin/ruby
- EXECUTABLE DIRECTORY: /Users/ttm/.rbenv/versions/2.0.0-p247/bin
- SPEC CACHE DIRECTORY: /Users/ttm/.gem/specs
- RUBYGEMS PLATFORMS:
- ruby
- x86_64-darwin-12
- GEM PATHS:
- /Users/ttm/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0
- /Users/ttm/.gem/ruby/2.0.0
- GEM CONFIGURATION:
- :update_sources => true
- :verbose => true
- :backtrace => false
- :bulk_threshold => 1000
- REMOTE SOURCES:
- https://rubygems.org/
- SHELL PATH:
- /Users/ttm/.rbenv/versions/2.0.0-p247/bin
- /Users/ttm/.rbenv/libexec
- /Users/ttm/.rbenv/plugins/ruby-build/bin
- /Users/ttm/perl5/perlbrew/bin
- /Users/ttm/perl5/perlbrew/perls/perl-5.18.1/bin
- /Users/ttm/.pyenv/shims
- /Users/ttm/.pyenv/bin
- /Users/ttm/.rbenv/shims
- /Users/ttm/.rbenv/bin
- /Users/ttm/bin
- /usr/local/mysql-5.6.12-osx10.7-x86_64/bin
- /Users/ttm/libsmi/bin
- /usr/local/bin
- /usr/bin
- /bin
- /usr/sbin
- /sbin
- /usr/local/bin

Notice the two sections for:

  • INSTALLATION DIRECTORY
  • GEM PATHS

Setting up configuration settings when writing a gem

Try refactoring to:

def self.configuration
@configuration ||= Configuration.new
end

def self.configure
yield(configuration) if block_given?
end

Bundler config to either look for gems in custom path or download from custom source

The syntax for sourcing a gem from a local folder is:

gem 'some-gem-name', path: '/my/custom/path'

And the syntax for specifying a custom source is:

gem 'another-gem-name', source: 'https://a.nice.host'

And to install gems into a specific local folder, you can run:

bundle install --path ./local/relative/path

Now, that's probably all the tools you need, in truth... (And in fact, especially for that last requirement, you may instead wish to look into rvm gemsets, or using bundle install --deployment.)

But you did also ask about "looking in a local folder first, and only falling back to a remote source if it doesn't exist". That's quite an odd requirement (usually you'd only want to explicitly opt-in to fetching gems from a local path?!), but to answer this question as you've asked it...

A Gemfile is literally just ruby code! So you can define this logic using... You guessed it, ruby! For example:

if File.exists?('/my/custom/path')
gem 'some-gem-name', path: '/my/custom/path'
else
gem 'some-gem-name', source: 'https://a.nice.host'
end

If this (unusual) pattern needs to be repeated in multiple places, you could wrap it into some helper method.

For more information on the configuration options of bundler, please see the documentation.

How to read files (.yml) from a Gem

Solution 1

You can store the root path in a constant from your main gem file and retrieve it in other locations of your code. You must ensure that the gem got initialized before the code in your app runs otherwise you'll have an errors because the constant won't be defined.

# lib/my_gem.rb
GEM_ROOT = File.expand_path("../..", __FILE__)

# app/.../some_class.rb
IO.read("#{GEM_ROOT}/config/test.yml")

Solution 2

The most advisable, you can get the gem path programmatically from Bundler, then use that root path to retrieve the full path of your yml file.

Have a look at this answer that you can easily adapt to your case



Related Topics



Leave a reply



Submit