expect does not support == matcher.

expect(actual).to eql(expected)   # passes if actual.eql?(expected)
expect(actual).to equal(expected) # passes if actual.equal?(expected)

# NOTE: `expect` does not support `==` matcher.

基本思想是:

  • 当访问一个对象的方法时,如果这个方法不存,则转发这个方法给委托的对象
  • Delegation is particularly useful with Active Record associations:
class Greeter < ActiveRecord::Base
  def hello
    'hello'
  end

  def goodbye
    'goodbye'
  end
end

class Foo < ActiveRecord::Base
  belongs_to :greeter
  delegate :hello, to: :greeter
end

Foo.new.hello   # => "hello"
Foo.new.goodbye # => NoMethodError: undefined method `goodbye' for #<Foo:0x1af30c>

pg连接错误

postgresql_adapter.rb:1216:in `initialize': 致命错误:  用户 "pgsql" Ident 认证失败 (PG::ConnectionBad)

问题环境
在安装postgresql_server的机器上,部署rails应用,连接时host:127.0.0.1出现以上错误,改为ip地址后,正常访问

ruby hash 排序

h = {"sa"=>1, "ab"=>2, "x3"=>3, "de"=>, :de=>5}
h.sort_by{|k,v|k.to_s}
#=> [["ab", 2], [:de, 5], ["de", 6], ["sa", 1], ["x3", 3]]

h.sort_by{|k,v|v.to_s}
#=>[["sa", 1], ["ab", 2], ["x3", 3], [:de, 5], ["de", 6]]

h.sort_by{|k|k[0].to_s}
#=>[["ab", 2], [:de, 5], ["de", 6], ["sa", 1], ["x3", 3]]

h.sort_by{|k|k[1].to_s}
#=>[["sa", 1], ["ab", 2], ["x3", 3], [:de, 5], ["de", 6]]

h = {"sa"=>1, "ab"=>2, "x3"=>3, "de"=>6, :de=>5}
h.sort
#=>argumentError: comparison of Array with Array failed,因为:de=>5与"de"=>6 Key类型不一样,键类型一样,才可以直接用sort,按键排序,否则要把键处理为一致的数据(可以比较的数据类型)

Most of the version specifiers, like >= 1.0, are self-explanatory. The specifier ~> has a special meaning, best shown by example. ~> 2.0.3 is identical to >= 2.0.3 and < 2.1. ~> 2.1 is identical to >= 2.1 and < 3.0. ~> 2.2.beta will match prerelease versions like 2.2.beta.12.

  • 可以看到为了提高查询性能,采用C去实现,如果安装了RubyInline,会启动C扩展实现的同名方法,加速binary_search查询性能
  • 也可以发现,从客户端分配键到哪个redis节点是通过比较节点的crc32与key的crc32的大小的方式,进行分配的
    class << self
      # gem install RubyInline to use this code
      # Native extension to perform the binary search within the hashring.
      # There's a pure ruby version below so this is purely optional
      # for performance.  In testing 20k gets and sets, the native
      # binary search shaved about 12% off the runtime (9sec -> 8sec).
      begin
        require 'inline'
        inline do |builder|
          builder.c <<-EOM
          int binary_search(VALUE ary, unsigned int r) {
              int upper = RARRAY_LEN(ary) - 1;
              int lower = 0;
              int idx = 0;
    
              while (lower <= upper) {
                  idx = (lower + upper) / 2;
    
                  VALUE continuumValue = RARRAY_PTR(ary)[idx];
                  unsigned int l = NUM2UINT(continuumValue);
                  if (l == r) {
                      return idx;
                  }
                  else if (l > r) {
                      upper = idx - 1;
                  }
                  else {
                      lower = idx + 1;
                  }
              }
              if (upper < 0) {
                upper = RARRAY_LEN(ary) - 1;
              }
              return upper;
          }
          EOM
        end
      rescue Exception
        # Find the closest index in HashRing with value <= the given value
        def binary_search(ary, value, &block)
          .....
        end
    
      end
    end

rspec exception comparisons

expect(actual).to be >  expected
expect(actual).to be >= expected
expect(actual).to be <= expected
expect(actual).to be <  expected
expect(actual).to be_between(minimum, maximum).inclusive
expect(actual).to be_between(minimum, maximum).exclusive
expect(actual).to match(/expression/)
expect(actual).to be_within(delta).of(expected)
expect(actual).to start_with expected
expect(actual).to end_with expected

secret_token:问题

A secret is required to generate an integrity hash for cookie session data. Use config.secret_token = “some secret phrase of at least 30 characters”in config/initializers/secret_token.rb

解决方法:

运行 rake secret 生成secret_token,创建config/initializers/secret_token.rb,添加代码

YouApp::Application.config.secret_token = '用 rake secret 生成的token'

  1. 返回储存在给定键的 HyperLogLog 的近似基数
  2. 通过 HyperLogLog 数据结构, 用户可以使用少量固定大小的内存, 来储存集合中的唯一元素 (每个 HyperLogLog 只需使用 12k 字节内存,以及几个字节的内存来储存键本身)。
    
    redis> PFADD  databases  "Redis"  "MongoDB"  "MySQL"
    (integer) 1
    
    redis> PFCOUNT  databases
    (integer) 3
    
    redis> PFADD  databases  "Redis"    # Redis 已经存在,不必对估计数量进行更新
    (integer) 0
    
    redis> PFCOUNT  databases    # 元素估计数量没有变化
    (integer) 3
    
    redis> PFADD  databases  "PostgreSQL"    # 添加一个不存在的元素
    (integer) 1
    
    redis> PFCOUNT  databases    # 估计数量增一
    4

问题思考:
这个是如何实现的,不存储元素的话,如何知道这个元素已经存在了。

module Rails
  class Server < ::Rack::Server
    def default_options
      super.merge({
        Port:               3000,
        DoNotReverseLookup: true,
        environment:        (ENV['RAILS_ENV'] || ENV['RACK_ENV'] || "development").dup,
        daemonize:          false,
        debugger:           false,
        pid:                File.expand_path("tmp/pids/server.pid"),
        config:             File.expand_path("config.ru")
      })
    end

    def start
      print_boot_information
      trap(:INT) { exit }
      create_tmp_directories
      log_to_stdout if options[:log_stdout]

      super
    ensure
      # The '-h' option calls exit before @options is set.
      # If we call 'options' with it unset, we get double help banners.
      puts 'Exiting' unless @options && options[:daemonize]
    end
  end
end

可以看到和rails2相比,已经大不一样了,代码全部移动到了Rails模块中,结构也更加清晰

Two segments are allocated for each index in InnoDB. One is for nonleaf nodes of the B-tree, the other is for the leaf nodes. Keeping the leaf nodes contiguous on disk enables better sequential I/O operations, because these leaf nodes contain the actual table data.

每个索引拥有2个segment,一个用于非叶结点,另一个用于叶子结点

InnoDB does not guarantee that it uses free pages sequentially, and many optimizations around bulk data loading will cause pages to be used out of order. (More on page splitting and these optimizations)

1、Innodb不保证连续的使用空闲空间保存数据,这么做的原因有(页的分裂和一些优化)
2、在为新数据分配extent时,可以看到有种情况发生:一个index后面是多个free page(63个),然后再是一个index.

B+树索引本身并不能找到一条记录,只能找到记录所在的页。详细见具本章节中的内容,及查看本机数据内容。找到所在页后,把页载入内存,

在内存中对Page Directory进行binary search。依次类推,最终找到记录(通过n_owned)。

require 'zlib'

class Redis
  class HashRing

    POINTS_PER_SERVER = 160 # this is the default in libmemcached

    attr_reader :ring, :sorted_keys, :replicas, :nodes

    # nodes is a list of objects that have a proper to_s representation.
    # replicas indicates how many virtual points should be used pr. node,
    # replicas are required to improve the distribution.
    def initialize(nodes=[], replicas=POINTS_PER_SERVER)
      @replicas = replicas
      @ring = {}
      @nodes = []
      @sorted_keys = []
      nodes.each do |node|
        add_node(node)
      end
    end

    # Adds a `node` to the hash ring (including a number of replicas).
    def add_node(node)
      @nodes << node
      @replicas.times do |i|
        key = Zlib.crc32("#{node.id}:#{i}")
        @ring[key] = node
        @sorted_keys << key
      end
      @sorted_keys.sort!
    end

    def remove_node(node)
      @nodes.reject!{|n| n.id == node.id}
      @replicas.times do |i|
        key = Zlib.crc32("#{node.id}:#{i}")
        @ring.delete(key)
        @sorted_keys.reject! {|k| k == key}
      end
    end

    # get the node in the hash ring for this key
    def get_node(key)
      get_node_pos(key)[0]
    end

    def get_node_pos(key)
      return [nil,nil] if @ring.size == 0
      crc = Zlib.crc32(key)
      idx = HashRing.binary_search(@sorted_keys, crc)
      return [@ring[@sorted_keys[idx]], idx]
    end

    def iter_nodes(key)
      return [nil,nil] if @ring.size == 0
      _, pos = get_node_pos(key)
      @sorted_keys[pos..-1].each do |k|
        yield @ring[k]
      end
    end

    class << self

      # gem install RubyInline to use this code
      # Native extension to perform the binary search within the hashring.
      # There's a pure ruby version below so this is purely optional
      # for performance.  In testing 20k gets and sets, the native
      # binary search shaved about 12% off the runtime (9sec -> 8sec).
      begin
        require 'inline'
        inline do |builder|
          builder.c <<-EOM
          int binary_search(VALUE ary, unsigned int r) {
              int upper = RARRAY_LEN(ary) - 1;
              int lower = 0;
              int idx = 0;

              while (lower <= upper) {
                  idx = (lower + upper) / 2;

                  VALUE continuumValue = RARRAY_PTR(ary)[idx];
                  unsigned int l = NUM2UINT(continuumValue);
                  if (l == r) {
                      return idx;
                  }
                  else if (l > r) {
                      upper = idx - 1;
                  }
                  else {
                      lower = idx + 1;
                  }
              }
              if (upper < 0) {
                upper = RARRAY_LEN(ary) - 1;
              }
              return upper;
          }
          EOM
        end
      rescue Exception
        # Find the closest index in HashRing with value <= the given value
        def binary_search(ary, value, &block)
          upper = ary.size - 1
          lower = 0
          idx = 0

          while(lower <= upper) do
            idx = (lower + upper) / 2
            comp = ary[idx] <=> value

            if comp == 0
              return idx
            elsif comp > 0
              upper = idx - 1
            else
              lower = idx + 1
            end
          end

          if upper < 0
            upper = ary.size - 1
          end
          return upper
        end

      end
    end

  end
end

An approach I’ve long taken to understanding something that is complex and poorly documented is the following three steps:

  1. Read the existing documentation and the existing code, until a basic understanding is reached. Often there are serious misunderstandings or incorrect factorization at this step.

  2. Write my own implementation, even a very basic and broken one, preferably in a completely different language (which avoids the tendency to cut and paste anything). Revise my understanding based on what works and what doesn’t.

  3. Create new documentation and diagrams based on my new understanding. Refactor my implementation as necessary (the act of reviewing everything in order to document it often reveals incorrect factorizations). Correct documentation based on new understanding from refactoring code. Repeat until correct.

复活节(主复活日)是一个西方的重要节日,在每年春分月圆之后第一个星期日。基督徒认为,复活节象征着重生与希望,为纪念耶稣基督于公元30到33年之间被钉死在十字架之后第三天复活的日子。天主教/新教和东正教复活节的计算略有差异,根据罗马教会传统,在春分(3月21日)当日见到满月或过了春分见到第一个满月后,遇到的第一个星期日即为复活节。东正教会则规定,如果满月恰好出现在这第一个星期日,则复活节再推迟一周。因此,复活节的日期大致在公历3月22日至4月25日之间。

  复活节那一天,是经由三个历法(分别是西历、中国阴历、星期)合并出来的,怪不得它会如此飘忽不定了。有些年份三个教派的复活节是重合的,而有些年份又不同,进而它们耶稣受难日的日期也有所差异,2015年的天主教、新教的复活节是2015年4月5日。

Read more »

# =============================
# 目标1:
# 1、测试eval的filename,与lineno参数使用
# =============================
# 结论
# 1、可以自定义程序出错时的出错信息(出错的文件名,程序在哪行执行的,方便出错日志的查询,错误的定位)
# =============================

class MyClass
  def my_method
    string = "1/0"
    # eval(string, nil, __FILE__, __LINE__)
    eval(string, nil, "custom_file.rb", 60)
    # eval(string)
  end
end

MyClass.new.my_method # custom_file.rb:60:in `/': divided by 0 (ZeroDivisionError)

# =============================
# 目标1:
# 1、load文件参数为false时,对变量,实例变量,常量的影响
# 2、load文件参数为false时,类是否被重新打开
# =============================
# 结论
# 1、变量不会被加载的文件影响,实例变量和常量会被影响。因为被加载文件中的变量会在文件加载完成后,掉出加载文件的作用域
#    而实例变量和常量不会掉出作用域
# 2、类可以被重新打开,因为被加载文件中类也没有掉出作用域(因为类也是常量)
# =============================
lambda {
  var  = 2
  @var = 2
  VAR  = 2

  class MyClass
    def my_method
      "i'm in load_2 file"
    end
  end

  puts "==========Test1:OUTPUT=========="

  load File.expand_path('load_file.rb', File.dirname(__FILE__))

  puts '==========load_2.rb======='
  puts "var :#{var }"
  puts "@var:#{@var}"
  puts "VAR :#{VAR }"
  puts "my_method:#{MyClass.new.my_method}"
  puts '==========load_2.rb======='
  puts "==========Test1:OUTPUT=========="
  # <!-- more -->
  # ==========Test1:OUTPUT==========
  # ==========load_1.rb=======
  # var :1
  # @var:1
  # VAR :1
  # my_method:i'm in load_1 file
  # ==========load_1.rb=======

  # ==========load_2.rb=======
  # var :2
  # @var:1
  # VAR :1
  # my_method:i'm in load_1 file
  # ==========load_2.rb=======
  # ==========Test1:OUTPUT==========

}

