Add Ruby Source Code as HTML to RDoc - ruby

How do I include a ruby code file, as is, into RDoc?
I have an example.rb file that documents how to use my gem and I would like to include that as one of the files like the README.rdoc and HISTORY.rdoc.
I've already figured out how to convert the ruby source code into HTML using the Syntax gem but I can't figure out how to make RDoc include the file without parsing it.
When I tell RDoc to include the html file it isn't listed and if I fake it out by using rdoc or txt as the file extension it doesn't display properly (the file is still actually html).
I've got a solution that works it's just incredibly ugly. There has got to be a better way to do this that's native to rdoc but I don't see it.
Here's what I have in my Rakefile:
# Build rdocs
require 'rake/rdoctask'
require 'syntax/convertors/html'
rdoc_dir = 'rdoc'
# This is rdoc1 but it doesn't work unless you DON'T wrap it in a task
# Generate html files from example ruby files
convertor = Syntax::Convertors::HTML.for_syntax "ruby"
replacement_key = "REPLACE_THIS_TEXT_WITH_PROPER_HTML"
# Create dummy files
Dir.glob('examples/*.rb').each do |file|
File.open("#{file}.txt", "w") do |dummy_file|
dummy_file.write(replacement_key)
end
end
# Call the rdoc task
Rake::RDocTask.new(:rdoc2) do |rdoc|
rdoc.rdoc_dir = rdoc_dir
rdoc.title = "pickled_optparse #{version}"
rdoc.rdoc_files.include('README*')
rdoc.rdoc_files.include('HISTORY*')
rdoc.rdoc_files.include('examples/*.txt')
rdoc.rdoc_files.include('lib/**/*.rb')
end
task :rdoc3 do
# Now use a hammer to replace the dummy text with the
# html we want to use in our ruby example code file.
html_header = File.read('rake_reqs/html_header.html')
Dir.glob('examples/*.rb').each do |file|
html_ruby = convertor.convert(File.read(file))
rdoc_file = "#{rdoc_dir}/examples/#{File.basename(file,".rb")}_rb_txt.html"
fixed_html = File.read(rdoc_file).gsub!(replacement_key, "#{html_header}#{html_ruby}")
File.open(rdoc_file, "w") {|f| f.write(fixed_html)}
File.delete("#{file}.txt")
end
end
task :rdoc => [:rdoc2, :rdoc3]

Sorry I can't give you an actual answer but I'm looking at sdoc myself.
You can install the gem from ruby gems.

Related

Ruby: Download zip file and extract

