• 非常感谢!

  • 非常感谢!

  • 初步深入 Rack (一) at 2015年02月10日

    多谢分享!

  • 确实不错,真学到知识了,如果广告都这样,社会就更和谐了,哈,顶下!

  • module D
      puts self.class    # 输出 Module
      class << self
        puts self.class   # 输出  Class
        def say_hello_of_d
          puts 'D say hello'
        end
      end
    end
    

    Module 的 singleton_class 就是 Class,不能 include 或 extend 到一个 class 中。 我能想到的就是用 delegate, 不过太难看了,不确定是否有问题。

    require 'delegate'
    
    module D  
      class << self
        def say_hello_of_d
          puts 'D say hello'
        end
      end
    end
    
    class Y < DelegateClass(D.singleton_class)
      def initialize
        super(D)
      end
    end
    
    y = Y.new
    y.say_hello_of_d
    
    
  • 谢谢楼主的感悟和分享,加油!

  • 好东西,谢谢分享!

  • Rails 路由系统源码探索 at 2014年11月20日

    多谢!!

  • Ruby 赋值中的一个坑 at 2014年10月26日

    @chiangdi a, = 3, 5 如同 a, _ = 3, 5 告诉 ruby,赋值号左边有类似给多个变量赋值的行为,即不把 [3,5] 赋给 a, 只把 3 赋值给 a

    这些在 <>书中有讲。

  • Ruby 赋值中的一个坑 at 2014年10月26日

    在 c/c++ 里 a = 3, b = 5 是可以的,中间的逗号,就是逗号运算符,

    在 ruby 中 a = 3, b = 5 ruby 并没有逗号运算符,这个表达式就相当于 a = 3, (b = 5), 也就是 a = 3, 5 ruby 的赋值操作有这样的特性,左边一个变量,右边多个值时,ruby 会创建一个包含右边值的数组,并赋值给左边的变量。

    可以试试 a, = 3, b = 5 p a, b

    当然不建议这么做。

  • 多谢!

  • 用 TracePoint 可以监听:return, :raise 事件,当发生异常时,会监听到:raise 事件,接着是:return 事件,利用这一特性,可以基本实现获取每一层栈帧的局部变量,代码如下:

    class LocalVariablesTracer
    
      class << self
        attr_accessor :raised_exception
    
        def start
          @raised_exception = nil
          _define_dump_func
    
          TracePoint.trace(:return, :raise) do |tp|
            case tp.event
            when :raise
              @raised_exception = tp.raised_exception
            when :return
              if @raised_exception
                @raised_exception.instance_eval {
                  @func_bindings ||= {}
                  @func_bindings[tp.method_id] = tp.binding
                }
              end
            end
          end
        end
    
        private
        def _define_dump_func
          Exception.class_eval do
            def dump_local_variables(out = $stderr)
              if @func_bindings
                backtrace.grep(/\`(\w+)\'/) do
                  func = $1.to_sym
                  b = @func_bindings[func]
                  local_variables = b.eval("local_variables").map { |var|
                    %/#{var}: #{b.eval("#{var}")}/
                  }
                  out.puts "#{func}: #{local_variables}"
                  LocalVariablesTracer.raised_exception = nil
                end
              end
            end
          end
        end
    
      end
    
    end
    

    使用例子:

    LocalVariablesTracer.start
    
    def func
      a = 1
      b = 1
      raise "hello"
    end
    
    def func1
      name = "xzgyb"
      msgs = ["hello", "world"]
      func
    end
    
    begin
      func1
    rescue Exception => ex
      ex.dump_local_variables
    end
    
  • -@ 是一元负号操作符,用于在类中定义一元负号操作符的实现, 可以看看 puts 1.-@ puts -1.-@

  • 多谢,收藏!

  • Writings 开源 at 2013年09月29日

    感谢分享,学习

  • if 'bar'.in?(params[:foo]) ... end rescue nil

  • 我使用的 Rails Gems 列表 at 2012年08月29日

    感谢!

  • 对 ruby 爱好者来说,这太好了,感谢!

    1. linux 系的平台下,shell 会进行通配符的展开,和 ruby 没关系。

    2. windows 下,cmd 并不会进行通配符的展开,ruby 为了保持和 linux 的行为一致,做了 glob 处理, 参见源码 (ruby.c):

      void
      ruby_sysinit(int *argc, char ***argv)
      {
      #if defined(_WIN32)
      void rb_w32_sysinit(int *argc, char ***argv);
      rb_w32_sysinit(argc, argv);
      #endif
      ...
      }
      

      其中的 rb_w32_sysinit(argc, argv) 会调用 rb_w32_cmdvector(GetCommandLine(), argv);进行参数的 glob 处理。

    且 ruby 没有提供什么选项,也无法提供,因为 linux 的 shell 缘故。

    确实没招了,只能转义。

  • a1.class.send(:define_method, :m) { puts self }

    1. 这个中间表,是 rails 内部用于实现多对多关联使用的 join table, 也不需要用页面来管理吧,用不着搞个 scaffold.

    2. 当提示存在时,是否覆盖,按提示来吧,选 n 不覆盖,让 rails 只创建新的文件, 完事后,在 controller 和 controller_test 中在补上相应的代码。

    或者直接手工创建相应的文件,也不是什么麻烦事。

    我觉得 rails generate 只是给了我们一个起点,就像一些 ide 的向导一样, 剩下的是我们自己的选择,但不能完全依赖它。

  • require 的问题 at 2012年04月04日

    @skandhas : 呵呵,!!! ~_~ !!!

  • require 的问题 at 2012年04月04日

    ruby1.9 之前是可以的,ruby1.9 之后,好像是因为安全问题吧,具体不太清楚, 另 require_relative 也是可用的。

  • @imsoz, 不客气。

    CALL、FCALL、VCALL 这几个,是属于 ruby 内部实现机制,

    ruby1.9.3 p0 compile.c:4050

      case NODE_CALL:
      case NODE_FCALL:
      case NODE_VCALL:{     /* VCALL: variable or call */
    /*
      call:  obj.method(...)
      fcall: func(...)
      vcall: func
    */
    
    

    这个纯属个人爱好,因为看到过,所以这里说上几句,不好意思,希望不会误导到你。

    因为对于 private 为什么会是这样的设计,我也疑惑过,所以这里在多说一下, 权当做参考。

    在全局作用域(也就是一般说的 toplevel)中定义的方法,在某个类中可以直接调用, 类似其他的语言中的全局函数的效果,原因就是初始脚本运行时,ruby vm 默认打开了 Object 类,并且设置方法的默认可见性为 private, 则在 toplevel 中定义的方法都是 Object 的方法且是 private 的, 由于 ruby 中的一切都是从 Object 继承的, 故 ruby 对于 private 方法的这一设计,使得在任何类的方法中, 都可以用这种类似全局函数调用的形式来进行调用。

    另外附带说一句, kernel 中的 load 函数 load( file_name, wrap=false )

    如果 wrap 设为 true, 就是使得 ruby vm 默认不是用 Object,而是创建一个匿名的 module, 目的是不让它污染 toplevel 的名字空间。

    另外同意@skandhas 的说法,人生苦短,ruby 的宗旨就是让程序员快乐的编程。

  • 在 ruby 中方法调用形式分为

    1. CALL 带 self 的方法调用,如 self.func('a')
    2. FCALL 不带 self 的方法调用,如 func('a')
    3. VCALL 没有参数的 FCALL, 如 func

    对于 FCALL 和 VCALL,ruby 并不校验该方法是否 private,这也正是实现了那种调用全局函数的效果,比如:

    def global_func
      puts 'global_func'
    end
    
    class A
      def method
        global_func  # 如果使用self.global_func, 则提示global_func为private
      end
    end
    
    a = A.new
    a.method
    
    
  • 统计中文字符串长度 at 2012年04月03日

    不客气

  • 统计中文字符串长度 at 2012年04月03日

    str.split(//u).size, 应该是 ruby1.8 的时候,String 内部就是 ascii 编码, ruby1.9,String 可以原生支持多种编码了, 只要你的源文件使用 utf-8 编码, size 和 length,算出来的长度就是正确的字符个数 bytesize 是字节数

  • 来个不一样的,仿照 Agile Web Development with Rails

    product.rb

    class Product
        attr_accessor  :name, :unit_price, :pack_count, :pack_price
    
        def initialize(name, unit_price, pack_count, pack_price)
            @name, @unit_price, @pack_count, @pack_price = 
                name, unit_price, pack_count, pack_price
        end
    
        def ==(other)
            @name == other.name
        end
    
        def self.products_data
            @@products_data ||= {}
        end
    
        def self.add(name, unit_price, pack_count = nil, pack_price = nil)
            self.products_data[name] = Product.new(name, unit_price, pack_count, pack_price)
        end
    
        def self.find_by_name(name)     
          self.products_data[name]
        end
    end
    

    cart_item.rb

    require './product'
    
    class CartItem
      attr_reader :product, :quantity
    
      def initialize(product)   
         @product  = product
         @quantity = 1
       end
    
        def increment_quantity
            @quantity += 1
        end
    
        def price
            if @product.pack_price.nil?
                @product.unit_price * @quantity
            else
                pack_items_count   = @quantity / @product.pack_count
                rest_quantity = @quantity - pack_items_count * @product.pack_count
    
                @product.pack_price * pack_items_count + 
                  @product.unit_price * rest_quantity
            end     
        end 
    end
    
    

    cart.rb

    require './cart_item'
    
    class Cart
        attr_reader :items
    
        def initialize
            @items = []
        end
    
        def add_product(product)
            current_item = @items.find { |item| item.product == product }
            if current_item
                current_item.increment_quantity
            else
                @items << CartItem.new(product)
            end
        end
    
        def total_cost
            @items.inject(0) { |sum, item| sum += item.price}
        end
    
    end
    

    terminal.rb

    require './product'
    require './cart'
    
    class Terminal
        def initialize
            @cart = Cart.new
        end
    
        def set_price(params)
            Product.add(params[:name],
                        params[:unit_price],
                        params[:pack_count],
                        params[:pack_price])
        end
    
        def scan(p_code)
            product = Product.find_by_name(p_code)
    
            raise "Product #{p_code} not found!" if product.nil?
    
            @cart.add_product(product)
        end
    
        def total_cost
            @cart.total_cost
        end
    end
    
    if $0 == __FILE__
        terminal = Terminal.new
    
        terminal.set_price(name:'A', unit_price: 2, pack_count: 4, pack_price: 7)
        terminal.set_price(name:'B', unit_price: 12)
        terminal.set_price(name:'C', unit_price: 1.25, pack_count: 6, pack_price: 6)
        terminal.set_price(name:'D', unit_price: 0.15)
    
        terminal.scan('A')
        terminal.scan('A')
        terminal.scan('A')
        terminal.scan('A')
        terminal.scan('B')
        terminal.scan('C')
    
        puts "Total cost #{terminal.total_cost}"
    end