SlideShare uma empresa Scribd logo
1 de 40
Baixar para ler offline
Extracting Plugins and
      Gems from Rails
         application

Josh Nichols       technicalpickles.com
Overview

      • The good of plugins
      • Reasons for creating your own
      • Overview of how to extract
      • Tools for extracting
      • Case studies
      • Distributing



Josh Nichols                            technicalpickles.com
Ruby and Rails notoriety...

      • Productivity!
      • Convention over configuration
      • Usually sane defaults
      • 80/20 rule
      • But, can it get any better?




Josh Nichols                           technicalpickles.com
Oh yeah it can.


Josh Nichols               technicalpickles.com
Enter the third party

      • Despite it’s goodness, Rails lacks a lot of functionality
       you’d find in an webapp
       • Pagination, Authentication, Etc
      • Do you really want to have to implement this for every
       app?
      • The Internet is full of clever bastards that have figured
       out good practices for these, and released them as
       plugins and gems



Josh Nichols                                       technicalpickles.com
But guess what?


Josh Nichols          technicalpickles.com
You can be clever
               too!


Josh Nichols          technicalpickles.com
Why make your own plugin?

      • Lets you clean up your code
       • Focus on core business concerns, not the other stuff
      • Re-use within your application
       • DRY
      • Re-use between applications
       • Extract something useful out of one app...
       • ... and have a headstart on the next

Josh Nichols                                   technicalpickles.com
What you’d probably want to
                   extract
      • Model stuff
      • View helper stuff
      • Controller stuff




Josh Nichols                 technicalpickles.com
Overview of extraction

      • Recognize need/want for extracting
      • Make sure the functionality you want to extract has
       good coverage
      • script/generate plugin to start a plugin
      • Move code into the plugin
      • Make your code use the plugin
      • Make sure tests still pass


Josh Nichols                                       technicalpickles.com
Overview of extraction

      • Documentation
       • RDoc, README
      • Clean up plugin layout
      • Test your plugin outside your application
      • Pull out of your app
      • Gem



Josh Nichols                                    technicalpickles.com
Your toolbox:

      • Modules
       • Group related things
       • Can’t create instances of them
       • Can ‘mixin’ to classes
         • ‘include’ a module into a class to add instance
          methods
         • ‘extend’ a module into a class to add class methods


Josh Nichols                                     technicalpickles.com
module ClassMethods
     def count()
       3
     end
   end

   module InstanceMethods
     def yell(message)
       puts quot;#{message.upcase}!!!!quot;
     end
   end

   class Person
     include InstanceMethods
     extend ClassMethods
   end

   Person.count
   @person = Person.new()
   @person.yell quot;I don't know what we're yelling aboutquot;




Josh Nichols                                              technicalpickles.com
Your toolbox: More modules

      • In your module’s methods, you have access to everything
       it was mixed into
      • There’s a callback for when a module is included
       • Gives you access to the class that included the module
       • Use this to include/extend other modules
       • ... or call class methods



Josh Nichols                                   technicalpickles.com
module MyPlugin
     def self.included(base)
       base.class_eval do
         include InstanceMethods
         extend ClassMethods
         validates_presence_of :awesome
       end
     end

     module InstanceMethods
     end

     module ClassMethods
     end
   end




Josh Nichols                              technicalpickles.com
Toolbox: init.rb

      • Rails will automatically load this
      • Add your special sauce here




Josh Nichols                                 technicalpickles.com
Thinking about how the plugin
               would be used
      • Make it always available
       • Include some modules in init.rb
      • Include a module
       • include MyAwesomePlugin
      • Macro method
       • acts_as_awesome



Josh Nichols                               technicalpickles.com
Toolbox: Always include

      • Usually do this in init.rb
      • For Model:
       • ActiveRecord::Base.class_eval { include MyPlugin }
      • For Controller:
       • ActionController::Base.class_eval { include MyPlugin }
      • For View:
       • ActionView::Base.class_eval { include MyPlugin }

Josh Nichols                                   technicalpickles.com
Toolbox: Include a module

        • Tell users to include in their classes

   class User < ActiveRecord::Base
     include Clearance::Models::User
   end

   class UsersController < ApplicationController
     include Clearance::Controllers::UsersController
   end




Josh Nichols                                           technicalpickles.com
Toolbox: Macro method

       • Just a class method
       • Include InstanceMethods
       • Extend ClassMethods
       • Whatever other class stuff you need to do




Josh Nichols                                  technicalpickles.com
module AwesomePlugin
    def self.included(base)
      base.class_eval do
        extend MacroMethods
      end
    end

    module MacroMethods
      def acts_as_awesome()
        include InstanceMethods
        extend ClassMethods
        validates_presence_of :awesome
      end
    end

     module InstanceMethods
     end
     module ClassMethods
     end
   end
   ActiveRecord::Base.class_eval { include AwesomePlugin }

Josh Nichols                                                 technicalpickles.com
Toolbox: Testing view stuff

      • Use ActionView::TestCase
      • Include the module
      • Just call your methods, test the output
      • For HTML stuff, assert_dom_equals




