美文网首页
自己写一个迷你的DSL

自己写一个迷你的DSL

作者: 33d8e4ec2cc9 | 来源:发表于2017-04-02 14:12 被阅读92次

    我想要达到的效果是这样的

    class Foo
      redis_cache 10 do
        def foo
          'bar'
        end
    
        def bar
          'foo'
        end
      end
    end
    

    被包裹在redis_cache里面的方法会在第一次执行后存入redis, 在第二次执行该方法的时候,去redis里面取,并且根据传入的秒数来设定缓存周期,以起到缓存的效果,老实讲是没什么必要这么弄,但是你们难道对如何实现不感兴趣吗?

    感兴趣的同学往下看

    思路

    我们首先要熟悉ruby祖先链相关的知识,当一个实例执行一个方法的时候,是怎么样的一个流程,实例对象中没有方法,只有变量,实例执行方法的时候,先向右一步,到自己的类中找,假如没有找到,会继续往上,到该类的超类里面找,直到Object为止,还没找到的话,从一开始的类开始找名叫method_missing的方法,假如还是没有,会在Object这里找到一个内置的method_missing,这个方法没什么内容,就是抛出一个undifine_method xxx for xxx。。。
    不过这一期的内容用不到这些知识

    你们有没有想过,当一个类混入一个模块(include Foo)后,类里面的实例方法,和模块中同名的实例方法,哪个会被优先调用,答案是,优先执行类里面的,再是模块里面的,这个可以自己去做个试验,看到这里,如何实现这个简单的DSL就很明朗了,我们首先把redis_cache里面的block,拿到后跑到该类的超类中写一个方法,然后再在自己的类中建一个同名方法,用super来实现上下层的方法的交互,但是我们写进超类里面会有一个问题,方法有可能会被写进Object,这样会产生一个问题,其他的任何类都会拥有这个方法,这不是我们想要的效果,那利用我们刚说的第二点,把方法写进一个模块,然后include,就可以了

    初步的代码是这样的

    class Object
      def redis_cache seconds=300, &block
        top_mod = self.const_set(:TopMethods, Module.new)
        top_mod.module_eval &block
        include top_mod
    
        top_mod.instance_methods.each do |m|
          self.class_eval <<-CODE
            def #{m}(*args)
              puts 'in bottom'
              super
            end
          CODE
        end
      end
    end
    
    class Foo
      redis_cache do
        def bar
          'it works!'
        end
      end
    end
    
    aa = Foo.new
    aa.bar
    #=> in bottom
    #=> 'it works!'
    

    后面如何继续实现,相信大家都有思路了

    相关文章

      网友评论

          本文标题:自己写一个迷你的DSL

          本文链接:https://www.haomeiwen.com/subject/zldgottx.html