I'm building a gem with Rust and I need to know how to run the Rust compiler when installing the gem. With a C-extension, I can use mkmf to generate a Makefile. But how can I run cargo build --release?
The directory structure looks like this:
.
├── bin
│ ├── console
│ └── setup
├── CODE_OF_CONDUCT.md
├── Gemfile
├── Gemfile.lock
├── lib
│ ├── rmpd_adschedule
│ │ └── version.rb
│ └── rustgem.rb
├── LICENSE.txt
├── Rakefile
├── README.md
├── rustgem.gemspec
├── rust
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
└── spec
├── rustgem.rb
└── spec_helper.rb
I've created a Rake task to build the Rust library:
task :compile do
sh "cd #{File.dirname(__FILE__)}/rust && cargo build --release"
end
But how to run this Rake task when installing the gem? Or how to generate a suitable Makefile with mkmf? The Makefile should look like this:
all:
cd rust/ && cargo build --release
But it doesn't work if I put it into the root directory, and I don't know how to tell mkmf to generate this exact Makefile.
I found one stupid way to do this when the gem is loading:
module Rustgem
system("cd #{File.dirname(__FILE__)}/../rust && cargo build --release")
end
But this is not a good solution.
Turns out I can do this:
# rust/extconf.rb
require 'mkmf'
create_makefile 'rust/rustgem'
system("cd #{File.dirname(__FILE__)} && cargo build --release")
In this case, create_makefile will create an empty Makefile. Then make will return 0 make: Nothing to be done for 'all'. which is what we need, and then system call will do the work.
Or somewhat better:
require 'mkmf'
create_makefile 'rutgem'
File.write('Makefile', "all:\n\tcargo build --release\nclean:\n\trm -rf target\ninstall: ;")
I'm not sure if this is good solution. If you know a better one please tell it.
UPDATE
Actually, you can put Makefile along with empty extconf.rb in the same directory and it will work. Blog post about Ruby-Rust integration: http://undefined-reference.org/2016/05/14/writing-rubygem-in-rust.html
Related
Here is current gem structure:
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── mygem.gemspec
├── images
│ ├── 1b1d4bde376084011d027bba1c047a4b.jpg
│ ├── 1d468d064d2e26b5b5de9a0241ef2d4b.jpg
│ ├── 309666c7b45ecbf8f13e85a0bd6b0a4c.jpg
│ ├── 3f9f3db06db20d1d9f8188cd753f6ef4.jpg
│ ├── 679634ff89a31279a39f03e278bc9a01.jpg
│ ├── 6d97739b4a08f965dc9239dd24382e96.jpg
│ ├── 71662d4d4029a3b41d47d5baf681ab9a.jpg
│ ├── 92d90b8977f813af803c78107e7f698e.jpg
│ ├── ad8a37f872956666c3077a3e9e737984.jpg
│ └── df0a3b93e9412536ee8a11255f974141.jpg
├── lib
│ └── mygem.rb
└── spec
└── _spec.rb
The ./images folder does not really exist until you run rspec the first time -- tests do download these binary files from a remote storage, use them and cache them here for later.
There are also rake tasks that use these files and instead of leaving the developer a memo "run this rake task only after you ran rspec" I would like to put the download_and_keep() procedure in some common place callable by both rspec and rake. The question is what is the recommended place to put such method?
There is a folder ./spec/support by rspec design but I'm thinking about switching to minitest so I need something more universal.
P.S.: can't think up more tags for this question.
There isn't really a standard for such a directory. The important part is to pick a name that, at a glance, doesn't sound like it's supposed to be included in the shipped gem.
For some inspiration, rails/rails has a tools, and rubygems/rubygems has util. A top-level support seems like it'd make a fair choice too.
My problem is the following:
I can't require a ruby file from any directory other than the one with all the main files, i.e. requires like this: ./file work fine, but requires like this: dir_name/file fail.
Here's my project structure:
├── bin
│ ├── console
│ └── setup
├── CODE_OF_CONDUCT.md
├── Gemfile
├── lib
│ ├── riverbattle
│ │ ├── base.rb <------- main file that starts everything
│ │ ├── colorful.rb
│ │ ├── computer.rb
│ │ ├── constants.rb
│ │ ├── exit_error.rb
│ │ ├── field.rb
│ │ ├── game.rb
│ │ ├── human.rb
│ │ ├── invalid_move_error.rb
│ │ ├── move.rb
│ │ ├── player.rb
│ │ ├── version.rb
│ │ └── victory_error.rb
│ └── riverbattle.rb <------ the file from where I get the error
├── LICENSE
├── LICENSE.txt
├── Rakefile
├── README.md
├── spec
│ ├── spec_helper.rb
│ └── riverbattle_spec.rb
├── riverbattle-0.1.2.gem
└── riverbattle.gemspec
So, when I launch the app like this: ruby base.rb when I am in the lib/riverbattle/ directory, everything launches fine, but when I try to do ruby riverbattle.rb from the directory lib/, I get the following error:
➜ lib git:(master) ✗ ruby riverbattle.rb
/home/denis/.rvm/rubies/ruby-2.2.1/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in require': cannot load such file -- ./field (LoadError) from
/home/denis/.rvm/rubies/ruby-2.2.1/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:inrequire' from
/home/denis/.rvm/gems/ruby-2.2.1/gems/riverbattle-0.1.2/lib/riverbattle/game.rb:1:in
<top (required)>' from
/home/denis/.rvm/rubies/ruby-2.2.1/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:inrequire' from
/home/denis/.rvm/rubies/ruby-2.2.1/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in require' from
/home/denis/.rvm/gems/ruby-2.2.1/gems/riverbattle-0.1.2/lib/riverbattle/base.rb:1:in
' from
/home/denis/.rvm/rubies/ruby-2.2.1/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in require' from
/home/denis/.rvm/rubies/ruby-2.2.1/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:inrequire' from riverbattle.rb:2:in `'
I just don't get it why it cannot require one single file.
By the way, here's the content of the file (riverbattle.rb) from which I get the error:
require "riverbattle/version"
module Riverbattle
require "riverbattle/base"
end
and nothing else.
I was told that require actually requires an absolute path to the file and I need to do something with my $LOAD_PATH or something, but this information doesn't help at all to solve the issue. Why is it okay to require files from the same directory, but not okay, if I require them from another one?
1) Could anybody help me with solving that problem?
2) Is it the problem with only my computer and I can safely publish it as a gem (i.e. it may work properly on another computer), or it is the wrong code that doesn't work?
require searches in your $LOAD_PATH. require should generally be used if you want to use a gem that is installed on your system. For your purposes you want to use require_relative:
require_relative 'riverbattle/version'
Note that at first glance
require './riverbattle/version'
might also seem to work. However, . here points to the current directory from where you run the process, not the directory where the file resides, obviously this is not what you want.
If lib/base.rb is your main file, then perhaps it should be the "executable" file? If that is the case, should it be in bin?
$LOAD_PATH can be added to by using -I option with the ruby executable.
ruby -Ipath_to_project_root/lib lib/main.rb
would then let any file that requires files in your project look in the lib folder.
It can also be used like the answer in your other question with the $: or $LOAD_PATH.
Look at how other gems are set up, and you will find that your base.rb file can live in the bin folder, just like your setup and console files are. If it is the main script that starts it all, it could be named riverbattle as is common with gems to have an executable of the same or similar name.
require is not used to load gems, it is used to load files and without any explicit path given, will look in the load path. It doesn't matter if it is a gem or not.
require_relative is used to load files that are always local relative to the file that is using the require_relative method.
Of course, the documentation is your friend.
I'm new in ruby and I'm trying to create a GEM. The "problem" is that I found really hard to execute the code I'm building, without having to repeat a lot of steps.
This is basically my gem structure
├── Gemfile
├── Gemfile.lock
├── Guardfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── bin
│ └── hermes
├── hermes.gemspec
├── lib
│ ├── hermes
│ │ ├── code.rb
│ │ ├── issue.rb
│ │ └── version.rb
│ └── hermes.rb
├── requirements.md
└── spec
├── hermes_spec.rb
└── spec_helper.rb
So I'm running bundle exec irb, then require 'hermes'and then Hermes.init, but if I change any of the files code, I have to exit irb and repeat the steps. Is there a better way of doing it? Which is the "correct" way of doing it (without using rspec) ?
Thanks!
You can use pry and then load "path/to/file.rb" every time you change your code. You can also give it a simple web interface with sinatra and use shotgun to have your code reloaded on the fly.
But, the correct way is using rspec. Nothing better than to start fresh and effective employing test driven development. Trust me, you'll be ahead of 90% of the market by just starting with tdd from scratch.
I am developing a ruby gem which will have a binary.
I am trying to develop the binary but i am worried its not finding my requires because the gem isnt installed as a gem is there a way to test the binary without packaging it as a gem?
#!/usr/bin/env ruby
require "middleman_ember_scaffold/load_paths"
# Start the CLI
MiddlemanEmberScaffold::Cli::Base.start
sits in a file named mse and ive added my bin folder of gem to path
.
└── middleman_ember_scaffold
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── bin
│ └── mes
├── lib
│ ├── middleman_ember_scaffold
│ │ ├── cli.rb
│ │ ├── load_paths.rb
│ │ └── version.rb
│ └── middleman_ember_scaffold.rb
└── middleman_ember_scaffold.gemspec
4 directories, 10 files
when i run mes i get
/Users/justin/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require': cannot load such file -- middleman_ember_scaffold/load_paths (LoadError)
from /Users/justin/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
from /Users/justin/middleman-generator/middleman_ember_scaffold/bin/mes:7:in `<main>'
i'd like to be able to run and develop mes without re-packaging everytime i make a change.
Probably a "better" way would be to do the following:
$ ruby -I./lib bin/mes
It does the same as changing your load path, but it only does it for the command you're executing.
Use RUBYLIB Environment Variable
The problem you're facing is that your source directory isn't getting some of magic applied to installed gems, and therefore doesn't have your lib directory in the $LOAD_PATH. While there are other ways to deal with this, for testing I'd recommend just adding your lib directory to the RUBYLIB environment variable. For example:
RUBYLIB="/path/to/middleman_ember_scaffold/lib:$RUBYLIB"
export RUBYLIB
bin/mes
should work for any Bourne-compatible shell. If you're running Bash, and don't have anything else stored in RUBYLIB, you might even be able to shorten the invocation to:
RUBYLIB="/path/to/middleman_ember_scaffold/lib" bin/mes
Either way, once Ruby knows what directories it should add to the $LOAD_PATH everything should work just fine.
I have a project tree as below:
├── bin
├── fpgrowth-ruby-0.0.1.gem
├── fpgrowth-ruby.gemspec
├── Gemfile
├── Gemfile.lock
├── lib
│ ├── fpgrowth
│ │ ├── fptree
│ │ │ ├── builder
│ │ │ │ ├── first_pass.rb
│ │ │ │ └── second_pass.rb
│ │ │ ├── fp_tree.rb
│ │ │ └── node.rb
│ │ ├── models
│ │ │ └── transaction.rb
│ │ └── ruby
│ │ └── version.rb
│ └── fpgrowth.rb
├── LICENSE.txt
├── Rakefile
├── README.md
└── test
└── tc_first_pass.rb
In the TestCase for first_pass I wrote:
require 'test/unit'
require "../lib/fpgrowth/fptree/builder/first_pass"
Then I get this:
ruby test/tc_first_pass.rb
/home/damien/.rvm/rubies/ruby-1.9.3-p392/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require': cannot load such file -- ../lib/fpgrowth/fptree/builder/first_pass (LoadError)
from /home/damien/.rvm/rubies/ruby-1.9.3-p392/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
from test/tc_first_pass.rb:2:in `<main>'
Something is wrong, but I don't know what.
Use the -I flag on the ruby command line, to specify a require path at runtime.
from your top level directory
ruby -I lib test/tc_first_pass.rb
The above tells the ruby interpreter to include /lib in the load path for this execution only.
Then for your require line,
require 'fpgrowth/fptree/builder/first_pass'
For gem building, and organizing your source I suggest reading chapters on organizing your source, and distributing and packaging your code from the Programming Ruby book found here: http://pragprog.com/book/ruby3/programming-ruby-1-9
You can't require files like that unless you use require_relative.
Barring that, you should do is alter $LOAD_PATH to include ../lib.
You could use the File class-methods to help you.
First is to start off at the directory not relative to the cwd, but relative to the file calling the require. They might not be the same.
require File.dirname(__FILE__) + "../lib/fpgrowth/fptree/builder/first_pass"
This, however, is not very portable and can be cleaned up by using the join class-method:
require File.join(File.dirname(__FILE__), '..', 'lib', 'fpgrowth', 'fptree', 'builder', 'first_pass')
But you probably find yourself adding this all over the place, not? In that case, consider adding a helper in fpgrowth.rb:
def self.root
Pathname.new(File.expand_path(File.dirname(__FILE__)))
end
Now, you can use that helper all over the place:
FpGrowth.root #=> "/absolute/path/to/fpgrowth/lib"
FpGrowth.root.join("fpgrowth", "fbtree", "builder") #=> "/absolute/path/to/fpgrowth/lib/fpbrowth/fbtree/builder"