环境:ruby 1.9.2 + rails 3.0.3
rails3里 autoload_paths 到底有什么用,什么情况加到 autoload_paths 中 ,什么时候使用require? , rails 2.X 里是 load_paths , 例如 rails3里添加 文件夹到 autoload_paths
config.autoload_paths += %W(#{Rails.root}/lib) # 不包括子文件夹
然后 代码里
include AuthenticatedSystem
就会从 autoload_paths 中寻找 authenticated_system.rb , autoload_paths 和java中的classpath类似 。。
所以没有必要把所有额外的 .rb 文件 都require 一下 , 这样会 影响启动时间 , 但是有些情况是必须 require 的 ,例如扩展 String ,Array ,Hash 类时 就必须require 了
autoload_paths 默认加到了 $LOAD_PATH 全局数组变量中 ,或者写成 $: , 用来保存 paths
autoload_paths 源码 : here
see:
环境: ruby1.9.2 + rails 3.0.3
我们知道 params 返回的是一个 hash , 例如 {"id"=>1} ,那为什么 params[:id] = 1 ,而不是 nil 呢 ?
irb下测试一下:
ruby-1.9.2-p0 > h={"id"=>1} => {"id"=>1} ruby-1.9.2-p0 > h[:id] => nil
带着这个疑问,设置断点 ,debug 进rails 源码 , 发现了原因,
1,跟到了 params 方法源码:
def params
@_params ||= request.parameters
end
给 @_params 设置Watch , 发现如下 :
发现 @_params 的class 是 ActiveSupport::HashWithIndifferentAccess
也就是说
params.class # => ActiveSupport::HashWithIndifferentAccess
2,继续 F7 跟进去
跟到了这个文件 的 default方法
require 'active_support/core_ext/hash/keys' # This class has dubious semantics and we only have it so that # people can write params[:key] instead of params['key'] # and they get the same value for both keys. module ActiveSupport class HashWithIndifferentAccess < Hash def extractable_options? true end def initialize(constructor = {}) if constructor.is_a?(Hash) super() update(constructor) else super(constructor) end end def default(key = nil) if key.is_a?(Symbol) && include?(key = key.to_s) self[key] else super end end def self.new_from_hash_copying_default(hash) ActiveSupport::HashWithIndifferentAccess.new(hash).tap do |new_hash| new_hash.default = hash.default end end alias_method :regular_writer, :[]= unless method_defined?(:regular_writer) alias_method :regular_update, :update unless method_defined?(:regular_update) # Assigns a new value to the hash: # # hash = HashWithIndifferentAccess.new # hash[:key] = "value" # def []=(key, value) regular_writer(convert_key(key), convert_value(value)) end alias_method :store, :[]= # Updates the instantized hash with values from the second: # # hash_1 = HashWithIndifferentAccess.new # hash_1[:key] = "value" # # hash_2 = HashWithIndifferentAccess.new # hash_2[:key] = "New Value!" # # hash_1.update(hash_2) # => {"key"=>"New Value!"} # def update(other_hash) other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) } self end alias_method :merge!, :update # Checks the hash for a key matching the argument passed in: # # hash = HashWithIndifferentAccess.new # hash["key"] = "value" # hash.key? :key # => true # hash.key? "key" # => true # def key?(key) super(convert_key(key)) end alias_method :include?, :key? alias_method :has_key?, :key? alias_method :member?, :key? # Fetches the value for the specified key, same as doing hash[key] def fetch(key, *extras) super(convert_key(key), *extras) end # Returns an array of the values at the specified indices: # # hash = HashWithIndifferentAccess.new # hash[:a] = "x" # hash[:b] = "y" # hash.values_at("a", "b") # => ["x", "y"] # def values_at(*indices) indices.collect {|key| self[convert_key(key)]} end # Returns an exact copy of the hash. def dup HashWithIndifferentAccess.new(self) end # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash # Does not overwrite the existing hash. def merge(hash) self.dup.update(hash) end # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second. # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a HashWithDifferentAccess. def reverse_merge(other_hash) super self.class.new_from_hash_copying_default(other_hash) end def reverse_merge!(other_hash) replace(reverse_merge( other_hash )) end # Removes a specified key from the hash. def delete(key) super(convert_key(key)) end def stringify_keys!; self end def stringify_keys; dup end undef :symbolize_keys! def symbolize_keys; to_hash.symbolize_keys end def to_options!; self end # Convert to a Hash with String keys. def to_hash Hash.new(default).merge!(self) end protected def convert_key(key) key.kind_of?(Symbol) ? key.to_s : key end def convert_value(value) case value when Hash self.class.new_from_hash_copying_default(value) when Array value.collect { |e| e.is_a?(Hash) ? self.class.new_from_hash_copying_default(e) : e } else value end end end end HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess
好了 所有 实现的原理都在 这个文件里了,HashWithIndifferentAccess是 Hash的子类,其中覆盖了default 方法,Hash当找不到 hash 的 key 时 会寻找default值,即执行 default 方法 , so ….
Hash#default
用法demo:
ruby-1.9.2-p0 > h={} => {} ruby-1.9.2-p0 > h.default=1 => 1 ruby-1.9.2-p0 > h[:a] => 1
核心代码:
def default(key = nil) if key.is_a?(Symbol) && include?(key = key.to_s) self[key] else super end end
当是symbol 时 转化为 string , 然后 self[string_key]
举一反三:
DEMO:
class MyHash < Hash def initialize(constructor = {}) if constructor.is_a?(Hash) super() update(constructor) # Hash#update == Hash#merge! else super(constructor) end end def default(key = nil) p key #=> :id if key.is_a?(Symbol) && include?(key = key.to_s) # Hash#include? = Hash#has_key? = Hash#member? = Hash#key? p key #=> "id" self[key] else super end end end h = MyHash.new({"id"=>1}) p h #=> {"id"=>1} p h.class #=> MyHash p h[:id] #=> 1 p h["id"] #=> 1
SEE: