Last active
October 3, 2017 20:21
-
-
Save squarism/5638319 to your computer and use it in GitHub Desktop.
All my ruby tricks! All my secrets! Reveal thyself fool!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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