Skip to content

Instantly share code, notes, and snippets.

@david-crowell
Last active February 24, 2017 15:54
Show Gist options
  • Save david-crowell/7fd7ab95516ae7d41ce4 to your computer and use it in GitHub Desktop.
Save david-crowell/7fd7ab95516ae7d41ce4 to your computer and use it in GitHub Desktop.
Gems for gem lecture

50 Gems


active_model_serializers

https://github.com/rails-api/active_model_serializers

  • Customize fields to be serialized to json
class SomeResource < ActiveRecord::Base
  # columns: title, body
end

class SomeSerializer < ActiveModel::Serializer
  attribute :title, key: :name
  attributes :body
end

acts-as-taggable-on

https://github.com/mbleigh/acts-as-taggable-on

  • Powerful support for tags
class User < ActiveRecord::Base
  acts_as_taggable_on :skills, :interests
end

@user.skill_list = "joking, clowning, boxing"
@user.skill_list.add("coding")
@user.skill_list
# => ["joking", "clowning", "boxing", "coding"]
User.tagged_with("awesome")
User.tagged_with(["awesome", "cool"], :any => true)

acts_as_follower

https://github.com/tcocca/acts_as_follower

  • For the next "Facebook For X", implement a follower model
class Book < ActiveRecord::Base
  ...
  acts_as_followable
  ...
end

class User < ActiveRecord::Base
  ...
  acts_as_follower
  ...
end
user.follow(book)
user.stop_following(book)
user.following?(book)
user.following_by_type('Book')
book.followers
book.block(user)

airbrake

https://github.com/airbrake/airbrake

  • Error tracking and notification emails
rails g airbrake PROJECT_ID PROJECT_KEY
[mobius-backend-staging] Development no implicit conversion of Date into String
---
Error Message:
no implicit conversion of Date into String

Where:
maker_spaces#show
[PROJECT_ROOT]/app/controllers/admin/maker_spaces_controller.rb, line 80

analytics-ruby

https://github.com/segmentio/analytics-ruby

  • Connects to segment.io, an analytics data broker that can talk to different tools
# Identify the user for the people section
analytics.identify(
    {
        user_id: user.id,
        traits: {
            email: user.email,
            first_name: user.first_name,
            last_name: user.last_name
        }
    }
)

# Track a user event
analytics.track(
    {
        user_id: user.id,
        event: 'Created Account'
    }
)

awesome_print

  • Print usefully
puts MakerSpace.first

#<MakerSpace:0x007fae73860118>
awesome_print MakerSpace.first

#<MakerSpace:0x007fae707d32e8> {
                     :id => "110d05bc-c7ca-4481-b342-f0ede9ad35a0",
             :created_at => Thu, 07 Jan 2016 05:48:33 EST -05:00,
             :updated_at => Thu, 07 Jan 2016 05:48:33 EST -05:00,
                   :name => "Pappalardo Lab",
               :location => "3-069",
               :latitude => 42.35898,
              :longitude => -71.092373,
                  :image => "http://i.imgur.com/fSwF1aQ.jpg",
    :access_requirements => "You must be enrolled in 2.007",
                :address => "33 Massachusetts Ave",
                   :city => "Cambridge",
                  :state => "MA",
            :postal_code => "02139",
                  :phone => "6175551234",
            :description => "The Pappalardo Lab"
}

aws-sdk

https://github.com/aws/aws-sdk-ruby

  • Wide-ranging gem for connecting with Amazon Web Services... services
s3 = Aws::S3::Client.new
resp = s3.list_buckets
resp.buckets.map(&:name)
#=> ["bucket-1", "bucket-2", ...]

bullet

https://github.com/flyerhzm/bullet

  • Helps identify N+1 queries and unused eager loading
clients = Client.limit(10)
 
clients.each do |client|
  puts client.address.postcode
end

vs.

clients = Client.includes(:address).limit(10)
 
clients.each do |client|
  puts client.address.postcode
end

consistency_fail

https://github.com/trptcolin/consistency_fail

  • Ensure that your db uniqueness restrictions match your validates_uniqueness claims
$ consistency_fail

There are calls to validates_uniqueness_of that aren't backed by unique indexes.
--------------------------------------------------------------------------------
Model             Table Columns
--------------------------------------------------------------------------------
User              users (authentication_token)
--------------------------------------------------------------------------------


There are calls to has_one that aren't backed by unique indexes.
--------------------------------------------------------------------------------
Model  Table Columns
--------------------------------------------------------------------------------
User   kerberos_identities (user_id)
--------------------------------------------------------------------------------

Hooray! All calls to has_one_with_polymorphic are correctly backed by a unique index.

counter_culture

https://github.com/magnusvk/counter_culture

  • Utilities for storing counts of children in a column on the parent
add_column :categories, :products_count, :integer, :null => false, :default => 0

class Product < ActiveRecord::Base
  belongs_to :category
  counter_culture :category
