Useful Resources

Articles

blog has information on why to prefer accessing instance variables via attribute methods.

TODO

irb --simple-prompt

Debugging

if $PROGRAM_NAME == __FILE__
  binding.irb if $DEBUG
end
ruby -d series.rb #
# or
ruby --disable-gems -d series.rb

load script into irb session(instead of adding binding at the EOF)

irb --simple-prompt -r ./series.rb

RDBG

bundle exec rdbg -c -- bundle exec rspec spec/worker_integrations/async_jobs/events/create_customer_order_from_external_order_updated_event_integration_spec.rb:57
b ActiveRecord::Associations::SingularAssociation#build
? b # RTFM

c # continue
i # info
s # step


f 1 # step back? frame command
del 0 # delete breakpoint

l # list, similar whereami?
finish

class instance vars

 class Lala
   def self.lala
     @hey ||= Time.now
   end
 end

 Lala.lala
14:17:21.110873 +1000
 Lala.lala
14:17:21.110873 +1000

Load Path

$LOAD_PATH.unshift(File.join(__dir__, 'lib'))

$LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))

Case pattern matching

case input
in "lala" then puts 'yolo'
in [_,a] if true
  puts a #=> 2
in _ => anything_else_reassigned
   puts anything_else_reassigned
end

Rails Resources

Tools

Gems

Cleaning up

gem pristine --all
gem cleanup
  • Gemfile of Dreams - The Libraries Evil Martians Use to Build Rails Apps

    Scheduling

    https://github.com/jjb/ruby-clock - ruby clock (for simple tasks)

Money

257.78 * 100
#=> 25777.999999999996
289.15 * 100
#=> 28914.999999999996

require "bigdecimal"
"%2.2f" % (BigDecimal('289.15') * BigDecimal(100))
#=> "28915.00"

https://www.honeybadger.io/blog/ruby-currency/

Ruby

assessment_type&.name
# OR
assessment_type.try(:name) # ------> RAILS verion

ARGS forwarding

def concrete_method(*positional_args, **keyword_args, &block)
	positional_args
	keyword_args
	block.call
end

def forwarding_method(...)
	concrete_method(...)
end

concrete_method(1, b: 2) { puts 3 }

All class methods

self.class.instance_methods(false)
  .each { |m| self.send m }

UUID

Digest::UUID.uuid_v5(Digest::UUID::OID_NAMESPACE, "fresho_salmon_heads")

Rails

init new project

rails new myapp \
  --minimal \
  --database=postgresql

Search all database

ApplicationRecord.descendants.map {|m| m.all }

Ignore columns on load

self.ignored_columns += %w[some_id]

Rails Logging switching via console

Rails.logger.level = :debug

Rake

https://github.com/friendlyantz/rake-sandbox

Inline execution

ruby -e puts 'Hello, world!'
rake -e puts 'Hello, world!'

File writing

%W[ch1.md ch2.md ch3.md].each do |md_file|
  html_file = File.basename(md_file, ".md") + ".html"
  file html_file => md_file do
    sh "pandoc -o #{html_file} #{md_file}"
  end
end

I had to call with .html, not .md, which was a bit confusing. Now rake handles file writing and trackes if there are any updates in .md file worth calling a conversion operation

❯ rake md_to_html README.html
pandoc -o README.html README.md
❯ rake md_to_html README.html
❯ rake md_to_html README.html

using Rules

unlike previous commit this rule defines a saving RULE for files with .html extension with prerequisite ‘md’ file, so the rake initially looks for a rule with exact match of README.html, doesn’t find and then goes to generic ‘.html’ rule Previously with used file task, with explicit name of the file

rule '.html' => '.md' do |t| 
  if `which pandoc`.empty?
    puts 'pandoc is not installed'
    exit 1
  end
  sh "pandoc -o #{t.name} #{t.source}"
end

Listing files

files = Rake::FileList['**/*.md']
files.exclude('**/~*') # exclude files with ~ in the name
files.exclude do |file|
  `git ls-files #{file}`.empty? # exclude files that are not tracked by git
end
files.exclude /^ignoredir/ # using REGEX

OR create instance of a file list and pass a block

files = Rake::FileList.new('**/*.md') do |fl|
  fl.exclude('**/~*') # exclude files with ~ in the name
  fl.exclude do |file|
    `git ls-files #{file}`.empty? # exclude files that are not tracked by git
  end
  fl.exclude(/^ignoredir/) # using REGEX
  fl.exclude(/README/) # using REGEX
end

listing files with new file ext

files.ext('html')

Exercism takeaways

GitHub exercism/ruby solutions with some comments in commit Interesting exercises:

  • Two Fer
  • Resistor Color Duo

class vs instance methods

The class level method is there for convenience only, and it should stay inflexible, as it is there for convenience not power. We will see this decision in use in our “expansion”, below.

Hash freezing and I18N connection

The languages available and the translations available should be a single thing, a Hash. Otherwise there more of a chance that things can become disconnected in the way that they are now related, (but not connected).

then as a circuit breaker

# meets condition, no-op
1.then.detect(&:odd?)            # => 1
# does not meet condition, drop value
2.then.detect(&:odd?)            # => nil

Progress Bar with custom style

bundle add progressbar

require 'progressbar'

      progressbar = ProgressBar.create(
        total: Ladida.count,
        format: "%a %e %P% %b\u{15E7}%i RateOfChange: %r Processed: %c from %C",
        progress_mark: " ",
        remainder_mark: "\u{FF65}",
      )

# in loop just do
progressbar.increment


# with colorize
require 'colorize'
progressbar = ProgressBar.create(
	total: range.count,
	format: "%a %e %P% %b#{"\u{15E7}".yellow}%i RateOfChange: %r Processed: %c from %C",
	progress_mark: ' ',
	remainder_mark: "\u{FF65}".light_green
)

docs https://github.com/jfelchner/ruby-progressbar/wiki/Formatting

CSV + AWS S3

require "csv"
require "aws-sdk-s3"

s3 = Aws::S3::Client.new(
  region: "ap-southeast-2",
  credentials: Aws::Credentials.new(
    ENV.fetch("AWS_ACCESS_KEY_ID"),
    ENV.fetch("AWS_SECRET_ACCESS_KEY"),
  ),
)

env = "stg"
response = s3.get_object(
  {
    bucket: "bucket_name#{env}",
    key: "dir/filename.csv",
  },
)

input_csv = CSV.new(response.body, headers: true)
progressbar = ProgressBar.create(total: input_csv.count)
input_csv.rewind # progress bar kills csv data
modified_data = []

input_csv.each do |row|
  row["new_column_header"] = "value_for_this_line"
  modified_data << row
  progressbar.increment
end

CSV.open("tmp/modified_data.csv", "wb") do |csv|
  csv << modified_data.first.headers
  modified_data.each do |row|
    csv << row
  end
end

s3.put_object(
  {
    body: File.read("tmp/modified_data.csv"),
    bucket: "bucker_name#{env}",
    key: "dir/filename_saturated.csv",
  },
)

Linting

https://evilmartians.com/chronicles/rubocoping-with-legacy-bring-your-ruby-code-up-to-standard

Leave a comment