# =============================
# 目标2:
# 1、load文件参数为true时,对变量,实例变量,常量的影响
# 2、load文件参数为true时,类是否被重新打开
# =============================
# 结论
# 1、变量不会被加载的文件影响,实例变量和常量也不会被影响
# 2、类不会被影响
# 因为ruby会在load文件时为其创建匿名模块,让其文件中的实例变量、常量、类落在其自身范围内,且在load完成后,销毁这个
# 匿名模块(即加载文件中的所有内容),加入true参数的这种做法无法让加载它的文件使用其文件中代码,仅仅是起到了运行load
# 文件的代码的作用
# =============================
lambda {
  var  = 2
  @var = 2
  VAR  = 2

  class MyClass
    def my_method
      "i'm in load_2 file"
    end
  end

  puts "==========Test2:OUTPUT=========="

  load File.expand_path('load_file.rb', File.dirname(__FILE__)), true

  puts '==========load_2.rb======='
  puts "var :#{var }"
  puts "@var:#{@var}"
  puts "VAR :#{VAR }"
  puts "my_method:#{MyClass.new.my_method}"
  puts '==========load_2.rb======='
  puts "==========Test2:OUTPUT=========="

  # ==========Test2:OUTPUT==========
  # ==========load_1.rb=======
  # var :1
  # @var:1
  # VAR :1
  # my_method:i'm in load_1 file
  # ==========load_1.rb=======

  # ==========load_2.rb=======
  # var :2
  # @var:2
  # VAR :2
  # my_method:i'm in load_2 file
  # ==========load_2.rb=======
  # ==========Test2:OUTPUT==========
}.call

load_file.rb

# module Load1

var = 1
@var = 1
VAR = 1

class MyClass
  def my_method
    "i'm in load_1 file"
  end
end

puts '==========load_1.rb======='
puts "var :#{var }"
puts "@var:#{@var}"
puts "VAR :#{VAR }"
puts "my_method:#{MyClass.new.my_method}"
puts '==========load_1.rb======='
puts

# end