flajann2 / juwelier

Opinionated tool for creating and managing Rubygem projects using Ruby 2.3 and beyond!
MIT License
91 stars 10 forks source link
gem gemcutter jeweler ruby

** Intro Provides the noble ruby developer with three primary features:

** Migrating from Jeweler Note that if you have a preexisting project created with Jeweler, you may have some issues. Eventally I will provide a migration option, but in the meantime, you may wish to run this bash script from the root directory of your project:

+BEGIN_SRC bash

for f in $(grep -irl jeweler *)
do
  sed -i 's/jeweler/juwelier/g' $f
  sed -i 's/Jeweler/Juwelier/g' $f
done
bundle update

+END_SRC

** Hot Ideas *** Merging Jeweler and Juwelier This just occured to me -- we simply use metaprorgamming to use "Jeweler" or "Juwelier" in the generated code. All else would be the same. So the name of the script that ran would be captured and encoded by the script itself, and then used throughout the generation. That way, all can be happy.

** Hello, world

Use RubyGems to install the heck out of juwelier to get started:

+BEGIN_SRC bash

$ gem install juwelier

+END_SRC

With juwelier installed, you can use the =juwelier= command to generate a new project. For the most basic use, just give it a name:

+BEGIN_EXAMPLE

$ juwelier hello-gem

+END_EXAMPLE

This requires some Git configuration (like name, email, GitHub account, etc), but =juwelier= will prompt along the way.

Your new =hello-gem= gem is ready in the =hello-gem= directory. Take a peek, and you'll see several files and directories

*** More =juwelier= options

The =juwelier= command supports a lot of options. Mostly, they are for generating baked in support for this test framework, or that.

Check out =juwelier --help= for the most up to date options.

** Hello, rake tasks

Beyond just editing source code, you'll be interacting with your gem using =rake= a lot. To see all the tasks available with a brief description, you can run:

+BEGIN_EXAMPLE

$ rake -T

+END_EXAMPLE

You'll need a version before you can start installing your gem locally. The easiest way is with the =version:write= Rake task. Let's imagine you start with 0.1.0

+BEGIN_EXAMPLE

$ rake version:write MAJOR=0 MINOR=1 PATCH=0

+END_EXAMPLE

You can now go forth and develop, now that there's an initial version defined. Eventually, you should install and test the gem:

+BEGIN_EXAMPLE

$ rake install

+END_EXAMPLE