end

class Category < ActiveRecord::Base
  has_many :products
end

Category.first.products_count

dalli

https://github.com/petergoldstein/dalli

  • Connect rails to memcached

database_cleaner

  • Cleans databases (generally for testing (generally by people not at Intrepid))
RSpec.configure do |config|

  config.before(:suite) do
    DatabaseCleaner.strategy = :transaction
    DatabaseCleaner.clean_with(:truncation)
  end

  config.around(:each) do |example|
    DatabaseCleaner.cleaning do
      example.run
    end
  end

end

draper

https://github.com/drapergem/draper

  • Decorators for presentation logic

BAD:

# app/helpers/articles_helper.rb
def publication_status(article)
  if article.published?
    "Published at #{article.published_at.strftime('%A, %B %e')}"
  else
    "Unpublished"
  end
end

GOOD:

# app/decorators/article_decorator.rb
class ArticleDecorator < Draper::Decorator
  delegate_all

  def publication_status
    if published?
      "Published at #{published_at}"
    else
      "Unpublished"
    end
  end

  def published_at
    object.published_at.strftime("%A, %B %e")
  end
end

@article = Article.first.decorate

factory_girl_rails

https://github.com/thoughtbot/factory_girl_rails

  • Factories for creating mock objects for testing
FactoryGirl.define do
  factory :incident do
    user
    author
    maker_space
    sequence(:name) { |n| "Cut off #{n} fingers" }
    notes "This totally happened"
    severity "severe"
  end
end
incident = create(:incident)

firebase

https://github.com/oscardelben/firebase-ruby

  • Hosting and db
  • Sort of heroku
  • Claims to focus on "real-time" apps

foreman

https://github.com/ddollar/foreman

  • For running and managing Procfile-based applications
  • For a heroku-hosted project

foreman start

has given way to

heroku local


groupdate

https://github.com/ankane/groupdate

  • Query records and group by date
User.group_by_day(:created_at).count

Groupdate.time_zone = "Pacific Time (US & Canada)"

User.group_by_week(:created_at, time_zone: "Pacific Time (US & Canada)").count
# {
#   2013-03-10 00:00:00 PST => 70,
#   2013-03-17 00:00:00 PDT => 54,
#   2013-03-24 00:00:00 PDT => 80
# }

hashids

https://github.com/peterhellberg/hashids.rb

  • Alternative to create_table :users, id: :uuid

httparty

https://github.com/jnunemaker/httparty

  • Convenience methods arround http requests
class StackExchange
  include HTTParty
  base_uri 'api.stackexchange.com'

  def initialize(service, page)
    @options = { query: {site: service, page: page} }
  end

  def questions
    self.class.get("/2.2/questions", @options)
  end

  def users
    self.class.get("/2.2/users", @options)
  end
end

stack_exchange = StackExchange.new("stackoverflow", 1)
puts stack_exchange.questions
puts stack_exchange.users

json-schema

https://github.com/ruby-json-schema/json-schema

  • For validating json scheme
  • Ended up being to heavy weight to use much
schema = {
  "type" => "object",
  "required" => ["a"],
  "properties" => {
    "a" => {"type" => "integer"}
  }
}

data = {
  "a" => 5
}

JSON::Validator.validate(schema, data)

kaminari

https://github.com/amatsuda/kaminari

  • Support for pagination
User.page(7).per(50)

koala

https://github.com/arsduo/koala

  • Wraps facebook APIs and auth
@graph = Koala::Facebook::API.new(oauth_access_token)
@graph.get_connections("me", "mutualfriends/#{friend_id}")

monban

https://github.com/halogenandtoast/monban

  • User management
Monban.config.sign_up_service.new(user_attributes).perform
Monban.config.authentication_service.new(user, current_password).perform
Monban.config.password_reset_service.new(user, password).perform

money-rails

https://github.com/RubyMoney/money-rails

class Product < ActiveRecord::Base
  register_currency :usd
  monetize :price_cents
end
...
add_money :products, :price

oj

https://github.com/ohler55/oj

  • Fast JSON parser

parity

https://github.com/thoughtbot/parity

  • Shell commands for development, staging, and production parity for Heroku apps
production backup

development restore production
staging deploy

staging console

progressbar

https://github.com/jfelchner/ruby-progressbar

    def progressbar
      @progressbar ||= ProgressBar.create(title: "#{title} (#{record_count - 1})",
                                          starting_at: 0,
                                          total: record_count)
    end

	def import!
      progressbar.tap do |bar|
        CSV.foreach(file_path, headers: true) do |row|
          ...
        end
        bar.finish
      end
    end

puma

https://github.com/puma/puma

  • alternative web server

pundit

  • Restrict CRUD actions to certain users
  • Scope records to those who should be able to view them
class IncidentPolicy < ApplicationPolicy
  def create?
    user.admin?
  end

  def show?
    user.admin? || record.user == user
  end

  def index?
    true
  end

  class Scope < Scope
    def resolve
      if user.admin?
        scope.all
      else
        scope.where(user: user)
      end
    end
  end