Josh Nichols                                      technicalpickles.com
Tools: Testing model stuff

      • Fake enough of the environment to get by
      • Create ActiveRecord::Base migration to sqlite in
       memory db
      • Migrate a schema




Josh Nichols                                   technicalpickles.com
In test/test_helper.rb


   require 'rubygems'
   require 'active_record'

   RAILS_ROOT = File.dirname(__FILE__)
   require 'logger'
   RAILS_DEFAULT_LOGGER = Logger.new(quot;#{RAILS_ROOT}/test.logquot;)

   require File.dirname(__FILE__) + '/../init'    # Load the plugin
   require File.dirname(__FILE__) + '/post.rb' # Test model

   config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
   ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + quot;/debug.logquot;)
   ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'plugin_test'])

   load(File.dirname(__FILE__) + quot;/schema.rbquot;) if File.exist?(File.dirname(__FILE__) + quot;/
   schema.rbquot;)




Josh Nichols                                                      technicalpickles.com
Toolbox: Other testing

      • Create a rails app within your plugin test layout
       • test/rails_root
      • Update Rakefile to run tests from within the test/
       rails_root




Josh Nichols                                   technicalpickles.com
Rakefile



   test_files_pattern = 'test/rails_root/test/{unit,functional,other}/**/*_test.rb'
   Rake::TestTask.new do |t|
     t.libs << 'lib'
     t.pattern = test_files_pattern
     t.verbose = false
   end




Josh Nichols                                               technicalpickles.com
Case Study: content_given
                 View helpers, always included
      http://github.com/technicalpickles/content_given




Josh Nichols                             technicalpickles.com
Case study: safety_valve
        Controller stuff, opt in by including module
       http://github.com/technicalpickles/safety_valve




Josh Nichols                              technicalpickles.com
Case study: has_markup
                  Model stuff, macro method
       http://github.com/technicalpickles/has_markup




Josh Nichols                            technicalpickles.com
Distributing

      • GitHub
       • Free
       • Easy to collaborate with others
       • script/plugin install git://github.com/technicalpickles/
         ambitious-sphinx.git
       • Also supports generating RubyGems



Josh Nichols                                       technicalpickles.com
Distributing: Gems

      • Create a gemspec for your project
      • Enable RubyGems for your repository
      • http://hasmygembuiltyet.org/




Josh Nichols                                  technicalpickles.com
Gem::Specification.new do |s|
     s.name = %q{jeweler}
     s.version = quot;0.1.1quot;

     s.required_rubygems_version = Gem::Requirement.new(quot;>= 0quot;) if
   s.respond_to? :required_rubygems_version=
     s.authors = [quot;Josh Nicholsquot;, quot;Dan Croakquot;]
     s.date = %q{2008-10-14}
     s.description = %q{Simple and opinionated helper for creating Rubygem projects on
   GitHub}
     s.email = %q{josh@technicalpickles.com}
     s.files = [quot;Rakefilequot;, quot;README.markdownquot;, quot;TODOquot;, quot;VERSION.ymlquot;, quot;lib/jewelerquot;, quot;lib/
   jeweler/active_support.rbquot;, quot;lib/jeweler/bumping.rbquot;, quot;lib/jeweler/errors.rbquot;, quot;lib/
   jeweler/gemspec.rbquot;, quot;lib/jeweler/singleton.rbquot;, quot;lib/jeweler/tasks.rbquot;, quot;lib/jeweler/
   versioning.rbquot;, quot;lib/jeweler.rbquot;, quot;test/jeweler_test.rbquot;, quot;test/test_helper.rbquot;]
     s.homepage = %q{http://github.com/technicalpickles/jeweler}
     s.require_paths = [quot;libquot;]
     s.rubygems_version = %q{1.2.0}
     s.summary = %q{Simple and opinionated helper for creating Rubygem projects on GitHub}
   end




Josh Nichols                                                      technicalpickles.com
$ sudo gem install technicalpickles-jeweler




Josh Nichols                              technicalpickles.com
Distributing: Versioning

      • Update gemspec
      • Update files
      • Push to github
      • Kinda annoying to maintain files
      • Can maintain it with Rake
       • Give Gem::Spec Rake’s FileList to generate list of file
       • Write the spec out

Josh Nichols                                    technicalpickles.com
spec = Gem::Specification.new do |s|
     s.name              = quot;shouldaquot;
     s.version           = Thoughtbot::Shoulda::VERSION
     s.summary           = quot;Making tests easy on the fingers and eyesquot;
     s.homepage          = quot;http://thoughtbot.com/projects/shouldaquot;
     s.rubyforge_project = quot;shouldaquot;

    s.files       = FileList[quot;[A-Z]*quot;, quot;{bin,lib,rails,test}/**/*quot;]
    s.executables = s.files.grep(/^bin/) { |f| File.basename(f) }

    s.has_rdoc         = true
    s.extra_rdoc_files = [quot;README.rdocquot;, quot;CONTRIBUTION_GUIDELINES.rdocquot;]
    s.rdoc_options     = [quot;--line-numbersquot;, quot;--inline-sourcequot;, quot;--mainquot;, quot;README.rdocquot;]

    s.authors = [quot;Tammer Salehquot;]
    s.email   = quot;tsaleh@thoughtbot.comquot;

     s.add_dependency quot;activesupportquot;, quot;>= 2.0.0quot;
   end

   desc quot;Generate a gemspec file for GitHubquot;
   task :gemspec do
     File.open(quot;#{spec.name}.gemspecquot;, 'w') do |f|
       f.write spec.to_ruby
     end
   end




Josh Nichols                                                      technicalpickles.com
Distributing: Versioning

      • Update Rakefile’s Gem::Specification’s version
      • Run ‘rake gemspec’
      • Commit and push
      • Easy to forget to keep Rakefile and gemspec in sync
      • Can it get easier?




Josh Nichols                                  technicalpickles.com
Jeweler
                     Craft the perfect gem
          http://github.com/technicalpickles/jeweler




Josh Nichols                              technicalpickles.com
Jeweler

      • Rake tasks for creating and validating gemspec
      • Rake tasks for bumping the version
       • Will automatically write out updated gemspec




Josh Nichols                                  technicalpickles.com
$ rake version
   (in /Users/nichoj/Projects/jeweler)
   Current version: 0.1.1

   $ rake gemspec
   (in /Users/nichoj/Projects/jeweler)
   Generated: jeweler.gemspec
   jeweler.gemspec is valid.

   $ rake version:bump:minor
   (in /Users/nichoj/Projects/jeweler)
   Current version: 0.1.1
   Wrote to VERSION.yml: 0.2.0
   Generated: jeweler.gemspec

   $ rake version:bump:patch
   (in /Users/nichoj/Projects/jeweler)
   Current version: 0.2.0
   Wrote to VERSION.yml: 0.2.1
   Generated: jeweler.gemspec

   $ rake version:bump:major
   (in /Users/nichoj/Projects/jeweler)
   Current version: 0.2.1
   Wrote to VERSION.yml: 1.0.0
   Generated: jeweler.gemspec


Josh Nichols                             technicalpickles.com
Rakefile
  begin
    require 'rubygems'
    require 'jeweler'
    gemspec = Gem::Specification.new do |s|
      s.name = quot;has_markupquot;
      s.summary = quot;Manage markup close to home... right in the model! Caching, validation,
  etcquot;
      s.email = quot;josh@technicalpickles.comquot;
      s.homepage = quot;http://github.com/technicalpickles/has_markupquot;
      s.description = quot;Manage markup close to home... right in the model! Caching,
  validation, etcquot;
      s.authors = [quot;Josh Nicholsquot;]
      s.files = FileList[quot;[A-Z]*.*quot;, quot;{generators,lib,test,spec}/**/*quot;]
    end
    Jeweler.craft(gemspec)
  rescue LoadError
    puts quot;Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler
  -s http://gems.github.comquot;
  end




Josh Nichols                                                     technicalpickles.com

Mais conteúdo relacionado

Mais procurados

Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...
Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...
Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...Alan Richardson
 
Single Sourcing RAP and RCP - Desktop and web clients from a single code base
Single Sourcing RAP and RCP - Desktop and web clients from a single code baseSingle Sourcing RAP and RCP - Desktop and web clients from a single code base
Single Sourcing RAP and RCP - Desktop and web clients from a single code baseRalf Sternberg
 
Riga Dev Day - Automated Android Continuous Integration
Riga Dev Day - Automated Android Continuous IntegrationRiga Dev Day - Automated Android Continuous Integration
Riga Dev Day - Automated Android Continuous IntegrationNicolas Fränkel
 
QA Fest 2018. Adam Stasiak. React Native is Coming – the story of hybrid mobi...
QA Fest 2018. Adam Stasiak. React Native is Coming – the story of hybrid mobi...QA Fest 2018. Adam Stasiak. React Native is Coming – the story of hybrid mobi...
QA Fest 2018. Adam Stasiak. React Native is Coming – the story of hybrid mobi...QAFest
 
JavaOne - The JavaFX Community and Ecosystem
JavaOne - The JavaFX Community and EcosystemJavaOne - The JavaFX Community and Ecosystem
JavaOne - The JavaFX Community and EcosystemAlexander Casall
 
Building a JavaScript Library
Building a JavaScript LibraryBuilding a JavaScript Library
Building a JavaScript Libraryjeresig
 
2014 Joker - Integration Testing from the Trenches
2014 Joker - Integration Testing from the Trenches2014 Joker - Integration Testing from the Trenches
2014 Joker - Integration Testing from the TrenchesNicolas Fränkel
 
Performance, Games, and Distributed Testing in JavaScript
Performance, Games, and Distributed Testing in JavaScriptPerformance, Games, and Distributed Testing in JavaScript
Performance, Games, and Distributed Testing in JavaScriptjeresig
 
Turbogears Presentation
Turbogears PresentationTurbogears Presentation
Turbogears Presentationdidip
 
Abstraction Layers Test Management Summit Faciliated Session 2014
Abstraction Layers Test Management Summit Faciliated Session 2014Abstraction Layers Test Management Summit Faciliated Session 2014
Abstraction Layers Test Management Summit Faciliated Session 2014Alan Richardson
 
JavaFX8 TestFX - CDI
JavaFX8   TestFX - CDIJavaFX8   TestFX - CDI
JavaFX8 TestFX - CDISven Ruppert
 
Improving your Gradle builds
Improving your Gradle buildsImproving your Gradle builds
Improving your Gradle buildsPeter Ledbrook
 
Selenium Clinic Eurostar 2012 WebDriver Tutorial
Selenium Clinic Eurostar 2012 WebDriver TutorialSelenium Clinic Eurostar 2012 WebDriver Tutorial
Selenium Clinic Eurostar 2012 WebDriver TutorialAlan Richardson
 
Why your build matters
Why your build mattersWhy your build matters
Why your build mattersPeter Ledbrook
 
What the heck went wrong?
What the heck went wrong?What the heck went wrong?
What the heck went wrong?Andy McKay
 
Efficient JavaScript Unit Testing, May 2012
Efficient JavaScript Unit Testing, May 2012Efficient JavaScript Unit Testing, May 2012
Efficient JavaScript Unit Testing, May 2012Hazem Saleh
 

Mais procurados (20)

Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...
Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...
Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...
 
Unit testing - A&BP CC
Unit testing - A&BP CCUnit testing - A&BP CC
Unit testing - A&BP CC
 
Single Sourcing RAP and RCP - Desktop and web clients from a single code base
Single Sourcing RAP and RCP - Desktop and web clients from a single code baseSingle Sourcing RAP and RCP - Desktop and web clients from a single code base
Single Sourcing RAP and RCP - Desktop and web clients from a single code base
 
Riga Dev Day - Automated Android Continuous Integration
Riga Dev Day - Automated Android Continuous IntegrationRiga Dev Day - Automated Android Continuous Integration
Riga Dev Day - Automated Android Continuous Integration
 
QA Fest 2018. Adam Stasiak. React Native is Coming – the story of hybrid mobi...
QA Fest 2018. Adam Stasiak. React Native is Coming – the story of hybrid mobi...QA Fest 2018. Adam Stasiak. React Native is Coming – the story of hybrid mobi...
QA Fest 2018. Adam Stasiak. React Native is Coming – the story of hybrid mobi...
 
JavaOne - The JavaFX Community and Ecosystem
JavaOne - The JavaFX Community and EcosystemJavaOne - The JavaFX Community and Ecosystem
JavaOne - The JavaFX Community and Ecosystem
 
Building a JavaScript Library
Building a JavaScript LibraryBuilding a JavaScript Library
Building a JavaScript Library
 
2014 Joker - Integration Testing from the Trenches
2014 Joker - Integration Testing from the Trenches2014 Joker - Integration Testing from the Trenches
2014 Joker - Integration Testing from the Trenches
 
Performance, Games, and Distributed Testing in JavaScript
Performance, Games, and Distributed Testing in JavaScriptPerformance, Games, and Distributed Testing in JavaScript
Performance, Games, and Distributed Testing in JavaScript
 
Turbogears Presentation
Turbogears PresentationTurbogears Presentation
Turbogears Presentation
 
Abstraction Layers Test Management Summit Faciliated Session 2014
Abstraction Layers Test Management Summit Faciliated Session 2014Abstraction Layers Test Management Summit Faciliated Session 2014
Abstraction Layers Test Management Summit Faciliated Session 2014
 
JavaFX8 TestFX - CDI
JavaFX8   TestFX - CDIJavaFX8   TestFX - CDI
JavaFX8 TestFX - CDI
 
Improving your Gradle builds
Improving your Gradle buildsImproving your Gradle builds
Improving your Gradle builds
 
Why Gradle?
Why Gradle?Why Gradle?
Why Gradle?
 
Selenium Clinic Eurostar 2012 WebDriver Tutorial
Selenium Clinic Eurostar 2012 WebDriver TutorialSelenium Clinic Eurostar 2012 WebDriver Tutorial
Selenium Clinic Eurostar 2012 WebDriver Tutorial
 
Your code are my tests
Your code are my testsYour code are my tests
Your code are my tests
 
Why your build matters
Why your build mattersWhy your build matters
Why your build matters
 
What the heck went wrong?
What the heck went wrong?What the heck went wrong?
What the heck went wrong?
 
Efficient JavaScript Unit Testing, May 2012
Efficient JavaScript Unit Testing, May 2012Efficient JavaScript Unit Testing, May 2012
Efficient JavaScript Unit Testing, May 2012
 
Easy automation.py
Easy automation.pyEasy automation.py
Easy automation.py
 

Semelhante a Extracting Plugins And Gems From Rails Apps

Doctrine Php Object Relational Mapper
Doctrine Php Object Relational MapperDoctrine Php Object Relational Mapper
Doctrine Php Object Relational MapperJonathan Wage
 
Drupal Deployment
Drupal DeploymentDrupal Deployment
Drupal DeploymentJeff Eaton
 
Test Driven Development Introduction
Test Driven Development IntroductionTest Driven Development Introduction
Test Driven Development IntroductionNguyen Hai
 
Drupal Recipes: Building Image Galleries with jQuery and Flickr
Drupal Recipes: Building Image Galleries with jQuery and FlickrDrupal Recipes: Building Image Galleries with jQuery and Flickr
Drupal Recipes: Building Image Galleries with jQuery and FlickrBen Shell
 
Developing for Plone using ArchGenXML / ArgoUML
Developing for Plone using ArchGenXML / ArgoUMLDeveloping for Plone using ArchGenXML / ArgoUML
Developing for Plone using ArchGenXML / ArgoUMLJazkarta, Inc.
 
Static Analysis Techniques For Testing Application Security - Houston Tech Fest
Static Analysis Techniques For Testing Application Security - Houston Tech FestStatic Analysis Techniques For Testing Application Security - Houston Tech Fest
Static Analysis Techniques For Testing Application Security - Houston Tech FestDenim Group
 
Creating a Smooth Development Workflow for High-Quality Modular Open-Source P...
Creating a Smooth Development Workflow for High-Quality Modular Open-Source P...Creating a Smooth Development Workflow for High-Quality Modular Open-Source P...
Creating a Smooth Development Workflow for High-Quality Modular Open-Source P...Pantheon
 
symfony: An Open-Source Framework for Professionals (Dutch Php Conference 2008)
symfony: An Open-Source Framework for Professionals (Dutch Php Conference 2008)symfony: An Open-Source Framework for Professionals (Dutch Php Conference 2008)
symfony: An Open-Source Framework for Professionals (Dutch Php Conference 2008)Fabien Potencier
 
Create ReactJS Component & publish as npm package
Create ReactJS Component & publish as npm packageCreate ReactJS Component & publish as npm package
Create ReactJS Component & publish as npm packageAndrii Lundiak
 
Staging Drupal 8 31 09 1 3
Staging Drupal 8 31 09 1 3Staging Drupal 8 31 09 1 3
Staging Drupal 8 31 09 1 3Drupalcon Paris
 
Streamlining Your Applications with Web Frameworks
Streamlining Your Applications with Web FrameworksStreamlining Your Applications with Web Frameworks
Streamlining Your Applications with Web Frameworksguestf7bc30
 
Peer Code Review: In a Nutshell and The Tantric Team: Getting Your Automated ...
Peer Code Review: In a Nutshell and The Tantric Team: Getting Your Automated ...Peer Code Review: In a Nutshell and The Tantric Team: Getting Your Automated ...
Peer Code Review: In a Nutshell and The Tantric Team: Getting Your Automated ...Atlassian
 
Cypress report
Cypress reportCypress report
Cypress reportAdarsh
 
2014 11 20 Drupal 7 -> 8 test migratie
2014 11 20 Drupal 7 -> 8 test migratie2014 11 20 Drupal 7 -> 8 test migratie
2014 11 20 Drupal 7 -> 8 test migratiehcderaad
 
Intro To Django
Intro To DjangoIntro To Django
Intro To DjangoUdi Bauman
 
Ruby on Rails 101 - Presentation Slides for a Five Day Introductory Course
Ruby on Rails 101 - Presentation Slides for a Five Day Introductory CourseRuby on Rails 101 - Presentation Slides for a Five Day Introductory Course
Ruby on Rails 101 - Presentation Slides for a Five Day Introductory Coursepeter_marklund
 

Semelhante a Extracting Plugins And Gems From Rails Apps (20)

Doctrine Php Object Relational Mapper
Doctrine Php Object Relational MapperDoctrine Php Object Relational Mapper
Doctrine Php Object Relational Mapper
 
Tdd
TddTdd
Tdd
 
Drupal Deployment
Drupal DeploymentDrupal Deployment
Drupal Deployment
 
Test Driven Development Introduction
Test Driven Development IntroductionTest Driven Development Introduction
Test Driven Development Introduction
 
Drupal Recipes: Building Image Galleries with jQuery and Flickr
Drupal Recipes: Building Image Galleries with jQuery and FlickrDrupal Recipes: Building Image Galleries with jQuery and Flickr
Drupal Recipes: Building Image Galleries with jQuery and Flickr
 
Continuous feature-development
Continuous feature-developmentContinuous feature-development
Continuous feature-development
 
Developing for Plone using ArchGenXML / ArgoUML
Developing for Plone using ArchGenXML / ArgoUMLDeveloping for Plone using ArchGenXML / ArgoUML
Developing for Plone using ArchGenXML / ArgoUML
 
Static Analysis Techniques For Testing Application Security - Houston Tech Fest
Static Analysis Techniques For Testing Application Security - Houston Tech FestStatic Analysis Techniques For Testing Application Security - Houston Tech Fest
Static Analysis Techniques For Testing Application Security - Houston Tech Fest
 
Creating a Smooth Development Workflow for High-Quality Modular Open-Source P...
Creating a Smooth Development Workflow for High-Quality Modular Open-Source P...Creating a Smooth Development Workflow for High-Quality Modular Open-Source P...
Creating a Smooth Development Workflow for High-Quality Modular Open-Source P...
 
symfony: An Open-Source Framework for Professionals (Dutch Php Conference 2008)
symfony: An Open-Source Framework for Professionals (Dutch Php Conference 2008)symfony: An Open-Source Framework for Professionals (Dutch Php Conference 2008)
symfony: An Open-Source Framework for Professionals (Dutch Php Conference 2008)
 
Create ReactJS Component & publish as npm package
Create ReactJS Component & publish as npm packageCreate ReactJS Component & publish as npm package
Create ReactJS Component & publish as npm package
 
Staging Drupal 8 31 09 1 3
Staging Drupal 8 31 09 1 3Staging Drupal 8 31 09 1 3
Staging Drupal 8 31 09 1 3
 
Js tacktalk team dev js testing performance
Js tacktalk team dev js testing performanceJs tacktalk team dev js testing performance
Js tacktalk team dev js testing performance
 
Streamlining Your Applications with Web Frameworks
Streamlining Your Applications with Web FrameworksStreamlining Your Applications with Web Frameworks
Streamlining Your Applications with Web Frameworks
 
Peer Code Review: In a Nutshell and The Tantric Team: Getting Your Automated ...
Peer Code Review: In a Nutshell and The Tantric Team: Getting Your Automated ...Peer Code Review: In a Nutshell and The Tantric Team: Getting Your Automated ...
Peer Code Review: In a Nutshell and The Tantric Team: Getting Your Automated ...
 
React nativebeginner1
React nativebeginner1React nativebeginner1
React nativebeginner1
 
Cypress report
Cypress reportCypress report
Cypress report
 
2014 11 20 Drupal 7 -> 8 test migratie
2014 11 20 Drupal 7 -> 8 test migratie2014 11 20 Drupal 7 -> 8 test migratie
2014 11 20 Drupal 7 -> 8 test migratie
 
Intro To Django
Intro To DjangoIntro To Django
Intro To Django
 
Ruby on Rails 101 - Presentation Slides for a Five Day Introductory Course
Ruby on Rails 101 - Presentation Slides for a Five Day Introductory CourseRuby on Rails 101 - Presentation Slides for a Five Day Introductory Course
Ruby on Rails 101 - Presentation Slides for a Five Day Introductory Course
 

Último

"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024Lonnie McRorey
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 
Ryan Mahoney - Will Artificial Intelligence Replace Real Estate Agents
Ryan Mahoney - Will Artificial Intelligence Replace Real Estate AgentsRyan Mahoney - Will Artificial Intelligence Replace Real Estate Agents
Ryan Mahoney - Will Artificial Intelligence Replace Real Estate AgentsRyan Mahoney
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersNicole Novielli
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersRaghuram Pandurangan
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxLoriGlavin3
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Visualising and forecasting stocks using Dash
Visualising and forecasting stocks using DashVisualising and forecasting stocks using Dash
Visualising and forecasting stocks using Dashnarutouzumaki53779
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfLoriGlavin3
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfMounikaPolabathina
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 

Último (20)

"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 
Ryan Mahoney - Will Artificial Intelligence Replace Real Estate Agents
Ryan Mahoney - Will Artificial Intelligence Replace Real Estate AgentsRyan Mahoney - Will Artificial Intelligence Replace Real Estate Agents
Ryan Mahoney - Will Artificial Intelligence Replace Real Estate Agents
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software Developers
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information Developers
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Visualising and forecasting stocks using Dash
Visualising and forecasting stocks using DashVisualising and forecasting stocks using Dash
Visualising and forecasting stocks using Dash
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdf
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdf
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 

Extracting Plugins And Gems From Rails Apps

  • 1. Extracting Plugins and Gems from Rails application Josh Nichols technicalpickles.com
  • 2. Overview • The good of plugins • Reasons for creating your own • Overview of how to extract • Tools for extracting • Case studies • Distributing Josh Nichols technicalpickles.com
  • 3. Ruby and Rails notoriety... • Productivity! • Convention over configuration • Usually sane defaults • 80/20 rule • But, can it get any better? Josh Nichols technicalpickles.com
  • 4. Oh yeah it can. Josh Nichols technicalpickles.com
  • 5. Enter the third party • Despite it’s goodness, Rails lacks a lot of functionality you’d find in an webapp • Pagination, Authentication, Etc • Do you really want to have to implement this for every app? • The Internet is full of clever bastards that have figured out good practices for these, and released them as plugins and gems Josh Nichols technicalpickles.com
  • 6. But guess what? Josh Nichols technicalpickles.com
  • 7. You can be clever too! Josh Nichols technicalpickles.com
  • 8. Why make your own plugin? • Lets you clean up your code • Focus on core business concerns, not the other stuff • Re-use within your application • DRY • Re-use between applications • Extract something useful out of one app... • ... and have a headstart on the next Josh Nichols technicalpickles.com
  • 9. What you’d probably want to extract • Model stuff • View helper stuff • Controller stuff Josh Nichols technicalpickles.com
  • 10. Overview of extraction • Recognize need/want for extracting • Make sure the functionality you want to extract has good coverage • script/generate plugin to start a plugin • Move code into the plugin • Make your code use the plugin • Make sure tests still pass Josh Nichols technicalpickles.com
  • 11. Overview of extraction • Documentation • RDoc, README • Clean up plugin layout • Test your plugin outside your application • Pull out of your app • Gem Josh Nichols technicalpickles.com
  • 12. Your toolbox: • Modules • Group related things • Can’t create instances of them • Can ‘mixin’ to classes • ‘include’ a module into a class to add instance methods • ‘extend’ a module into a class to add class methods Josh Nichols technicalpickles.com
  • 13. module ClassMethods def count() 3 end end module InstanceMethods def yell(message) puts quot;#{message.upcase}!!!!quot; end end class Person include InstanceMethods extend ClassMethods end Person.count @person = Person.new() @person.yell quot;I don't know what we're yelling aboutquot; Josh Nichols technicalpickles.com
  • 14. Your toolbox: More modules • In your module’s methods, you have access to everything it was mixed into • There’s a callback for when a module is included • Gives you access to the class that included the module • Use this to include/extend other modules • ... or call class methods Josh Nichols technicalpickles.com
  • 15. module MyPlugin def self.included(base) base.class_eval do include InstanceMethods extend ClassMethods validates_presence_of :awesome end end module InstanceMethods end module ClassMethods end end Josh Nichols technicalpickles.com
  • 16. Toolbox: init.rb • Rails will automatically load this • Add your special sauce here Josh Nichols technicalpickles.com
  • 17. Thinking about how the plugin would be used • Make it always available • Include some modules in init.rb • Include a module • include MyAwesomePlugin • Macro method • acts_as_awesome Josh Nichols technicalpickles.com
  • 18. Toolbox: Always include • Usually do this in init.rb • For Model: • ActiveRecord::Base.class_eval { include MyPlugin } • For Controller: • ActionController::Base.class_eval { include MyPlugin } • For View: • ActionView::Base.class_eval { include MyPlugin } Josh Nichols technicalpickles.com
  • 19. Toolbox: Include a module • Tell users to include in their classes class User < ActiveRecord::Base include Clearance::Models::User end class UsersController < ApplicationController include Clearance::Controllers::UsersController end Josh Nichols technicalpickles.com
  • 20. Toolbox: Macro method • Just a class method • Include InstanceMethods • Extend ClassMethods • Whatever other class stuff you need to do Josh Nichols technicalpickles.com
  • 21. module AwesomePlugin def self.included(base) base.class_eval do extend MacroMethods end end module MacroMethods def acts_as_awesome() include InstanceMethods extend ClassMethods validates_presence_of :awesome end end module InstanceMethods end module ClassMethods end end ActiveRecord::Base.class_eval { include AwesomePlugin } Josh Nichols technicalpickles.com
  • 22. Toolbox: Testing view stuff • Use ActionView::TestCase • Include the module • Just call your methods, test the output • For HTML stuff, assert_dom_equals Josh Nichols technicalpickles.com
  • 23. Tools: Testing model stuff • Fake enough of the environment to get by • Create ActiveRecord::Base migration to sqlite in memory db • Migrate a schema Josh Nichols technicalpickles.com
  • 24. In test/test_helper.rb require 'rubygems' require 'active_record' RAILS_ROOT = File.dirname(__FILE__) require 'logger' RAILS_DEFAULT_LOGGER = Logger.new(quot;#{RAILS_ROOT}/test.logquot;) require File.dirname(__FILE__) + '/../init' # Load the plugin require File.dirname(__FILE__) + '/post.rb' # Test model config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml')) ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + quot;/debug.logquot;) ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'plugin_test']) load(File.dirname(__FILE__) + quot;/schema.rbquot;) if File.exist?(File.dirname(__FILE__) + quot;/ schema.rbquot;) Josh Nichols technicalpickles.com
  • 25. Toolbox: Other testing • Create a rails app within your plugin test layout • test/rails_root • Update Rakefile to run tests from within the test/ rails_root Josh Nichols technicalpickles.com
  • 26. Rakefile test_files_pattern = 'test/rails_root/test/{unit,functional,other}/**/*_test.rb' Rake::TestTask.new do |t| t.libs << 'lib' t.pattern = test_files_pattern t.verbose = false end Josh Nichols technicalpickles.com
  • 27. Case Study: content_given View helpers, always included http://github.com/technicalpickles/content_given Josh Nichols technicalpickles.com
  • 28. Case study: safety_valve Controller stuff, opt in by including module http://github.com/technicalpickles/safety_valve Josh Nichols technicalpickles.com
  • 29. Case study: has_markup Model stuff, macro method http://github.com/technicalpickles/has_markup Josh Nichols technicalpickles.com
  • 30. Distributing • GitHub • Free • Easy to collaborate with others • script/plugin install git://github.com/technicalpickles/ ambitious-sphinx.git • Also supports generating RubyGems Josh Nichols technicalpickles.com
  • 31. Distributing: Gems • Create a gemspec for your project • Enable RubyGems for your repository • http://hasmygembuiltyet.org/ Josh Nichols technicalpickles.com
  • 32. Gem::Specification.new do |s| s.name = %q{jeweler} s.version = quot;0.1.1quot; s.required_rubygems_version = Gem::Requirement.new(quot;>= 0quot;) if s.respond_to? :required_rubygems_version= s.authors = [quot;Josh Nicholsquot;, quot;Dan Croakquot;] s.date = %q{2008-10-14} s.description = %q{Simple and opinionated helper for creating Rubygem projects on GitHub} s.email = %q{josh@technicalpickles.com} s.files = [quot;Rakefilequot;, quot;README.markdownquot;, quot;TODOquot;, quot;VERSION.ymlquot;, quot;lib/jewelerquot;, quot;lib/ jeweler/active_support.rbquot;, quot;lib/jeweler/bumping.rbquot;, quot;lib/jeweler/errors.rbquot;, quot;lib/ jeweler/gemspec.rbquot;, quot;lib/jeweler/singleton.rbquot;, quot;lib/jeweler/tasks.rbquot;, quot;lib/jeweler/ versioning.rbquot;, quot;lib/jeweler.rbquot;, quot;test/jeweler_test.rbquot;, quot;test/test_helper.rbquot;] s.homepage = %q{http://github.com/technicalpickles/jeweler} s.require_paths = [quot;libquot;] s.rubygems_version = %q{1.2.0} s.summary = %q{Simple and opinionated helper for creating Rubygem projects on GitHub} end Josh Nichols technicalpickles.com
  • 33. $ sudo gem install technicalpickles-jeweler Josh Nichols technicalpickles.com
  • 34. Distributing: Versioning • Update gemspec • Update files • Push to github • Kinda annoying to maintain files • Can maintain it with Rake • Give Gem::Spec Rake’s FileList to generate list of file • Write the spec out Josh Nichols technicalpickles.com
  • 35. spec = Gem::Specification.new do |s| s.name = quot;shouldaquot; s.version = Thoughtbot::Shoulda::VERSION s.summary = quot;Making tests easy on the fingers and eyesquot; s.homepage = quot;http://thoughtbot.com/projects/shouldaquot; s.rubyforge_project = quot;shouldaquot; s.files = FileList[quot;[A-Z]*quot;, quot;{bin,lib,rails,test}/**/*quot;] s.executables = s.files.grep(/^bin/) { |f| File.basename(f) } s.has_rdoc = true s.extra_rdoc_files = [quot;README.rdocquot;, quot;CONTRIBUTION_GUIDELINES.rdocquot;] s.rdoc_options = [quot;--line-numbersquot;, quot;--inline-sourcequot;, quot;--mainquot;, quot;README.rdocquot;] s.authors = [quot;Tammer Salehquot;] s.email = quot;tsaleh@thoughtbot.comquot; s.add_dependency quot;activesupportquot;, quot;>= 2.0.0quot; end desc quot;Generate a gemspec file for GitHubquot; task :gemspec do File.open(quot;#{spec.name}.gemspecquot;, 'w') do |f| f.write spec.to_ruby end end Josh Nichols technicalpickles.com
  • 36. Distributing: Versioning • Update Rakefile’s Gem::Specification’s version • Run ‘rake gemspec’ • Commit and push • Easy to forget to keep Rakefile and gemspec in sync • Can it get easier? Josh Nichols technicalpickles.com
  • 37. Jeweler Craft the perfect gem http://github.com/technicalpickles/jeweler Josh Nichols technicalpickles.com
  • 38. Jeweler • Rake tasks for creating and validating gemspec • Rake tasks for bumping the version • Will automatically write out updated gemspec Josh Nichols technicalpickles.com
  • 39. $ rake version (in /Users/nichoj/Projects/jeweler) Current version: 0.1.1 $ rake gemspec (in /Users/nichoj/Projects/jeweler) Generated: jeweler.gemspec jeweler.gemspec is valid. $ rake version:bump:minor (in /Users/nichoj/Projects/jeweler) Current version: 0.1.1 Wrote to VERSION.yml: 0.2.0 Generated: jeweler.gemspec $ rake version:bump:patch (in /Users/nichoj/Projects/jeweler) Current version: 0.2.0 Wrote to VERSION.yml: 0.2.1 Generated: jeweler.gemspec $ rake version:bump:major (in /Users/nichoj/Projects/jeweler) Current version: 0.2.1 Wrote to VERSION.yml: 1.0.0 Generated: jeweler.gemspec Josh Nichols technicalpickles.com
  • 40. Rakefile begin require 'rubygems' require 'jeweler' gemspec = Gem::Specification.new do |s| s.name = quot;has_markupquot; s.summary = quot;Manage markup close to home... right in the model! Caching, validation, etcquot; s.email = quot;josh@technicalpickles.comquot; s.homepage = quot;http://github.com/technicalpickles/has_markupquot; s.description = quot;Manage markup close to home... right in the model! Caching, validation, etcquot; s.authors = [quot;Josh Nicholsquot;] s.files = FileList[quot;[A-Z]*.*quot;, quot;{generators,lib,test,spec}/**/*quot;] end Jeweler.craft(gemspec) rescue LoadError puts quot;Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.comquot; end Josh Nichols technicalpickles.com