The =install= rake task builds the gem and =gem install=s it. You're all set if you're using [[http://rvm.beginrescueend.com/][RVM]], but you may need to run it with sudo if you have a system-installed ruby:

+BEGIN_EXAMPLE

$ sudo rake install

+END_EXAMPLE

*** Releasing

At last, it's time to [[http://shipitsquirrel.github.com/][ship it]]! Make sure you have everything committed and pushed, then go wild:

+BEGIN_EXAMPLE

$ rake release

+END_EXAMPLE

This will automatically:

/Juwelier Generate =hello-gem.gemspec= and commit it / Use =git= to tag =v0.1.0= and push it * Build =hello-gem-0.1.0.gem= and push it to [[http://rubygems.org/gems/][rubygems.org]]

=rake release= accepts REMOTE(default: =origin=), LOCAL_BRANCH(default: =master=), REMOTE_BRANCH(default: =master=) and BRANCH(default: master)as options.

+BEGIN_EXAMPLE

$ rake release REMOTE=upstream LOCAL_BRANCH=critical-security-fix REMOTE_BRANCH=v3

+END_EXAMPLE

This will tag and push the commits on your local branch named =critical-security-fix= to branch named =v3= in remote named =upstream= (if you have commit rights on =upstream=) and release the gem.

+BEGIN_EXAMPLE

$ rake release BRANCH=v3

+END_EXAMPLE

If both remote and local branches are the same, use =BRANCH= option to simplify. This will tag and push the commits on your local branch named =v3= to branch named =v3= in remote named =origin= (if you have commit rights on =origin=) and release the gem.

*** Version bumping

It feels good to release code. Do it, do it often. But before that, bump the version. Then release it. There's a few ways to update the version:

+BEGIN_EXAMPLE

# version:write like before
$ rake version:write MAJOR=0 MINOR=3 PATCH=0

# bump just major, ie 0.1.0 -> 1.0.0
$ rake version:bump:major

# bump just minor, ie 0.1.0 -> 0.2.0
$ rake version:bump:minor

# bump just patch, ie 0.1.0 -> 0.1.1
$ rake version:bump:patch

+END_EXAMPLE

Then it's the same =release= we used before:

+BEGIN_EXAMPLE

$ rake release

+END_EXAMPLE

** Customizing your gem

If you've been following along so far, your gem is just a blank slate. You're going to need to make it colorful and full of metadata.

You can customize your gem by updating your =Rakefile=. With a newly generated project, it will look something like this:

Juwelier require 'juwelier' ::Tasks.new do |gem| # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options gem.name = "whatwhatwhat" gem.summary = %Q{TODO: one-line summary of your gem} gem.description = %Q{TODO: longer description of your gem} gem.email = "fred.mitchell@gmx.com" gem.homepage = "http://github.com/flajann2/whatwhatwhat" Juwelier gem.authors = ["Joshua Nichols"] end JuwelierJuwelier ::RubygemsDotOrgTasks.new

It's crucial to understand the =gem= object is just a Gem::Specification. You can read up about it at [[http://guides.rubygems.org/specification-reference/][guides.rubygems.org/specification-reference]]. This is the most basic way of specifying a gem, -managed or not. just exposes this to you, in addition to providing some reasonable defaults, which we'll explore now.

*** Project information

+BEGIN_EXAMPLE

gem.name = "whatwhatwhat"

+END_EXAMPLE

Every gem has a name. Among other things, the gem name is how you are able to =gem install= it. [[http://guides.rubygems.org/specification-reference/#name][Reference]]

+BEGIN_EXAMPLE

gem.summary = %Q{TODO: one-line summary of your gem}

+END_EXAMPLE

This is a one line summary of your gem. This is displayed, for example, when you use =gem list --details= or view it on [[http://rubygems.org/gems/][rubygems.org]].

+BEGIN_EXAMPLE

gem.description = %Q{TODO: longer description of your gem}

+END_EXAMPLE

Description is a longer description. Scholars ascertain that knowledge of where the description is used was lost centuries ago.

+BEGIN_EXAMPLE

gem.email = "fred.mitchell@gmx.com"

+END_EXAMPLE

This should be a way to get a hold of you regarding the gem.

+BEGIN_EXAMPLE

gem.homepage = "http://github.com/flajann2/whatwhatwhat"

+END_EXAMPLE

The homepage should have more information about your gem. The juwelier generator guesses this based on the assumption your code lives on [[http://github.com/][GitHub]], using your Git configuration to find your GitHub username. This is displayed by =gem list --details= and on rubygems.org.

+BEGIN_EXAMPLE

gem.authors = ["Joshua Nichols"]

+END_EXAMPLE

Hey, this is you, the author (or me in this case). The =juwelier= generator also guesses this from your Git configuration. This is displayed by =gem list --details= and on rubygems.org.

** Juwelier Files

ThJuweliere quickest way to add more files is to =git add= them. uses your Git repository to populate your gem's files by including added and committed and excluding =.gitignore=d. In most cases, this is reasonable enough.

If you need to tweak the files, that's cool. populates =gem.files= as a =Rake::FileList=. It's like a normal array, except you can =include= and =exclude= file globs:

+BEGIN_EXAMPLE

gem.files.exclude 'tmp' # exclude temporary directory
gem.files.include 'lib/foo/bar.rb' # explicitly include lib/foo/bar.rb

+END_EXAMPLE

If that's not enough, you can just set =gem.files= outright

+BEGIN_EXAMPLE

gem.files = Dir.glob('lib/**/*.rb')

+END_EXAMPLE

*** Dependencies

Dependencies let you define other gems that your gem needs to function. =gem install your-gem= will install your-gem's dependencies along with it, and when you use your-gem in an application, the dependencies will be made available. Use =gem.add_dependency= to register them. [[http://guides.rubygems.org/specification-reference/#add_development_dependency][Reference]]

+BEGIN_EXAMPLE

gem.add_dependency 'nokogiri'

+END_EXAMPLE

This will ensure a version of =nokogiri= is installed, but it doesn't require anything more than that. You can provide extra args to be more specific:

+BEGIN_EXAMPLE

gem.add_dependency 'nokogiri', '= 1.2.1' # exactly version 1.2.1
gem.add_dependency 'nokogiri', '>= 1.2.1' # greater than or equal to 1.2.1, ie, 1.2.1, 1.2.2, 1.3.0, 2.0.0, etc
gem.add_dependency 'nokogiri', '>= 1.2.1', '< 1.3.0' # greater than or equal to 1.2.1, but less than 1.3.0
gem.add_dependency 'nokogiri', '~> 1.2.1' # same thing, but more concise

+END_EXAMPLE

When specifying which version is required, there's a bit of the condunrum. You want to allow the most versions possible, but you want to be sure they are compatible. Using =>= 1.2.1= is fine most of the time, except until the point that 2.0.0 comes out and totally breaks backwards the API. That's when it's good to use =~> 1.2.1=, which requires any version in the =1.2= family, starting with =1.2.1=.

** Juwelier Executables

Executables let your gem install shell commands. Just put any executable scripts in the =bin/= directory, make sure they are added using =git=, and will take care of the rest.

When you need more finely grained control over it, you can set it yourself:

+BEGIN_EXAMPLE

gem.executables = ['foo'] # note, it's the file name relative to `bin/`, not the project root

+END_EXAMPLE

*** Versioning

WeJuwelierJuwelier discussed earlier how to bump the version. The rake tasks are really just convience methods for manipulating the =VERSION= file. It just contains a version string, like =1.2.3=.

=VERSION= is a convention used by , and is used to populate =gem.version=. You can actually set this yourself, and won't try to override it:

+BEGIN_EXAMPLE

gem.version = '1.2.3'

+END_EXAMPLE

A common pattern is to have this in a version constant in your library. This is convenient, because users of the library can query the version they are using at runtime.

+BEGIN_EXAMPLE

# in lib/foo/version.rb
class Foo
  module Version
    MAJOR = 1
    MINOR = 2
    PATCH = 3
    BUILD = 'pre3'

    STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
  end
end

# in Rakefile

+END_EXAMPLE

Juwelier require 'juwelier' require './lib/foo/version.rb' ::Tasks.new do |gem| # snip gem.version = Foo::Version::STRING end

** Juwelier Rake tasks

lives inside of Rake. As a result, they are dear friends. But, that friendship doesn't interfere with typical Rake operations.

The Juwelier Rake means you can define your own namespaces, tasks, or use third party Rake libraries without cause for concern.

** New Features *** Rusty Gems Support Beginning with version 2.4.0, we now have integration with Rust, so you are able to write Rust-enabled gems. Your users will have to have Rust installed on their systems, but this is easy to do.

This interface currently uses FFI, and I don't have all
the bugs worked out, so use at your own risk (for now).

**** Example The example code generated illustrates how to pass strings to Rust, and also how to pass data structures to Rust as JSON (highly recommended) and have it reformed into Rust structures in a typesafe manner.

 First, create your Rusty Gem:
 #+begin_src bash
 juwelier --semver --rusty foo
 #+end_src

 Next, cd into the foo directory and run bundle
 and attempt to run the test:
 #+begin_src bash
 bundle
 ruby lib/foo.rb
 #+end_src

 The test attempt should fail, since we have not built
 the Rust extension yet. Do so by doing the following:
 #+begin_src
 cd rust
 make
 cd ..
 #+end_src

 And then attempt to run the test again:
 #+begin_src bash
 ruby lib/foo.rb
 #+end_src

 That should work. Now install the gem locally with:
 #+begin_src bash
 rake gemspec
 rake install
 #+end_src

 And that should install your foo gem cleanly.

** Release Notes | Version | Date | Notes | |---------+------------+-------------------------------------------------------------------------------------------------------------| | 2.4.7 | 2017-06-24 | Bundle update to allow the latest dependencies. | | 2.4.5 | 2017-05-14 | Fixed Rusty interfacing example and the segfault it was generating. | | 2.4.0 | 2017-05-09 | Support for Rusty Gems | | 2.3.5 | 2017-02-10 | Revving Semver to be Semver2 | | 2.2.3 | 2016-11-21 | Psych bug fixed | | 2.2.2 | 2016-11-19 | Added support for pry -- includes pry, pry-byebug, pry-doc, pry-remote, pry-rescue. and pry-stack_explorer. | | 2.2.0 | 2016-11-19 | Bugs with --semver fixed, new options for using .org and .markdown for README | | 2.1.3 | 2016-11-19 | Problems with --semver, --required-version |

** Known Issues | Date | Issue | |------------+----------------------------------------------------------------------------------------------------------| | 2017-02-10 | Orgmode and Markdown sync issue. They are out of sync for various reasons, and this must be ameorilated. | | 2016-11-19 | On generation of the Markdown, the initial title does not linefeed before the header sequence. |

** Contributing to

** Copyright

Copyright (c) 2016 Fred Mitchell. See LICENSE for details.