I have a ruby script that downloads a remote ZIP file from a server using rubys opencommand. When I look into the downloaded content, it shows something like this:
PK\x03\x04\x14\x00\b\x00\b\x00\x9B\x84PG\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x10\x00foobar.txtUX\f\x00\x86\v!V\x85\v!V\xF6\x01\x14\x00K\xCB\xCFOJ,RH\x03S\\\x00PK\a\b\xC1\xC0\x1F\xE8\f\x00\x00\x00\x0E\x00\x00\x00PK\x01\x02\x15\x03\x14\x00\b\x00\b\x00\x9B\x84PG\xC1\xC0\x1F\xE8\f\x00\x00\x00\x0E\x00\x00\x00\n\x00\f\x00\x00\x00\x00\x00\x00\x00\x00#\xA4\x81\x00\x00\x00\x00foobar.txtUX\b\x00\x86\v!V\x85\v!VPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00D\x00\x00\x00T\x00\x00\x00\x00\x00
I tried using the Rubyzip gem (https://github.com/rubyzip/rubyzip) along with its class Zip::ZipInputStream like this:
stream = open("http://localhost:3000/foobar.zip").read # this outputs the zip content from above
zip = Zip::ZipInputStream.new stream
Unfortunately, this throws an error:
Failure/Error: zip = Zip::ZipInputStream.new stream
ArgumentError:
string contains null byte
My questions are:
Is it possible, in general, to download a ZIP file and extract its content in-memory?
Is Rubyzip the right library for it?
If so, how can I extract the content?
I found the solution myself and then at stackoverflow :D (How to iterate through an in-memory zip file in Ruby)
input = HTTParty.get("http://example.com/somedata.zip").body
Zip::InputStream.open(StringIO.new(input)) do |io|
while entry = io.get_next_entry
puts entry.name
parse_zip_content io.read
end
end
Download your ZIP file, I'm using HTTParty for this (but you could also use ruby's open command (require 'open-uri').
Convert it into a StringIO stream using StringIO.new(input)
Iterate over every entry inside the ZIP archive using io.get_next_entry (it returns an instance of Entry)
With io.read you get the content, and with entry.name you get the filename.
Like I commented in https://stackoverflow.com/a/43303222/4196440, we can just use Zip::File.open_buffer:
require 'open-uri'
content = open('http://localhost:3000/foobar.zip')
Zip::File.open_buffer(content) do |zip|
zip.each do |entry|
puts entry.name
# Do whatever you want with the content files.
end
end

Why is force_encoding("BINARY") used here?

When we install Rails, we get this rails "executable":
#!/usr/bin/env ruby
#
# This file was generated by RubyGems.
#
# The application 'railties' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'rubygems'
version = ">= 0"
if ARGV.first
str = ARGV.first
str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding
if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then
version = $1
ARGV.shift
end
end
gem 'railties', version
load Gem.bin_path('railties', 'rails', version)
I'm wondering what the point of doing force_encoding("BINARY") is on that String. What possible values could it be that force_encoding is necessary? I would think that people would only specify versions using numbers and letters here.
This isn't a rails specific thing - it's a wrapper rubygems will generate for any ruby executable in a gem. The call to force_encoding was added in 6bf71914
The reason for the change is that the first argument might not be a version at all - we want to test if it is a version, but it could be anything and we don't want the regex check to blow up. For example some executables accept a list of file names as arguments, and those file names could be invalid in the default external encoding used by ruby.
There is a bit more discussion on the issue which prompted this change.

In Ruby, how can I extract a tar archive stream directly to the filesystem?

I am using Grit (Ruby gem) to load a git repo, extract a given commit to a folder, then 'do stuff' with it. Grit supports git archive with archive_tar which retuns a 'string containing tar archive'. I would like to extract this to the filesystem using Ruby libs / gems if possible (not direct system calls) and avoid things like saving the data to an archive first and then extracting it (really looking for an efficient one-liner here).
Under the assumption that 'string containing tar archive' is equivalent to
File.open("Downloads.tar", "rb") {|a| a.read }
I am able to accomplish this using fileutils, stringio, and the minitar gem.
First you will need to install minitar, which should be as simple as
gem install minitar
Since minitar does not support it's regular unpack to use a stream, we'll create our own rudimentary unpacking method.
require 'archive/tar/minitar'
require 'stringio'
require 'fileutils'
def unpack_tar(directory, string)
FileUtils.mkdir_p(directory) if !File.exist?(directory)
stringio = StringIO.new(string)
input = Archive::Tar::Minitar::Input.new(stringio)
input.each {|entry|
input.extract_entry(directory, entry)
}
end
unpack_tar("./test", File.open("Downloads.tar", "rb") {|a| a.read })
Of course replacing the whole File.open part with the archive_tar function of the Grit gem. This is all assuming that first assumption of course, though i'm sure ths method can easily be adapted to suit whatever archive_tar actually returns.

Is it possible to recursively require all files in a directory in Ruby?

I am working on an API that needs to load all of the .rb files in its current directory and all subdirectories. Currently, I am entering a new require statement for each file that I add but I would like to make it where I only have to place the file in one of the subdirectories and have it automatically added.
Is there a standard command to do this?
In this case its loading all the files under the lib directory:
Dir["#{File.dirname(__FILE__)}/lib/**/*.rb"].each { |f| load(f) }
require "find"
Find.find(folder) do |file|
next if File.extname(file) != ".rb"
puts "loading #{file}"
load(file)
end
This will recursively load each .rb file.
like Miguel Fonseca said, but in ruby >= 2 you can do :
Dir[File.expand_path "lib/**/*.rb"].each{|f| require_relative(f)}
I use the gem require_all all the time, and it gets the job done with the following pattern in your requires:
require 'require_all'
require_all './lib/exceptions/'
def rLoad(dir)
Dir.entries(dir).each {|f|
next if f=='.' or f=='..'
if File.directory?(f)
rInclude(f)
else
load(f) if File.fnmatch('*.rb', f)
end
}
end
This should recursively load all .rb files in the directory specified by dir. For example, rLoad Dir.pwd would work on the current working directory.
Be careful doing this, though. This does a depth-first search and if there are any conflicting definitions in your Ruby scripts, they may be resolved in some non-obvious manner (alphabetical by folder/file name I believe).
You should have a look at this gem. It is quite small so you can actually re-use the code instead of installing the whole gem.

Ruby path management

What is the best way to manage the require paths in a ruby program?
Let me give a basic example, consider a structure like:
\MyProgram
\MyProgram\src\myclass.rb
\MyProgram\test\mytest.rb
If in my test i use require '../src/myclass' then I can only call the test from \MyProgram\test folder, but I want to be able to call it from any path!
The solution I came up with is to define in all source files the following line:
ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(ROOT) and then always use require "#{ROOT}/src/myclass"
Is there a better way to do it?
As of Ruby 1.9 you can use require_relative to do this:
require_relative '../src/myclass'
If you need this for earlier versions you can get it from the extensions gem as per this SO comment.
Here is a slightly modified way to do it:
$LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), "..", "src"))
By prepending the path to your source to $LOAD_PATH (aka $:) you don't have to supply the root etc. explicitly when you require your code i.e. require 'myclass'
The same, less noisy IMHO:
$:.unshift File.expand_path("../../src", __FILE__)
require 'myclass'
or just
require File.expand_path "../../src/myclass", __FILE__
Tested with ruby 1.8.7 and 1.9.0 on (Debian) Linux - please tell me if it works on Windows, too.
Why a simpler method (eg. 'use', 'require_relative', or sg like this) isn't built into the standard lib? UPDATE: require_relative is there since 1.9.x
Pathname(__FILE__).dirname.realpath
provides a the absolute path in a dynamic way.
Use following code to require all "rb" files in specific folder (=> Ruby 1.9):
path='../specific_folder/' # relative path from current file to required folder
Dir[File.dirname(__FILE__) + '/'+path+'*.rb'].each do |file|
require_relative path+File.basename(file) # require all files with .rb extension in this folder
end
sris's answer is the standard approach.
Another way would be to package your code as a gem. Then rubygems will take care of making sure your library files are in your path.
This is what I ended up with - a Ruby version of a setenv shell script:
# Read application config
$hConf, $fConf = {}, File.expand_path("../config.rb", __FILE__)
$hConf = File.open($fConf) {|f| eval(f.read)} if File.exist? $fConf
# Application classpath
$: << ($hConf[:appRoot] || File.expand_path("../bin/app", __FILE__))
# Ruby libs
$lib = ($hConf[:rubyLib] || File.expand_path("../bin/lib", __FILE__))
($: << [$lib]).flatten! # lib is string or array, standardize
Then I just need to make sure that this script is called once before anything else, and don't need to touch the individual source files.
I put some options inside a config file, like the location of external (non-gem) libraries:
# Site- and server specific config - location of DB, tmp files etc.
{
:webRoot => "/srv/www/myapp/data",
:rubyLib => "/somewhere/lib",
:tmpDir => "/tmp/myapp"
}
This has been working well for me, and I can reuse the setenv script in multiple projects just by changing the parameters in the config file. A much better alternative than shell scripts, IMO.

Resources