How do you generate executables within a gem with Rake? - ruby

I've been learning Ruby recently, and I've not gotten into the dirty recesses of learning Rake yet. I've been playing around with NetBeans, and I made a little Ruby project with a file that simply prints "Hello, World!". I was looking at the Rakefile that NetBeans generates, and I noticed that it had commented out the s.executables line, so I uncommented, and tried to build it. Of course it failed with:
Don't know how to build task 'bin/your_executable_here'
What I'm trying to do, is figure out how to make that work. I've googled around, and I can't find any information on how to correctly generate an executable. Here is the Rakefile generated by NetBeans:
require 'rubygems'
require 'rake'
require 'rake/clean'
require 'rake/gempackagetask'
require 'rake/rdoctask'
require 'rake/testtask'
spec = Gem::Specification.new do |s|
s.name = 'Learning'
s.version = '0.0.1'
s.has_rdoc = true
s.extra_rdoc_files = ['README', 'LICENSE']
s.summary = 'Your summary here'
s.description = s.summary
s.author = ''
s.email = ''
s.executables = ['your_executable_here']
s.files = %w(LICENSE README Rakefile) + Dir.glob("{bin,lib,spec}/**/*")
s.require_path = "lib"
s.bindir = "bin"
end
Rake::GemPackageTask.new(spec) do |p|
p.gem_spec = spec
p.need_tar = true
p.need_zip = true
end
Rake::RDocTask.new do |rdoc|
files =['README', 'LICENSE', 'lib/**/*.rb']
rdoc.rdoc_files.add(files)
rdoc.main = "README" # page to start on
rdoc.title = "Learning Docs"
rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder
rdoc.options
I'm sorry if this is a stupid question, I honestly did try to find the information myself.
EDIT: I was unaware that there had to be an executable file by the same name as the default one you specify in ./bin in your project. I figured it all out.

The s.executables array must contain the names of the executables in the bin directory of your gem
s.executables = %w( my_awesome_commandline_churner )

This code is used to make a gem file. Gems are ruby's package management devices. Some gems come with executable script files to be run from the command line. They are placed in the ./bin directory when the gem is built, and hen it is deployed they will be copied into the same folder as the ruby executable.
To make a file executable you will need to add a shabang (#!/user/local/bin/ruby) to the first line and change the file permission to allow execution.

Related

How to fix "Gem::InvalidSpecificationException" error after breaking a gem?

Gem::Specification.new do |spec|
spec.name = "nba_schedule"
spec.version = NBA_Schedule::VERSION
spec.authors = ["TylerP33"]
spec.email = ["tyler3321#gmail.com"]
spec.summary = %q{TODO: Write a short summary, because RubyGems
requires one.}
spec.description = %q{TODO: Write a longer description or delete this
line.}
spec.homepage = "TODO: Put your gem's website or public repo URL
here."
spec.license = "MIT"
So in the above .gemspec folder, I had changed my spec.version from NbaSchedule::VERSION to NBA_Schedule::Version, which is what you see below (it was just too ugly).
Because NbaSchedule was used at the time of gem creation (used a command to stub out all the necessary gem files I would need to start one from scratch), it is giving me a lot of grief for changing it and I can't figure out why. I have covered all the dependencies by changing them as needed.
Am I missing something here?
/home/TylerP/.rbenv/versions/2.4.2/lib/ruby/gems/2.4.0/gems/bundler-
1.16.0/lib/bundler/rubygems_integration.rb:65:in `rescue in validate': The
gemspec at /mnt/c/Users/tyler/Desktop/nba_schedule/nba_schedule.gemspec is
not valid. Please fix this gemspec. (Gem::InvalidSpecificationException)
The validation error was '"FIXME" or "TODO" is not a description'
My error is above.
I am fairly new to coding, period. So I understand I may be missing something glaring but I would love to know why it is breaking if everything is cohesive and changed over.

Warbler: No Support for Path Gems

Here My Problem: Warbler doesn't support gems which are included via path in the gemfile. nevertheless i need to do that for my work. it is important that the included gem is packed and handled as a simple rubygem in the war archive. until now i have tried to manipulate the bundler so when the spec arrives in the warbler/traits/bundler.rb (that's where the specs are packed to the archive) it already has 'Bundler::Source::Rubygems' as source. the problem is that it sill needs to be build and installed from the path, but i can't handle it to pass the path anywhere in the spec or in the source. it already works that the gem is built, installed and packed into the archive as a rubygem and listed under GEM in the Lockfile but only with bad coding (it is all with reference to a specific gem and the path is typed in clearly)
Here my code:
warbler/lib/warbler/traits/bunlder.rb line: 60
case spec.source
when ::Bundler::Source::Git
config.bundler[:git_specs] ||= []
config.bundler[:git_specs] << spec
when ::Bundler::Source::Path
$stderr.puts("warning: Bundler `path' components are not currently supported.",
"The `#{spec.full_name}' component was not bundled.",
"Your application may fail to boot!")
else
##################################################################### MINE
if spec.name == "charla_common"
path = ::Bundler::GemHelper.new("../../common/trunk", spec.name).install_gem
end
##################################################################### MINE END
config.gems << spec
end
This is where the gem is installed path
Bundler/lib/bundler/dsl.rb line: 120
def source(source, options = {})
############################################################### MINE
if source.class == Bundler::Source::Path
options[:path] = source.options["path"]
source = "https://rubygems.org"
end
############################################################### MINE END
case source
when :gemcutter, :rubygems, :rubyforge then
Bundler.ui.warn "The source :#{source} is deprecated because HTTP " \
"requests are insecure.\nPlease change your source to 'https://" \
"rubygems.org' if possible, or 'http://rubygems.org' if not."
#rubygems_source.add_remote "http://rubygems.org"
return
when String
# ensures that the source in the lockfile is shown only once
unless options[:prepend]
#rubygems_source.add_remiote source
end
return
else
#source = source
if options[:prepend]
#sources = [#source] | #sources
else
#sources = #sources | [#source]
end
yield if block_given?
return #source
end
ensure
#source = nil
end
I'm not sure if this is an option for you, but in order to work around this problem, I create symbolic links in vendor/cache to the path with the included gem.
E.g. vendor/cache/gem_name -> ../../../gem_name.

Environment variables locally and Heroku

I have a sinatra app in which i have a yml file to set environment variables, i call them using this method
module MyConfig
def config
environment = ENV["RACK_ENV"] || "development"
YAML.load_file("./config/config.yml")[environment]
end
end
so when i want to use a variable i do this for example
aws_access_key_id = config['aws_access_key']
I have a .gitignore file that ignores config.yml when pushing to github for example.So when I push to heroku these environment variables will not be accessible?
So this leaves me with using the heroku way of setting them like so
heroku config:add aws_access_key= myapikey
but heroku accesses these like
aws_access_key_id = ENV['aws_access_key']
How can i set my dev environment to use method config and heroku use ENV, am i looking at this the wrong way? or does my config method do this for me?
Any help appreciated
RAKEFILE
require 'active_support/core_ext'
require './config/config.rb'
require 'bundler/setup'
Bundler.require(:default)
include MyConfig
AssetSync.configure do |con|
con.fog_provider = 'AWS'
con.fog_region = 'eu-west-1'
con.fog_directory = config['fog_directory']
con.aws_access_key_id = config['aws_access_key']
con.aws_secret_access_key = config['aws_secret_key']
con.prefix = "assets"
con.public_path = Pathname("./public")
end
namespace :assets do
desc "Precompile assets"
task :precompile do
AssetSync.sync
end
end
Update:
I now use the dotenv gem instead of the example below. So instead of ignoring the env.rb file, I now ignore the .env file with Git.
Original post:
Try this,
# /env.rb
ENV['aws_bucket'] = 'my_bucket'
ENV['aws_access_key'] = 'my_access_key'
ENV['aws_access_secret'] = 'my_access_secret'
This file sets the same ENV values as heroku config would do.
# /config.rb
require './env' if File.exists?('env.rb')
The env.rb will only get required if it exists.
# /.gitignore
/env.rb
The env.rb has been added to the .gitignore file so it isn't kept in Git.
You would then access the values using ENV['key'] instead of config['key'].
You might need to change the path to the env.rb if it's not in the same directory as the config.rb file.
EDIT:
From looking at your Rakefile in the previous question, you need to change it to this:
# Rakefile
require 'bundler/setup'
Bundler.require(:default)
require './env' if File.exists?('env.rb')
AssetSync.configure do |con|
con.fog_provider = 'AWS'
con.fog_region = 'eu-west-1'
con.fog_directory = ENV['aws_bucket']
con.aws_access_key_id = ENV['aws_access_key']
con.aws_secret_access_key = ENV['aws_access_secret']
con.prefix = "assets"
con.public_path = Pathname("./public")
end
namespace :assets do
desc "Precompile assets"
task :precompile do
AssetSync.sync
end
end
I've assumed that the only method in /config/config.rb was the config method so I've removed the,
require './config/config.rb'
include MyConfig
And swapped the config[key] for the ENV[key] values defined in env.rb. You may need to change the key names to match up.
You could delete the yaml, and describe the environment variables in a .env file then start your app with foreman start. See https://devcenter.heroku.com/articles/config-vars#local-setup
Or keep your hybrid system, where you load a yaml in dev, and use environment variables on heroku.
I do something similar to Sam's suggestion, but a little bit different. I have a YAML config file too, but I wrap the reading of it in a Rake task, which then runs the app.
# in the Rakefile
require 'yaml'
def set_connstring
s = %Q!postgres://#{ENV["DB_APP"]}#localhost/#{ENV["DB_APP"]}!
ENV['DATABASE_URL'] ||= ENV["RACK_ENV"] == "test" ? "#{s}.test" : s
end
def basic_environment
warn " Setting up environment..."
file = File.expand_path( File.join File.dirname(__FILE__), "./config.yml" )
if File.exist? file
YAML.load_file(file).each do |k,v|
warn "-> #{k}"
ENV[k.upcase] = v
end
end
set_connstring()
end
namespace :app do
desc "Set up the environment locally"
task :environment do
basic_environment()
end
desc "Run the app locally"
task :run_local => "app:environment" do
exec "bin/rackup config.ru -p #{ENV['RUN_LOCAL_PORT']}"
end
end
It means I can run it locally without any code inside the app to deal with this.
Edit: a quick aside, I notice you have Bundler.require(:default) in your Rakefile. If you use bundle install --binstubs then Bundler installs all executables into a dir named "bin/" within the project. Then, if you run any of those executables they automatically use the libraries installed by Bundler, no need to require via Bundler. See http://gembundler.com/v1.2/man/bundle-exec.1.html.
Exporting directly from heroku admin:
Settings -> Reveal Config Vars
Then open browser js console, paste this and type enter...
k=[];
$(".config-var-list input").map(function(y, x){k.push($(x).val())});
v=[];
$(".config-var-list textarea").map(function(y, x){v.push($(x).val())});
ret="";
k.map(function(x, i){ret+=k[i]+"\t"+v[2*i]+"\n"});
console.info(ret);

