Extracting a Gem from your Rails app
So you got that big pile of useful code.
Benefits of extracting
- Cleaner code and architecture
- External collaborators
- Fresh and new ideas
- Reusable and sharable code
- Test in isolation
- Give back to the community
- Knowledge on requirement, use cases and edge cases
- Implementation already exists
- You already have an app to test it
- Focus on the interface
Where to find useful code to extract?
Are you solving common problems?
Have some cool scripts?
Where to find useful code to extract?
- Custom validations
- Test matchers
- Wrapper around an external API
- Additions or changes to Rails
- PORO modeling common things
- And many more!
This talk is a step by step guide to extract a gem from existing code
Identify and move the key parts of your code.
Refactor your code to move as much as possible to lib/
Test in isolation
Setup of the gem
Bundle gem [gemname]
- Code of conduct
- RSpec (minitest available)
- Travis-CI ready
Rails plugin new [gemname]
- Dummy rails app
- Minitest only
- Rake tasks setup
- Useful for Rails Engines
Small number of variations Gemspec, licence, readme, etc
I did both commands
# Make dir and git init commands > bundle gem has_prerequisite > cd has_prerequisite > git commit -Am "init the gem" > cd .. > rails plugin new has_prerequisite --skip-test --dummy-path=spec/dummy > cd has_prerequisite > git add -p > git commit -m "init the rails specific setup of the gem"
- Edit the Gemspec with the gems info
- Tests are running?
- Online Git repository
Gem::Specification.new do |s| s.name = 'hola' s.version = '0.0.0' s.date = '2010-04-28' s.summary = "Hola!" s.description = "A simple hello world gem" s.authors = ["Nick Quaranto"] s.email = 'firstname.lastname@example.org' s.files = ["lib/hola.rb"] s.homepage = 'http://rubygems.org/gems/hola' s.license = 'MIT' end
Move the code!
Move one test first
- Make sure required dev and test dependencies are in the Gemspec (rspec-rails, shoulda-matchers, its, etc)
- Some minor changes to the test setup are expected
- Make sure they run
- Make sure they fail properly
Move the implementation
- If step 2 was skipped, you might have broken dependencies
- Add them to the gemfile and require
- Implement components you don't want to be dependent on
- Make sure to not move any secrets
I decided to keep the dependency on Active Support but not Devise.
So I have to implement similar methods
Failures: 1) HasPrerequisite redirection it redirects to the path when the prerequisite is not met Failure/Error: store_location_for(:user, request.fullpath) NoMethodError: undefined method `store_location_for' for #<#<Class:0x007f9d98a359e8>:0x007f9d9b931238> # ./lib/has_prerequisite.rb:36:in `perform_checks' # ./spec/has_prerequisite_spec.rb:43:in `block (2 levels) in <top (required)>' # ./spec/has_prerequisite_spec.rb:55:in `block (3 levels) in <top (required)>'
Hooks for initialisation
- Extend core classes
- Set generators
module HasPrerequisite class Railtie > ::Rails::Railtie initializer "has_prerequisite.configure_view_controller" do |app| ActiveSupport.on_load :action_controller do include HasPrerequisite end end end end
- Integration test (I have a dummy app!)
- Pack the gem locally
- Reference it from your application
Method 1: Reference from a path
gem 'has_prerequisite', path: '~/dev/has_prerequisite'
> bundle install => It will use your gem > rspec
Method 2: Pack and install locally
> gem build has_prerequisite.gemspec Successfully built RubyGem Name: has_prerequisite Version: 0.0.0 File: has_prerequisite-0.0.0.gem > gem install ./has_prerequisite-0.0.0.gem Successfully installed has_prerequisite-0.0.0 1 gem installed
I recommend following Semantic Versioning
MAJOR . MINOR . PATCH
- Production ready?
- Stable API?
When to publish 1.0.0 ?
Tip: you can always use the labels (1.0.0.beta, 1.0.0.rc.1, 1.0.0.pre, 1.0.0.racecar-1)
Code of conduct
Use Git/Github features
- Readme for documentation
- Push hooks to test
- Issue tracker
- Releases automatically tagged
- Use branches to merge and deploy bugfixes across versions
- Where gems magically come from
- Create an account
Run your own with `gem server`
Note: configure .gemspec to avoid accidental pushes to Rubygems.org
→ bundle exec rake release has_prerequisite 0.0.1 built to pkg/has_prerequisite-0.0.1.gem. Tagged v0.0.1. Pushed git commits and tags. Pushed has_prerequisite 0.0.1 to rubygems.org.
Never forget about the documentation
Open-Source Starter Kit
- CI servers
- Contributing guides
- Set expectations on PR
- Use automated review tools
- Build a team you trust
- Those pull-requests can wait
- Those bugs can wait
- Delegate! Delegate!
- Your free time is YOURS
- You don't owe anyone anything
Free Time management
Don't burn out
Thank you! ❤️💎
Moving code to lib folder:
PR for open-source:
Extracting a Gem from your Rails app Web.com 2018
By Sophie Déziel