Testing Jekyll plugin with Cucumber - ruby

I am developing a plugin for Jekyll and want to use Cucumber and Aruba to test it. My plugin is currently just adding a command to Jekyll like it specifies here: https://jekyllrb.com/docs/plugins/#commands I'm bundling this as a gem.
This is the structure of my gem folder:
├── Gemfile
├── Gemfile.lock
├── LICENSE.txt
├── README.md
├── Rakefile
├── bin
│   ├── console
│   └── setup
├── features
│   ├── hello_world.feature
│   ├── step_definitions.rb
│   └── support
│   └── env.rb
├── hello_world.gemspec
├── lib
│   ├── hello_world
│   │   └── version.rb
│   └── hello_world.rb
├── pkg
│   └── hello_world-0.1.0.gem
└── spec
├── hello_world_spec.rb
└── spec_helper.rb
This is my lib/hello_world.rb file:
require "hello_world/version"
require "jekyll"
module Jekyll
module Commands
class Hello < Command
class << self
def init_with_program(prog)
prog.command(:hello) do |c|
c.action do |args, options|
Jekyll.logger.info "Hello world!"
end
end
end
end
end
end
end
Here is my features/hello_world.feature file:
Feature: Hello world
As a hacker who likes to blog
I want to have a custom Jekyll command
Scenario: Show Hello world
When I run `jekyll hello`
Then the output should contain "Hello world!"
And the exit status should be 0
However this is what I get:
$ cucumber
Feature: Hello world
As a hacker who likes to blog
I want to have a custom Jekyll command
Scenario: Show Hello world # features/hello_world.feature:6
When I run `jekyll hello` # aruba-0.14.2/lib/aruba/cucumber/command.rb:13
Then the output should contain "Hello world!" # aruba-0.14.2/lib/aruba/cucumber/command.rb:159
expected "fatal: 'jekyll hello' could not be found. You may need to install the jekyll-hello gem or a related gem to be able to use this subcommand. " to string includes: "hello world" (RSpec::Expectations::ExpectationNotMetError)
features/hello_world.feature:8:in `Then the output should contain "Hello world!"'
And the exit status should be 0 # aruba-0.14.2/lib/aruba/cucumber/command.rb:277
Failing Scenarios:
cucumber features/hello_world.feature:6 # Scenario: Help show a helpful message
1 scenario (1 failed)
3 steps (1 failed, 1 skipped, 1 passed)
I believe that the issue must have something to do with the way Jekyll plugins are required: https://jekyllrb.com/docs/plugins/#installing-a-plugin They are supposed to be required in the :jekyll_plugins group of the Gemfile.
In my Gemfile I have tried just referencing just the gemspec and trying to include the gemspec into the :jekyll_plugins group like this and neither option has worked:
source 'https://rubygems.org'
gemspec :group => :jekyll_plugins
I have also tried building my gem, installing it locally and then using it in a test Jekyll build (including via the :jekyll_plugins group in the Gemfile) and it works correctly.
How can I get my plugin's command to "register" correctly with Jekyll so that it is available for my Cucumber test?

One way to achieve this would by defining a step-definition that creates a fixture site with the proper Gemfile
Feature: Hello world
As a hacker who likes to blog
I want to have a custom Jekyll command
Scenario: Show Hello world
Given I have a fixture site
When I run `jekyll hello`
Then the output should contain "Hello world!"
And the exit status should be 0
In the above, executing the Given step is supposed to create a fixture site with a Gemfile that points to your plugin and a basic jekyll-config file. (Edit your step_definitions.rb file to do so)
# Gemfile
gem "jekyll", "~> 3.6"
group :jekyll_plugins do
# point to the location of your plugin's gemspec, relative to this file
gem "hello_world", :path => "path/to/repo root"
end

Related

What is the recommended folder structure for a Ruby command-line app with tests?

I'm new to the Ruby development ecosystem and learning Rails in my spare time. However, I would like to build a command-line app from scratch with unit tests as a part of my learning.
What is the recommended structure for a Ruby command-line project? The one I came up with is
/test
- person_test.rb
/src
- person.rb
main.rb
Gemfile
Gemfile.lock
Thanks
Unit Tests for Gems
If you're using MiniTest or similar, the expected place within gem foo would be foo/test. If you're using RSpec or minitest/spec, then you'd use foo/spec instead.
When you generate a new gem skeleton with Bundler it provides the following defaults:
$ bundle gem foo; tree foo
foo
├── Gemfile
├── README.md
├── Rakefile
├── bin
│   ├── console
│   └── setup
├── foo.gemspec
├── lib
│   ├── foo
│   │   └── version.rb
│   └── foo.rb
└── spec
├── foo_spec.rb
└── spec_helper.rb
4 directories, 10 files
The default Rakefile is set up to run RSpec, but you can certainly implement the Rakefile's default task and your tests any way you'd like.

Manually testing a Ruby CLI

I'm building a CLI gem with Ruby using Thor. I run rake install which runs rake build then the task to install the gem locally. However when I try to run it in the commandline, it cannot locate the command. The gem is called smokestack so theoretically I should be able to run it in terminal once it's installed.
Structure:
├── CODE_OF_CONDUCT.md
├── Gemfile
├── Gemfile.lock
├── LICENSE.txt
├── README.md
├── Rakefile
├── bin
│   ├── console
│   ├── setup
│   └── smokestack
├── lib
│   ├── smokestack
│   │   ├── build.rb
│   │   ├── cli.rb
│   │   └── version.rb
│   └── smokestack.rb
├── pkg
│   └── smokestack-0.1.0.gem
├── smokestack.gemspec
└── test
├── build_test.rb
├── smokestack_test.rb
└── test_helper.rb
bin/smokestack:
#!/usr/bin/env ruby -wU
require 'smokestack'
Smokestack::Cli.start(ARGV)
You can see in the tree the pkg folder from when I ran rake install.
Here's the result when I run that:
smokestack 0.1.0 built to pkg/smokestack-0.1.0.gem.
smokestack (0.1.0) installed.
I then run smokestack in my terminal and get the error: zsh: command not found: smokestack
I've also tried gem install --local ~/path/to/gem/pkg/gem.gem
Result:
Successfully installed smokestack-0.1.0
Parsing documentation for smokestack-0.1.0
Done installing documentation for smokestack after 0 seconds
1 gem installed
This results in the same 'command not found error'.
Question How am I supposed to run this CLI locally to test it out during development?
It ideally interacts with the current project it's in, so just running bundle exec bin/smokestack inside of it's own directory won't yield the results I need properly. It should be a system CLI anyway right?
Here is the repo for additional context if necessary.
There were a couple things going wrong with this. First, bundle moved executable out of /bin to /exe. See [this article(http://bundler.io/blog/2015/03/20/moving-bins-to-exe.html) for reference.
I moved bin/smokestack to exe/smokestack.
You also have to make sure and stage your files, cause the .gemspec files gets the gem files list by running git ls-files. Now that all that is done, everything appears to be working.

How to make C library for ffi during `gem install`

Recently I wrote a gem using ffi. With this help, the ruby code can call the C extensions with a dynamic library, which I compile all C extensions into.
My gem structure is like this:
.
├── ext
│   ├── common.c
│   ├── common.h
│   ├── Makefile
│   └── ...
├── Gemfile
├── lib
│   ├── ...
│   │   └── version.rb
│   └── ...
├── Rakefile
├── README.md
└── test
What I plan to do is running make in ./ext, then everything goes ok.
But, the problem occurs. If other users install my gem with gem install, how to make C library for themselves?
Is any one willing to offer me a help? Any idea is welcome.
Take a look at ffi-compiler. There's an example on the gem's page:
Example
Directory layout
lib
|- example
|- example.rb
ext
|- example.c
|- Rakefile
example.gemspec
lib/example/example.rb
require 'ffi'
require 'ffi-compiler/loader'
module Example
extend FFI::Library
ffi_lib FFI::Compiler::Loader.find('example')
# example function which takes no parameters and returns long
attach_function :example, [], :long
end
ext/example.c
long
example(void)
{
return 0xdeadbeef;
}
ext/Rakefile
require 'ffi-compiler/compile_task'
FFI::Compiler::CompileTask.new('example') do |c|
c.have_header?('stdio.h', '/usr/local/include')
c.have_func?('puts')
c.have_library?('z')
end
example.gemspec
Gem::Specification.new do |s|
s.extensions << 'ext/Rakefile'
s.name = 'example'
s.version = '0.0.1'
s.email = 'ffi-example'
s.files = %w(example.gemspec) + Dir.glob("{lib,spec,ext}/**/*")
s.add_dependency 'rake'
s.add_dependency 'ffi-compiler'
end
Build gem and install it
gem build example.gemspec && gem install example-0.0.1.gem
Successfully built RubyGem
Name: example
Version: 0.0.1
File: example-0.0.1.gem
Building native extensions. This could take a while...
Successfully installed example-0.0.1
Test it
$ irb
2.0.0dev :001 > require 'example/example'
=> true
2.0.0dev :002 > puts "Example.example=#{Example.example.to_s(16)}"
Example.example=deadbeef
=> nil
You can run make command during gem installation this way:
add spec.extensions << 'ext/mkrf_conf.rb' to your gemspec.
create ext/mkrf_conf.rb file:
task :default { `make` }
enjoy!

How does one test a gem with native extensions when bundler includes gem from source?

I hope I'm being a bit dense, but I can't figure out how one is supposed to develop and test gems with native extensions using bundler when bundler seems not to build them.
I have a gem, swig_try1 which is just my exercise in how to build a native extension. I can build the gem and install it and I see the expected Building native extensions message, the build succeeds, and when I go to another directory, I can require the gem and use methods defined in the native extension.
My problem lies in the fact that when I run bundle install I see this line:
Using swig_try1 (0.0.1) from source at .
But I don't see the Building native extensions message, and it doesn't show up in my directory structure:
.
├── Gemfile
├── Gemfile.lock
├── LICENSE.txt
├── README.md
├── Rakefile
├── ext
│   └── swig_try1
│   ├── extconf.rb
│   └── swig.c
├── lib
│   ├── swig_try1
│   │   └── version.rb
│   └── swig_try1.rb
├── pkg
│   └── swig_try1-0.0.1.gem
└── swig_try1.gemspec
i.e, no Makefile was generated in ext/swig_try1, as well as no swig_try1.bundle (in my rvm gems directory, I see these files).
I could go about my business and only ever use my gem from some other directory...but what about tests? I'm used to using rspec from my gem directory, but from there it always tries to load the version at . which doesn't have the native extension built.
This issue on github suggests that bundler might not be able to handle this? How can that be possible?
Please help me see what I'm missing.
Generally this is done as a rake task e.g. rake compile, which you ensure is run before tests.
I put together a skeleton gem that does this, amongst other standard things expected in a native-extension gem, here: https://github.com/neilslater/ruby_nex_c
The key lines in the Rakefile are:
require 'rake/extensiontask'
gemspec = Gem::Specification.load('foo.gemspec')
Rake::ExtensionTask.new do |ext|
ext.name = 'foo'
ext.ext_dir = 'ext/foo'
ext.lib_dir = 'lib/foo'
ext.gem_spec = gemspec
end
task :default => [:compile, :test]
You then can run the combined compile/test with the simple command rake, which is quite handy for Travis CI.
Many other structures are possible - I gleaned this one by reading through a variety of gems on github, until I found something that I felt was a good fit to a gem created with bundle gem.

Creating Ruby Gem

I'm learning ruby and I wanted to test how to create gem file. I have followings installed in my machine.
ruby 1.9.3p362 (2012-12-25 revision 38607) [x86_64-linux]
Bundler version 1.2.3
rake, version 10.0.3
I created a gem using bundle gem hello_gem. I added following sample code to hello_gem.rb
module HelloGem
class Base
def self.hello
puts "Hello Ruby Gem #{HelloGem::VERSION}"
end
end
end
My folder structure is like follows.
├── Gemfile
├── Gemfile.lock
├── hello_gem.gemspec
├── lib
│   ├── hello_gem
│   │   └── version.rb
│   └── hello_gem.rb
├── LICENSE.txt
├── Rakefile
├── README.md
Then I created the gem using rake install. Then I started irb and I can execute following.
1.9.3-p362 :001 > require 'hello_gem'
=> true
1.9.3-p362 :002 > HelloGem::Base.hello
Hello Ruby Gem 0.0.1
=> nil
1.9.3-p362 :003 >
Problem comes when I wanted to move code to lib folder. I created lib/hello_gem/base.rb and added the above code there. And in the hello_gem.rb I just used require "hello_gem/base". Now my project look like follows.
├── Gemfile
├── Gemfile.lock
├── hello_gem.gemspec
├── lib
│   ├── hello_gem
│   │   ├── base.rb
│   │   └── version.rb
│   └── hello_gem.rb
├── LICENSE.txt
├── Rakefile
├── README.md
When I build the gem using rake install and use irb to test following error happened.
1.9.3-p362 :001 > require 'hello_gem'
LoadError: cannot load such file -- hello_gem/base
from /home/sandarenu/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
from /home/sandarenu/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
from /home/sandarenu/.rvm/gems/ruby-1.9.3-p362/gems/hello_gem-0.0.1/lib/hello_gem.rb:2:in `<top (required)>'
from /home/sandarenu/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:60:in `require'
from /home/sandarenu/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:60:in `rescue in require'
from /home/sandarenu/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:35:in `require'
from (irb):1
from /home/sandarenu/.rvm/rubies/ruby-1.9.3-p362/bin/irb:16:in `<main>'
1.9.3-p362 :002 >
I can't find a way to fix this issue. It would be a great help if somebody can tell me what I'm doing wrong here.
Thanks in advance.
The .gemspec created by bundle gem uses Git to determine which files to include in the gem; it contains the line:
gem.files = `git ls-files`.split($/)
In order for it to add your hello_gem/base.rb you need to add it to the Git repository. Since the original setup works for you I assume you have Git installed, so you just need to run:
git add lib/hello_gem/base.rb
You don’t actually need to commit the file for git ls-files to pick it up and add it to the gem, so this should be enough to get it to work.

Resources