How to use a RubyGems class in a Rails app - ruby-on-rails

I need to stream a TAR file using data from the database in a Rails app.
I know that RubyGems has the TarWriter module that suits my use case perfectly.
The question is, how can I include the module in my Rails app?
I tried to require rubygems/package as follows:
require 'zlib'
require 'rubygems/package'
tar = StringIO.new
Gem::Package::TarWriter.new(tar) do |writer|
writer.add_file("a_file.txt", 0644) do |f|
(1..1000).each do |i|
f.write("some text\n")
end
end
writer.add_file("another_file.txt", 0644) do |f|
f.write("some more text\n")
end
end
tar.seek(0)
gz = Zlib::GzipWriter.new(File.new('this_is_a_tar_gz.tar.gz', 'wb'))
gz.write(tar.read)
tar.close
gz.close
The require failed on the controller. I tried to adding gem 'rubygems/package' on my Gemfile but it didn't work.
It's a noob question, how can I use RubyGems modules from a Rails app?

You should specify full path to module: Gem::Package::TarWriter and add require 'rubygems/package' to your controller.

Related

Ruby Gem Project -- Thor Generator Causing Read-only File System Error

As a personal project, I decided to write a minified-version of Ruby on Rails and upload it as a gem using Bundle called railz_lite.
Inside of my project, I was hoping to implement a Generator similar to rails new, which would create the necessary folders for a web app i.e. controllers/, views/, models/, etc.
To do so, I included Thor as a dependency, then created the following files:
require 'thor/group'
module RailzLite
module Generators
class Project < Thor::Group
include Thor::Actions
def self.source_root
File.dirname(__FILE__) + "/templates"
end
def add_controllers
empty_directory("controllers")
end
def add_models
empty_directory("models")
end
def add_server
template("server.rb", "config/server.rb")
end
def add_views
empty_directory("views")
end
def add_public
empty_directory("public")
end
end
end
end
Inside the gem project's root folder, when I run bundle exec railz_lite new, the generator works just fine and the necessary files are created.
However, if I create a new project, puts my gem (railz_lite) in the Gemfile, run bundle install, then execute bundle exec rails_lite new, I am greeted with the following error:
.rbenv/versions/2.5.1/lib/ruby/2.5.0/fileutils.rb:232:in `mkdir':
: Read-only file system # dir_s_mkdir - /controllers (Errno::EROFS)
I suspect the error is because the empty_directory command is not referring to the root directory of the project I just created. I am hoping that there is a simple way to fix this.
For further reference, the CLI script and class look as follows:
railz_lite
#!/usr/bin/env ruby
require 'railz_lite/cli'
RailzLite::CLI.start
cli.rb
require 'thor'
require 'railz_lite'
require 'railz_lite/generators/project'
module RailzLite
class CLI < Thor
desc 'new', 'Generates a new RailzLite project'
def new
RailzLite::Generators::Project.start([])
end
end
end
Any solutions would be greatly appreciated!
Note: I am running this on macOS Catalina.
So I found a solution after searching extensively through gem forums and looking at the Rails source code.
Inside of the generation I have to manually set the destination_root to the working directory of the project. The working directory can be found with Dir.pwd
require 'thor/group'
module RailzLite
module Generators
class Project < Thor::Group
include Thor::Actions
def self.source_root
File.dirname(__FILE__) + "/templates"
end
def self.destination_root # this method fixes the problem!
Dir.pwd # get the current project directory
end
def add_controllers
empty_directory("controllers")
end
def add_models
empty_directory("models")
end
def add_server
template("server.rb", "config/server.rb")
end
def add_views
empty_directory("views")
end
def add_public
empty_directory("public")
end
end
end
end

How to create an inline / minimal Rails app?

It's possible, for example, to use ActiveRecord inline in a Ruby script. I like this a lot to report bugs, test features and share gists.
I'm wondering if the same could be done for a Rails webservice? (I'm mainly interested in getting the controller layer to work, the rest should be easy to add on demand.) Something along these lines:
begin
require "bundler/inline"
rescue LoadError => e
$stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
raise e
end
gemfile(true) do
source "https://rubygems.org"
gem 'rails', '~> 6.0.0'
end
require 'rails/commands'
APP_PATH = File.expand_path('config/application', __dir__)
Rails::Command.invoke('server')
When toying around with this it did seem that an external entry point (APP_PATH) is required. So alternatively, an acceptable approach would be to cram all config into the single entry point. I couldn't get this to work so far.
The Rails Initialization Process is the best resource I found about this so far.
I produced a minimal Rails app to get started as follows:
rails new min-rails --skip-keeps --skip-action-mailer --skip-action-mailbox --skip-action-text --skip-active-record --skip-active-storage --skip-puma --skip-action-cable --skip-sprockets --skip-spring --skip-listen --skip-javascript --skip-turbolinks --skip-test --skip-system-test --skip-bootsnap --api
cd min-rails
rm -rf app/jobs app/models config/initializers config/locales lib log public tmp vendor config/environments/test.rb config/environments/production.rb config/credentials.yml.enc config/master.key bin/rake bin/setup bin/bundle
I ended up with the following script:
inline-rails.rb
begin
require "bundler/inline"
rescue LoadError => e
$stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
raise e
end
gemfile(true) do
source "https://rubygems.org"
gem 'rails', '~> 6.0.0'
end
require "action_controller/railtie"
class App < Rails::Application
routes.append do
get "/hello/world" => "hello#world"
end
config.consider_all_requests_local = true # display errors
end
class HelloController < ActionController::API
def world
render json: {hello: :world}
end
end
App.initialize!
Rack::Server.new(app: App, Port: 3000).start
Run it as:
ruby inline-rails.rb

Ruby on Rails docsplit file path

I have a pdftotext.rb file in /lib and the code is
module Pdftotext
require 'rubygems'
require 'docsplit'
class << self
def convert
Docsplit.extract_text("hello.pdf")
end
end
end
I have the hello.pdf file in the /assets folder and I tried "assets/hello.pdf" but it keeps telling me Error: Couldn't open file '/assets/hello.pdf': No such file or directory.
How can I get the right path to get the file to be converted?
By the way I am using rails 3.2.1, thanks.
Do you mean it is in RAILS_ROOT/assets/hello.pdf?
You should use File.join to get at the file. Like this:
module Pdftotext
require 'rubygems'
require 'docsplit'
class << self
def convert
Docsplit.extract_text(File.join(Rails.root, "assets", "hello.pdf"))
end
end
end
Using "/assets/hello.pdf" will try to get it from the file system root.

Rack Error -- LoadError: cannot load such file

Trying to go through the tekpub rack tutorial but run into this error.
Boot Error
Something went wrong while loading app.ru
LoadError: cannot load such file -- haiku
There is a file named haiku.rb in the same directory as the app I am trying to run but I get the above error while trying to run the program. Here is the code:
class EnvironmentOutput
def initialize(app=nil)
#app = app
end
def call(env)
out = ""
unless(#app.nil?)
response = #app.call(env)[2]
out+=response
end
env.keys.each {|key| out+="<li>#{key}=#{env[key]}</li>"}
["200",{"Content-Type" => "text/html"},[out]]
end
end
require 'haml'
require 'haiku'
class MyApp
def call(env)
poem = Haiku.new.random
template = File.open("views/index.haml").read
engine = Haml::Engine.new(template)
out = engine.render(Object.new, :poem => poem)
["200",{"Content-Type" => "text/html"}, out]
end
end
use EnvironmentOutput
run MyApp.new
I'm sure its a small error as the code is the same as in the tutorial and it works for him...
Thanks
You'll want to read up on ruby load path (either $LOAD_PATH or $:). By default, ruby has a load path which includes wherever your gems are installed, which is why you can do require 'haml' without providing the full path to where your haml gem is located.
When you type require 'haiku', you're basically telling ruby to look for some file called haiku.rb somewhere in it's load path, and the LoadError comes from ruby not finding your haiku.rb file in any of the directories listed in $LOAD_PATH (or $:, which is just shorthand for $LOAD_PATH).
You can solve this in one of (at least) two ways:
change require 'haiku' to require File.dirname(__FILE__) + '/haiku.rb' to explicitly tell ruby what file to load
add the current working directory to your load path: $:.push(File.dirname(__FILE__)). This way you can keep the require 'haiku' part.

Rails 3 generators in gem

Might sound like a simple question, but I'm stumped.
I've created a gem that essentially contains a generator.
It contains the following structure:
lib
- generators
- my_generator
my_generator_generator.rb (see below)
- templates
my_template_files...
- my_generator.rb (empty file)
test
-test files
GemFile
etc..
However when I add this Gem to my gem file and run rails g, it's not listed. Is there any additional config that I need to do?
My generator roughly looks like this...
class MyGeneratorGenerator < Rails::Generators::NamedBase
source_root File.expand_path('../templates', __FILE__)
generator code....
end
The strange thing is, it works in Cygwin, but not in Ubuntu...
This took a little bit for me to figure out, but I've run into the same problem. Here is how I fixed it.
Tree structure looks like this:
lib
- generators
- gemname
install_generator.rb
- templates
(template files)
Here's the code for install_generator.rb
#lib/generators/gemname/install_generator.rb
require 'rails/generators'
module Gemname
class InstallGenerator < Rails::Generators::Base
desc "Some description of my generator here"
# Commandline options can be defined here using Thor-like options:
class_option :my_opt, :type => :boolean, :default => false, :desc => "My Option"
# I can later access that option using:
# options[:my_opt]
def self.source_root
#source_root ||= File.join(File.dirname(__FILE__), 'templates')
end
# Generator Code. Remember this is just suped-up Thor so methods are executed in order
end
end
When I run
rails g
I see:
Gemname
gemname:install
Some other things you may need to setup:
#lib/gemname.rb
module Gemname
require 'gemname/engine' if defined?(Rails)
# any additional requires
end
and
#/lib/gemname/engine.rb
require 'rails'
module Gemname
class Engine < Rails::Engine
end
end
Some good references I've found on this are:
http://textmate.rubyforge.org/thor/Thor.html (take a look at the modules, especially Thor::Actions)
http://api.rubyonrails.org/classes/Rails/Generators/Base.html
http://api.rubyonrails.org/classes/Rails/Generators/Actions.html
https://github.com/wycats/thor/blob/master/README.md
http://www.themodestrubyist.com/2010/03/16/rails-3-plugins---part-3---rake-tasks-generators-initializers-oh-my/
If you use Railtie, you can define your generator wherever it could be using:
generators do
require "path/to/my_railtie_generator"
end
in Railtie class.

Resources