quattro_4 scribble

scribble 落書き (調べた事をただ落書きする)

RubyTapas #179 - #183

179 Sequel

★★

require "sequel"

DB = Sequel.sqlite

DB.create_table :people do
  primary_key :id
  String :name
end

DB.create_table :items do
  foreign_key :person_id, :people
  String  :name
  Integer :quantity, default: 1
  unique  [:person_id, :name]
end
stacey_id = DB[:people].insert(name: "Stacey")
DB[:people].count
DB[:people].all
DB[:items].each do |row|
  p row
end
DB[:items].first
DB[:items].sum(:quantity)
DB[:items].where(name: "Mineral Water")
# => #<Sequel::SQLite::Dataset: "SELECT * FROM `items` WHERE (`name` = 'Mineral Water')">
  • If we replace #join with #graph, Sequel automatically aliases the items_name column in order to resolve the naming collision
DB[:people].join(:items, person_id: :id).where(person_id: avdi_id).all
# => [{:id=>2, :name=>"Granola", :person_id=>2, :quantity=>1},
DB[:people].graph(:items, person_id: :id).where(person_id: avdi_id).all
# => [{:id=>2, :name=>"Avdi", :person_id=>2, :items_name=>"Granola", :quantity=>1},

180 Ghost Load

★★

  • ghost_episode.rb (ghost object pattern)
    • def initialize(attributes={})
      • @load_state = :ghost
    • def load
      • return if load_state == :loaded
    • def description
      • load
      • @description
ep.load_state                   # => :ghost
ep.description         
# => "Today we explore a pattern for bridging the gap between different domain models."
ep.load_state                   # => :loaded

181 Schwartzian Transform

★★

videos = Dir["../*/[0-9][0-9][0-9]*.mp4"]

def duration(filename)
  stats = `avprobe #{filename} 2>&1`
  /(?<=Duration: )\d\d:\d\d:\d\d\.\d\d/.match(stats).to_s
end
duration(videos.last)          # => "00:04:44.48"

videos.count                    # => 144
start_time = Time.now
sorted_videos = videos.sort{|x, y| duration(x) <=> duration(y)}
Time.now - start_time           # => 53.290444705
  • problem of slow sorting

    • create a global $call_count variable. inside duration
    • we're sorting 144 video files. But the #duration method is called a whopping 1824 times!
  • Let's rewrite our sort. Before the sort, we map our list of videos into a list of two-element arrays. Each array has the video file name as its first element, and the video duration as the second element.

sorted_videos = videos.map{|v| [v, duration(v)]} 
  .sort{|(xv, xd), (yv, yd)| xd <=> yd} 
  .map(&:first)
Time.now - start_time           # => 4.059575059

sort_byと同じ

sorted_videos = videos.sort_by{|v| duration(v)}

182 Macro

★★

episode 180 続き

  class Episode

    def self.lazy_accessor(*names)
      names.each do |name|
        attr_writer name
        define_method(name) do
          load
          instance_variable_get("@#{name}")
        end
      end
    end

    lazy_accessor :description,
                  :synopsis,
                  :publish_time

183 Extracting Ghost Load

episode 180, 182 続き

★★

  • module Ghostly追加
    • def lazy_accessor定義
    • module Macros追加して内容を移動 + private化
    • def self.included
    • load_state, load
module RubyTapas
  module Ghostly
    module Macros
      private
      def lazy_accessor(*names)
        names.each do |name|
          attr_writer name
          define_method(name) do
            load
            instance_variable_get("@#{name}")
          end
        end
      end
    end

    def self.included(other)
      other.extend(Macros)
    end

    attr_accessor :data_source
    attr_writer   :load_state

    def load_state
      @load_state ||= :ghost
    end

    def load
      return if load_state == :loaded
      data_source.load(self)
    end
  end

  class Episode
    include Ghostly