end
  def index
    incidents = policy_scope(Incident)
    authorize incidents

    render json: incidents
  end

  def show
    incident = Incident.find(params[:id])
    authorize incident

    render json: incident
  end

pusher

  • Library/service for cross-platform (socket-based) push notifications

railroady

https://github.com/preston/railroady

  • UML diagrams, etc.

rails_12factor

https://github.com/heroku/rails_12factor

  • Deployment configs, logging, etc. management

randumb

https://github.com/spilliton/randumb

Artist.order_by_rand.limit(3).all

recipient_interceptor

https://github.com/croaky/recipient_interceptor

  • Never accidentally send emails to real people from your staging environment.

rubocop

https://github.com/bbatsov/rubocop

  • Ruby linter with CLI and editor plugins

shoryuken

https://github.com/phstc/shoryuken

  • Consume messages from AWS' Simple Queueing Service

shoulda-matchers

https://github.com/thoughtbot/shoulda-matchers

  • One-liner rspec tests for common functionality
describe Person do
  it { should validate_presence_of(:name) }
end

sidekiq

  • Easy background processing
class HardWorker
  include Sidekiq::Worker
  def perform(name, count)
    # do something
  end
end

HardWorker.perform_in(5.minutes, 'bob', 5)

simple_form

https://github.com/plataformatec/simple_form

<%= simple_form_for(@maker_space, :url => admin_maker_spaces_path, :method => :post) do |f| %>
  <%= f.error_notification %>

  <div class="form-inputs">
    <%= f.input :name %>
    <%= f.input :location %>
    <%= f.input :latitude %>
    <%= f.input :longitude %>
    <%= f.input :image %>
    <%= f.input :access_requirements %>
  </div>

  <div class="form-actions">
    <%= f.button :submit %>
  </div>
<% end %>

time_for_a_boolean

https://github.com/calebthompson/time_for_a_boolean

class Post < ActiveRecord::Base
  time_for_a_boolean :deleted
end

post.deleted?

post.deleted = true
post.save

post.deleted_at
# <timestamp>

timecop

https://github.com/travisjeffery/timecop

  • Manipulate time, as understood by Time.now, Date.today, and DateTime.now
joe = User.find(1)
joe.purchase_home()
assert !joe.mortgage_due?
# move ahead a month and assert that the mortgage is due
Timecop.freeze(Date.today + 30) do
  assert joe.mortgage_due?
end

travis

https://github.com/travis-ci/travis.rb

  • Intreface with Travis CI

venice

https://github.com/nomad/venice

  • Apple IAP receipt validation
data = "(Base64-Encoded Receipt Data)"
if receipt = Venice::Receipt.verify(data)
  ...
end

versionist

https://github.com/bploetz/versionist

  • Easily support different API versioning strategies

HTTP Header strategy Accept: application/vnd.mycompany.com; version=1,application/json

MyApi::Application.routes.draw do
  api_version(:module => "V1", :header => {:name => "Accept", :value => "application/vnd.mycompany.com; version=1"}) do
    match '/foos.(:format)' => 'foos#index', :via => :get
    match '/foos_no_format' => 'foos#index', :via => :get
    resources :bars
  end
end

Path strategy /v3/foos

MyApi::Application.routes.draw do
  api_version(:module => "V3", :path => {:value => "v3"}) do
    match '/foos.(:format)' => 'foos#index', :via => :get
    match '/foos_no_format' => 'foos#index', :via => :get
    resources :bars
  end
end

Request Parameter /foos?version=v2

MyApi::Application.routes.draw do
  api_version(:module => "V2", :parameter => {:name => "version", :value => "v2"}) do
    match '/foos.(:format)' => 'foos#index', :via => :get
    match '/foos_no_format' => 'foos#index', :via => :get
    resources :bars
  end
end

warden

https://github.com/hassox/warden

  • User management
class V1::AuthenticatedController < V1::ApiController
  def current_user
    request.env['warden'].user
  end
end

class AuthorizedConstraint
  def matches?(request)
    warden = request.env['warden']
    warden && warden.authenticate!(:token_authentication_strategy)
  end
end
# in routes.rb

	constraints AuthorizedConstraint.new do
	  resources :users, only: [:show, :index, :update]
	end

webmock

https://github.com/bblimke/webmock

  • Stub web requests for tests
stub_request(:post, "www.example.com").
  with(:body => "abc", :headers => { 'Content-Length' => 3 })

uri = URI.parse("http://www.example.com/")
req = Net::HTTP::Post.new(uri.path)
req['Content-Length'] = 3

res = Net::HTTP.start(uri.host, uri.port) do |http|
  http.request(req, "abc")
end    # ===> Success

zero_push

https://github.com/ZeroPush/zero_push

  • Easily send iOS push notifications
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment