Skip to content

Instantly share code, notes, and snippets.

@squarism
Last active October 3, 2017 20:21
Show Gist options
  • Save squarism/5638319 to your computer and use it in GitHub Desktop.
Save squarism/5638319 to your computer and use it in GitHub Desktop.
All my ruby tricks! All my secrets! Reveal thyself fool!
# Plot Twist: Does not contain all my Ruby Tricks <('‿'<)
# initialize a hash with empty array default value, on no key found, you can just push into a new array
hash = Hash.new {|h, k| h[k] = [] }
hash[:never_seen_before] << "asdf"
hash
# => {:never_seen_before => ["asdf"]}
# Default method options.
# ---------------------------------------------------------------------
# do not overabuse
# http://robots.thoughtbot.com/post/50655960596/sandi-metz-rules-for-developers
def some_method options={}
# sensible defaults
options.merge!({
some_default: 42,
something_else: 'asdf'
})
use_options(options[:something_else])
end
# Even better default method options with active_support's reverse merge
# ---------------------------------------------------------------------
require 'active_support/core_ext'
def some_method options={}
# sensible defaults
options.reverse_merge!({
some_default: 42,
something_else: 'asdf'
})
puts "I'm going to do #{options[:something_else]} with #{options[:some_default]} now!"
end
some_method({ some_default: 56 })
# Select only the attributes you want from a hash.
# this is crazy useful when getting web parameters for example.
# ---------------------------------------------------------------------
require 'active_support/core_ext'
bacon = { delicious:true, color:"red" }
bacon.slice(:color)
# => { color:"red" }
# it returns a new hash, you can slice unknown keys and it still works.
# there's a lot of great stuff in active_support (from the rails gem)
# it mostly stands by itself but it's big so you can require just 'active_support/core_ext'
# to be more precise. if you want everything in active_support do require 'active_support/all'
# but your global namespace might get too crowded.
# Super awesome time formatting
# ---------------------------------------------------------------------
# Let's say we want to watch The Avengers. It's really long! Do we have time? Who knows!?
require 'active_support/core_ext'
require 'stamp'
running_time = 143.minutes
end_of_movie = Time.now + running_time
end_of_movie.format_like("12:45am")
# => "01:18am"
# require a whole directory of files
# ---------------------------------------------------------------------
project_root = File.dirname(File.absolute_path(__FILE__))
Dir.glob(project_root + '/helpers/*') {|file| require file }
# Are you writing a console app outside of rails but you are using ActiveRecord?
# The next few examples are things I end up doing over and over again.
# ---------------------------------------------------------------------
# Reconnect automatically to the database if the server dies
# ---------------------------------------------------------------------
# Do this before each sql call. Rails does this automatically in the controller on every request.
ActiveRecord::Base.verify_active_connections!
# Wire up an the ActiveRecord ORM outside of a Rails project:
# ---------------------------------------------------------------------
# 1. Create a lib folder and create a file called config.rb. You'll want to require this for
# anything that needs to use the DB.
# lib/config.rb
require 'active_record'
# global config variable, bleh, you could use configatron if you don't like globals.
$dbconfig = YAML.load(File.read('database.yml'))['development']
ActiveRecord::Base.establish_connection $dbconfig
# database.yml
development:
adapter: mysql2
database: foo_development
host: your-server
username: user
password: pass
pool: 8
# 2. in a small project, I put all my classes in one file, you can break it up, just require config.rb like below
# app/models.rb
require_relative '../lib/config'
class Thing < ActiveRecord::Base
establish_connection $dbconfig['development']
end
# Logging outside a Rails project
# ---------------------------------------------------------------------
# In the lib/config.rb file above:
$logger = Logger.new('development.log')
# You can see that using globals is pretty bad. But you don't have a Rails environment
# to put these concepts into. When this grows too big, I either switch to a framework
# or use configatron as a simplified config store. So something like:
# configatron.logger = Logger.new('development.log')
# (usage later)
# configatron.logger.info "This is an INFO level log message."
# Migraitons outside a Rails project
# ---------------------------------------------------------------------
# migrate.rb -- this will be the equivalent of rake db:migrate
require './lib/config'
ActiveRecord::Migrator.migrate './migrations', ARGV[0] ? ARGV[0].to_i : nil
# create a folder for migrations called migrations
# migrations/001_create_foo.rb
require './lib/config'
class CreateFooTable < ActiveRecord::Migration
def change
create_table :foos, :options => 'Engine=InnoDB DEFAULT CHARSET=utf8' do |table|
table.string :bar
table.timestamps
end
end
end
# usage:
# ruby migrate.rb
# ruby migrate.rb VERSION=0000 # (rollback)
# Weee! Isn't this better than SQL scripts?!
# Explicitly access a hash using a key that you probably meant
# ---------------------------------------------------------------------
# Hashes (especially with ActiveRecord objects) can be slightly annoying with
# almost case sensitive access. You probably don't have conflicting symbols
# and strings for keys. So make the computer do what you meant and not what you said.
hash = { name: "Steve", "age": 590 }
hash.with_indifferent_access :age
# => 590
another_example = User.first
another_example.attributes.with_indifferent_access "name"
# => "Steve"
# Create a mixin that allows for overriding in the implementation class
# ---------------------------------------------------------------------
module Feature
def hook
puts "I'm in the mixin hook!"
super if defined?(super)
end
end
# require statements needed here if splitting up into files obviously ...
class User
include Feature
# omit this and the mixin hook method will run
def hook
puts "I'm overriding the mixin!"
end
end
u = User.new
u.hook
# => "I'm overriding the mixin!"
# A good gitignore file for rails projects
# ---------------------------------------------------------------------
# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so
# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
# Logs and databases #
######################
*.log
*.sql
*.sqlite
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Rails specific #
##################
*.rbc
*.sassc
.sass-cache
capybara-*.html
.rspec
.rvmrc
/.bundle
/vendor/bundle
/log/*
/tmp/*
/db/*.sqlite3
/public/system/*
/coverage/
/spec/tmp/*
**.orig
rerun.txt
.project
config/database.yml
config/mongoid.yml
#################################################################################################
# Running a specific version of rails to generate a project.
# so you have multiple gems installed? or maybe you have a gemset per project?
# how the !@#$ do you generate a new project then? what if you want to stay behind a version?
# Well, there's this magical syntax that makes no fucking sense.
rails --version # => the latest version
rails _3.2.14_ --version # => Rails 3.2.14
# Big bundler gotcha here.
# ---------------------------------------------------------------------
# this also goes in the WTF bucket
bundle install --without development
# doing the above for production will remember the setting in .bundle/config
# THIS IS MADNIS. IT SHOULD NOT DO THIS.
# For example, if you test your deployment with jruby with --without development then bundler will remember that setting forever
# and you'll be like WTF later when pry doesn't load or ... anything in the dev group. :( :(
# fix: specify a group that doesn't exist. this is soooo stupid.
bundle install --without foo
# Sort a hash by value but return a Hash.
# ---------------------------------------------------------------------
# Sorting a hash and some other methods on Hash return an array.
# This can sometimes not be what you want.
Hash[h.sort_by{ |_, v| -v }]
# This works when the value is a number
counts = { water: 1, air: 2, earth: 0 }
counts.sort_by { |_, v| v }.to_h
# => {:earth=>0, :water=>1, :air=>2}
counts.sort_by { |_, v| -v }.to_h
# => {:air=>2, :water=>1, :earth=>0}
# This does not work when it's strings.
# Only ascending order works ... but that's ok because we can reverse.
hash = { third: "c", second: "b", first: "a" }
hash.sort_by { |_, v| v }.to_h
# => {:first=>"a", :second=>"b", :third=>"c"}
hash.sort_by { |_, -v| v }.to_h # THIS DOES NOT WORK
# => {:first=>"a", :second=>"b", :third=>"c"}
hash.sort_by { |_, v| v }.reverse.to_h
# => {:third=>"c", :second=>"b", :first=>"a"}
# This is different than Array sorting which can take a spaceship operator <=>
# to compare current and previous values as it iterates.
array = [ "cat", "apple", "bear" ]
array.sort { |a, b| a <=> b }
# => ["apple", "bear", "cat"]
array.sort { |a, b| b <=> a }
# => ["cat", "bear", "apple"]
# What if we want something custom? Well, the sort method just works off of
# 1, 0, -1
# where 1 is ASCENDING -> apple bear cat
# 0 is UNCHANGED -> cat apple bear
# -1 is DESCENDING -> cat bear apple
# But you cannot do this, it's evaluated as the Enumerable executes.
array.sort { |a, b| -1 } # no worky
# => ["cat", "apple", "bear"]
# So if we wanted smallest words to biggest words (ascending)
# We can do this:
array.sort { |a, b| a.length <=> b.length }
# => ["cat", "bear", "apple"]
# Word frequency or doing a word count from an array of words in Ruby
# ---------------------------------------------------------------------
words = [ 'pie', 'is', 'delicious', 'but', 'bacon', 'is', 'delicious', 'too' ]
words.reduce(Hash.new(0)) { |counts, word| counts[word] += 1; counts }
# => {"pie"=>1, "is"=>2, "delicious"=>2, "but"=>1, "bacon"=>1, "too"=>1}
# or maybe map reduce style all in one go.
sentence = "pie is delicious but bacon is delicious too."
sentence.split.
map { |s| s.gsub('.','') }.
reduce(Hash.new(0)) { |counts, word| counts[word] += 1; counts }
# => {"pie"=>1, "is"=>2, "delicious"=>2, "but"=>1, "bacon"=>1, "too"=>1}
# Multiple formats in Grape API
# ---------------------------------------------------------------------
# ...
class API < Grape::API
default_format :json
format :json
content_type :xml, "text/xml"
content_type :json, "application/json"
# ...
# Now you can do .xml and .json in the same api like a rails app.
# In memory ruby database techniques
# ---------------------------------------------------------------------
# Gems:
# ephermeral is good for api loading but not general cases
# supermodel requires a really old version of activemodel (3.0) but there are PRs
# activerecord-nulldb-adapter wants a rails project although you might be able to fake it out,
# started going down that path a little bit but it turned weird.
# active_hash - good option, has some caveats with AR associations, not a big deal.
# Like everything ... it depends on what you are doing.
# Active Model in-memory database
require 'active_model'
class Person
# please turn these on below as needed
# -------------------------------------
# include ActiveModel::AttributeMethods
# extend ActiveModel::Callbacks
# include ActiveModel::Conversion
# include ActiveModel::Dirty
# include ActiveModel::Validations
attr_accessor :name, :age
end
p = Person.new
p.name = "Joe"
p.age = 21
# No storage so you can't do Person.where at this point. By itself this is worse than struct or ostruct.
# Adding the other mixins makes active_model a good route to take.
# Using ActionView helpers outside of Rails for maximum delicious syntax sugar num num num.
# ---------------------------------------------------------------------
require 'action_view'
# Mix this into a class if you have one. This will mix into "main".
include ActionView::Helpers::NumberHelper
number_to_human_size(8_765_309_000)
# => "8.16 GB"
# There are other useful helpers in ActionView but the usage is similar.
# Time Zone Cheat Sheet
# ---------------------------------------------------------------------
time = Time.current
Time.use_zone("Different Time Zone Name") do
Time.zone.local(time.year, time.month, time.day, time.hour, time.minutes, time.seconds)
end
# if it's 9:00 in "current time zone" this makes a 9:00 in "new time zone" so users don't have to think about it.
# From - http://www.elabs.se/blog/36-working-with-time-zones-in-ruby-on-rails
# DOs
2.hours.ago # => Thu, 27 Aug 2015 14:39:36 AFT +04:30
1.day.from_now # => Fri, 28 Aug 2015 16:39:36 AFT +04:30
Time.zone.parse("2015-08-27T12:09:36Z") # => Thu, 27 Aug 2015 16:39:36 AFT +04:30
Time.current # => Thu, 27 Aug 2015 16:39:36 AFT +04:30
Time.current.utc.iso8601 # When supliyng an API ("2015-08-27T12:09:36Z")
Time.strptime("2015-08-27T12:09:36Z", "%Y-%m-%dT%H:%M:%S%z").in_time_zone # If you can't use Time.zone.parse (Thu, 27 Aug 2015 16:39:36 AFT +04:30)
Date.current # If you really can't have a Time or DateTime for some reason (Thu, 27 Aug 2015)
Date.current.in_time_zone # If you have a date and want to make the best out of it (Thu, 27 Aug 2015 00:00:00 AFT +04:30)
# DON'Ts
Time.now # Returns system time and ignores your configured time zone. (2015-08-27 14:09:36 +0200)
Time.parse("2015-08-27T12:09:36Z") # Will assume time string given is in the system's time zone. (2015-08-27 12:09:36 UTC)
Time.strptime("2015-08-27T12:09:36Z", "%Y-%m-%dT%H:%M:%S%z") # Same problem as with Time.parse. (2015-08-27 12:09:36 UTC)
Date.today # This could be yesterday or tomorrow depending on the machine's time zone, see https://github.com/ramhoj/time-zone-article/issues/1 for more info. (Thu, 27 Aug 2015)
# Show SQL output in pry if you loaded the rails environment
ActiveRecord::Base.logger = Logger.new(STDOUT)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment