Accessing files packaged into a Ruby Gem - ruby

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 ... }

Related

How to configure Steep to find RBS files for a gem outside the stdlib?

Cannot find type `Sinatra::Base`
ruby file
class StaticApp < Sinatra::Base
end
rbs file
class StaticApp < Sinatra::Base
end
run
bundle exec steep check --log-level=fatal
result
[error] Cannot find type `Sinatra::Base`
Diagnostic ID: RBS::UnknownTypeName
I use steep gem. It seems it is needed to require some files. But
library 'sinatra'
doesn't work.
#<RBS::EnvironmentLoader::UnknownLibraryError: Cannot find type definitions for library: sinatra ([nil])>
What do I wrong?
Thanks.
But library 'sinatra' doesn't work ..
RBS::EnvironmentLoader::UnknownLibraryError
Short answer
# Steepfile
target :app do
repo_path 'vendor/rbs'
Create a directory structure like vendor/rbs/sinatra/0/sinatra.rbs.
How you write or generate the contents of sinatra.rbs is out of the scope of this answer, but check out:
rbs collection
gem_rbs_collection
rbs_rails
Long answer
Steep is using an RBS::EnvironmentLoader.
# steep-0.47.0/lib/steep/project/target.rb:54
loader = RBS::EnvironmentLoader.new(core_root: core_root_path, repository: repo)
options.libraries.each do |lib|
name, version = lib.split(/:/, 2)
loader.add(library: name, version: version)
end
The EnvironmentLoader can find a library in either gem_sig_path (I think this would be a sig folder distributed in the gem), or in a repository.
# rbs-1.7.1/lib/rbs/environment_loader.rb:68
def has_library?(library:, version:)
if self.class.gem_sig_path(library, version) || repository.lookup(library, version)
true
else
false
end
end
In a Steepfile, repositories are configured via repo_path.
# Steepfile
repo_path 'vendor/rbs'
For the directory structure of a repository, I took https://github.com/ruby/rbs/tree/master/stdlib as an example.
ls -R vendor/rbs
activesupport
vendor/rbs/activesupport:
0
vendor/rbs/activesupport/0:
activesupport.rbs

How do I parse the Gemfile to find internal gems not inside a source block?

For the sake of security internal Ruby gems in the Gemfile should always be referenced inside a source block so it never tries to fetch them from rubygems.org. I'd like to automate finding where people fail to do this, so would like to parse the Gemfile, find any gems that match our internal names, and check that rubygems.org isn't in their possible sources list.
source 'https://rubygems.org'
gem 'rails'
gem 'my-private-gem1' # this should be in the source block below
source PRIVATE_GEM_REPO do
gem 'my-private-gem2'
end
I've seen you can parse the Gemfile
Bundler::Definition.build('Gemfile', '', {})
But can't find anything in the returned data structure that shows me the available / allowed sources per gem
If I include the Gemfile.lock I see more source info, but it doesn't seem right because every gem lists all my sources regardless of if they're in a source block
Bundler::Definition.build('Gemfile', 'Gemfile.lock', {}).
locked_gems.
specs.
map {|g| [g.full_name, g.source.remotes.map(&:hostname).join(', ')]}
=> ["rails-6.0.3.4", "my.private.gemserver, rubygems.org"],
["my-private-gem1-1.0.0", "my.private.gemserver, rubygems.org"],
["my-private-gem2-1.0.0", "my.private.gemserver, rubygems.org"]]
Any thoughts on how to parse the Gemfile to find that my-private-gem1 is outside a source block?
Figured it out finally, just took awhile digging through the Bundler methods - and a coworker's help.
Bundler::Definition.
build('Gemfile', '', nil).
dependencies.
map {|dep| [dep.name, dep.source&.remotes&.map(&:hostname)&.join(', ')]}
=>
[["rails", nil],
["my-private-gem1", nil],
["my-private-gem2", "my.private.gemserver"]]
Now I can easily search that resulting data structure for any private gems that aren't locked down to my private gem server.
Preface
While I was writing this answer, the OP found a Bundler-specific answer. However, I offer a more generalizable solution below. This solution also offers user feedback that may make it easier to fix the file.
Finding Candidate Gems by Column Alignment, with Whitelisting
If you can safely assume that your Gemfile is always properly indented, the KISS solution may be to simply identify the gems that aren't indented within a group definition. For example:
# example Gemfile to test against
GEMFILE = <<~'EOF'
source 'https://rubygems.org'
gem 'rails'
gem 'my-private-gem1' # this should be in the source block below
source PRIVATE_GEM_REPO do
gem 'my-private-gem2'
end
EOF
# gems that are acceptable in a non-group context
whitelist = Regexp.new %w[rails sass-rails webpacker].join(?|)
UngroupedGem = Struct.new :line_no, :line_txt, :gem_name
ungrouped_gems = []
GEMFILE.lines.each_with_index do |line_txt, line_no|
next if line_txt =~ whitelist or line_txt !~ /^\s*gem/
gem_name = line_txt.match(/(?<=(['"]))(.*?)(?=\1)/)[0]
ungrouped_gems.append(
UngroupedGem.new line_no.succ, line_txt, gem_name
).compact!
end
# tell the user what actions to take
if ungrouped_gems.any?
puts "Line No.\tGem Name"
ungrouped_gems.each { printf "%d\t\t%s\n", _1.line_no, _1.gem_name }
else
puts "No gems need to be moved."
end
With this example, it will print:
Line No. Gem Name
3 my-private-gem1
5 my-private-gem2
which will give you a solid idea of what lines in the Gemfile need to be moved, and which specific gems are involved.

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.

JRUBY - RAILS 3 - TOMCAT 7 Deploy failed

I have my rails 3 app. This work fine in netbeans with webrick.
I create this application with:
Jruby jruby 1.6.5.1 (ruby-1.8.7-p330)
Rails 3.1.3
My bundle work fine.
Mi gemfile:
gem 'rails', '3.1.3'
gem 'dynamic_form'
gem 'activerecord-jdbcmysql-adapter'
gem 'jruby-openssl'
gem 'json'
group :assets do
gem 'sass', '~> 3.1.12'
gem 'sass-rails', '~> 3.1.5'
gem 'coffee-rails', '~> 3.1.1'
gem 'uglifier', '>= 1.0.3'
end
gem 'jquery-rails'
For create .war file from application, I have installed with bundle all gem required.
I have installed wrable and run
wrable config
and then
wrable
This create the .war file.
I put this file on the webapps tomcat7 directory and start the server.
Server starting fine, catalina.out logs no errors but...
When I try to go at the address when tomcat manager says is my application, I have more time to attendo for view just a withe page. the application name is "cameraAccreditiGiornalisti". So, the address is localhost:8080/cameraAccreditiGiornalisti => white page!
I read all guides in the web but I not say where is the problem!! Not have a log to look and the white page... no error!
Please help!
I have the same problem, I found out that war file does not include web.xml file, which is necessary for Tomcat to start up the webapp. I did not found any info why warble does not generate that file, as it should.
my problem solved... I read a wrong log file.... Maybe for your problem check the file warble.rb in projectdirectory/config. This is my and work:
# Disable Rake-environment-task framework detection by uncommenting/setting to false
# Warbler.framework_detection = false
# Warbler web application assembly configuration file
Warbler::Config.new do |config|
# Features: additional options controlling how the jar is built.
# Currently the following features are supported:
# - gemjar: package the gem repository in a jar file in WEB-INF/lib
# - executable: embed a web server and make the war executable
# - compiled: compile .rb files to .class files
# config.features = %w(gemjar)
# Application directories to be included in the webapp.
config.dirs = %w(app config lib log vendor tmp)
# Additional files/directories to include, above those in config.dirs
# config.includes = FileList["db"]
# Additional files/directories to exclude
# config.excludes = FileList["lib/tasks/*"]
# Additional Java .jar files to include. Note that if .jar files are placed
# in lib (and not otherwise excluded) then they need not be mentioned here.
# JRuby and JRuby-Rack are pre-loaded in this list. Be sure to include your
# own versions if you directly set the value
# config.java_libs += FileList["lib/java/*.jar"]
# Loose Java classes and miscellaneous files to be included.
# config.java_classes = FileList["target/classes/**.*"]
#config.java_classes = FileList["log4j.properties"]
# One or more pathmaps defining how the java classes should be copied into
# the archive. The example pathmap below accompanies the java_classes
# configuration above. See http://rake.rubyforge.org/classes/String.html#M000017
# for details of how to specify a pathmap.
# config.pathmaps.java_classes << "%{target/classes/,}p"
# Bundler support is built-in. If Warbler finds a Gemfile in the
# project directory, it will be used to collect the gems to bundle
# in your application. If you wish to explicitly disable this
# functionality, uncomment here.
# config.bundler = false
# An array of Bundler groups to avoid including in the war file.
# Defaults to ["development", "test"].
# config.bundle_without = []
# Other gems to be included. If you don't use Bundler or a gemspec
# file, you need to tell Warbler which gems your application needs
# so that they can be packaged in the archive.
# For Rails applications, the Rails gems are included by default
# unless the vendor/rails directory is present.
# config.gems += ["activerecord-jdbcmysql-adapter", "jruby-openssl"]
# config.gems << "tzinfo"
config.gems += ["activerecord-jdbcmysql-adapter", "jruby-openssl"]
# Uncomment this if you don't want to package rails gem.
# config.gems -= ["rails"]
# The most recent versions of gems are used.
# You can specify versions of gems by using a hash assignment:
#
config.gems["rails"] = "3.2.1"
# You can also use regexps or Gem::Dependency objects for flexibility or
# finer-grained control.
# config.gems << /^merb-/
# config.gems << Gem::Dependency.new("merb-core", "= 0.9.3")
# Include gem dependencies not mentioned specifically. Default is
# true, uncomment to turn off.
# config.gem_dependencies = false
# Array of regular expressions matching relative paths in gems to be
# excluded from the war. Defaults to empty, but you can set it like
# below, which excludes test files.
# config.gem_excludes = [/^(test|spec)\//]
# Pathmaps for controlling how application files are copied into the archive
# config.pathmaps.application = ["WEB-INF/%p"]
# Name of the archive (without the extension). Defaults to the basename
# of the project directory.
# config.jar_name = "mywar"
# Name of the MANIFEST.MF template for the war file. Defaults to a simple
# MANIFEST.MF that contains the version of Warbler used to create the war file.
# config.manifest_file = "config/MANIFEST.MF"
# When using the 'compiled' feature and specified, only these Ruby
# files will be compiled. Default is to compile all \.rb files in
# the application.
# config.compiled_ruby_files = FileList['app/**/*.rb']
# === War files only below here ===
# Path to the pre-bundled gem directory inside the war file. Default
# is 'WEB-INF/gems'. Specify path if gems are already bundled
# before running Warbler. This also sets 'gem.path' inside web.xml.
# config.gem_path = "WEB-INF/vendor/bundler_gems"
# Files for WEB-INF directory (next to web.xml). This contains
# web.xml by default. If there is an .erb-File it will be processed
# with webxml-config. You may want to exclude this file via
# config.excludes.
# config.webinf_files += FileList["jboss-web.xml"]
# Files to be included in the root of the webapp. Note that files in public
# will have the leading 'public/' part of the path stripped during staging.
# config.public_html = FileList["public/**/*", "doc/**/*"]
# Pathmaps for controlling how public HTML files are copied into the .war
# config.pathmaps.public_html = ["%{public/,}p"]
# Value of RAILS_ENV for the webapp -- default as shown below
config.webxml.rails.env = ENV['RAILS_ENV'] || 'production'
# Application booter to use, one of :rack, :rails, or :merb (autodetected by default)
# config.webxml.booter = :rails
# Set JRuby to run in 1.9 mode.
# config.webxml.jruby.compat.version = "1.9"
# When using the :rack booter, "Rackup" script to use.
# - For 'rackup.path', the value points to the location of the rackup
# script in the web archive file. You need to make sure this file
# gets included in the war, possibly by adding it to config.includes
# or config.webinf_files above.
# - For 'rackup', the rackup script you provide as an inline string
# is simply embedded in web.xml.
# The script is evaluated in a Rack::Builder to load the application.
# Examples:
# config.webxml.rackup.path = 'WEB-INF/hello.ru'
# config.webxml.rackup = %{require './lib/demo'; run Rack::Adapter::Camping.new(Demo)}
# config.webxml.rackup = require 'cgi' && CGI::escapeHTML(File.read("config.ru"))
# Control the pool of Rails runtimes. Leaving unspecified means
# the pool will grow as needed to service requests. It is recommended
# that you fix these values when running a production server!
# config.webxml.jruby.min.runtimes = 2
# config.webxml.jruby.max.runtimes = 4
config.webxml.jruby.min.runtimes = 2
config.webxml.jruby.max.runtimes = 4
# JNDI data source name
# config.webxml.jndi = 'jdbc/rails'
end

How do you generate executables within a gem with Rake?

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.

Resources