Accessing files packaged into a Ruby Gem

I have a Buildr extension that I'm packaging as a gem. I have a collection of scripts that I want to add to a package. Currently, I have these scripts stored as a big text block that I'm writing to file. I would prefer to have individual files that I can either copy directly or read/write back out. I would like these files to be packaged into the gem. I don't have a problem packaging them in (just stick them in the file system before rake install) but I can't figure out how to access them. Is there a Gem Resources bundle type thing?
There are basically two ways,
1) You can load resources relative to a Ruby file in your gem using __FILE__:
def path_to_resources
File.join(File.dirname(File.expand_path(__FILE__)), '../path/to/resources')
end
2) You can add arbitrary paths from your Gem to the $LOAD_PATH variable and then walk the $LOAD_PATH to find resources, e.g.,
Gem::Specification.new do |spec|
spec.name = 'the-name-of-your-gem'
spec.version ='0.0.1'
# this is important - it specifies which files to include in the gem.
spec.files = Dir.glob("lib/**/*") + %w{History.txt Manifest.txt} +
Dir.glob("path/to/resources/**/*")
# If you have resources in other directories than 'lib'
spec.require_paths << 'path/to/resources'
# optional, but useful to your users
spec.summary = "A more longwinded description of your gem"
spec.author = 'Your Name'
spec.email = 'you#yourdomain.com'
spec.homepage = 'http://www.yourpage.com'
# you did document with RDoc, right?
spec.has_rdoc = true
# if you have any dependencies on other gems, list them thusly
spec.add_dependency('hpricot')
spec.add_dependency('log4r', '>= 1.0.5')
end
and then,
$LOAD_PATH.each { |dir| ... look for resources relative to dir ... }

Ruby gem testing before deployment

I'm creating a gem which has
several scripts in the bin directory
the utility classes in the lib directory
and several tests in the test directory
supertool
bin
toolA
toolB
lib
supertool
supertool.rb
helper.rb
test
tc_main.rb
tc_etc.rb
Now, to run the tests before I even install the gem, I have the following snippet at the top of my tests:
base = File.basename(Dir.pwd)
if base == 'test' || base =~ /supertool/
Dir.chdir('..') if base == 'test'
$LOAD_PATH.unshift(Dir.pwd + '/lib')
Dir.chdir('test') if base =~ /supertool/
end
This seems tedious though, especially if I have to put these in the scripts in the bin directory too. Is there a better way of setting up the environment so we can test gems before they are installed? I'm sure it's something simple that I just can't find. A simple link to the right place would help a lot :)
I'm not sure what you're trying to achieve with that script. It doesn't seem to have anything to do with gems...
Is it so that you can run ruby tc_main.rb from within the test directory (or ruby test/tc_main.rb from the base dir), and have it set the load path appropriately? If so, here's a much nicer way:
In your test directory, create a test_helper.rb file. In that file, put this
$LOAD_PATH << File.expand_path( File.dirname(__FILE__) + '/../lib' )
And in all your test files, set the first line to
require 'test_helper'
If you have subdirectories inside your test dir, then files in those subdirs can just do
require '../test_helper'
Take a look at hoe gem, it is a helper for other gems.

Resources