Module: NinjaDecorators::ClassMethods

Defined in:
lib/ninja_decorators.rb

Instance Method Summary collapse

Instance Method Details

#after_filter(after_method, method_names) ⇒ Object



33
34
35
# File 'lib/ninja_decorators.rb', line 33

def after_filter(after_method, method_names)
  filter_factory(after_method, method_names, :after)
end

#around_filter(around_method, method_names) ⇒ Object



25
26
27
# File 'lib/ninja_decorators.rb', line 25

def around_filter(around_method, method_names)
  filter_factory(around_method, method_names, :around)
end

#before_filter(before_method, method_names) ⇒ Object



29
30
31
# File 'lib/ninja_decorators.rb', line 29

def before_filter(before_method, method_names)
  filter_factory(before_method, method_names, :before)
end

#delayed_alias_method_chainsObject



10
11
12
# File 'lib/ninja_decorators.rb', line 10

def delayed_alias_method_chains
  @delayed_alias_method_chains ||= {}
end

#filter_factory(filter_method, method_names, filter_type) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/ninja_decorators.rb', line 37

def filter_factory(filter_method, method_names, filter_type)
  method_names.each do |meth|

    # Build up a proc that will construct the filtered method.  Execution of the proc is delayed
    # until we encounter the alias_method_chain call.
    filtered_method_builder = Proc.new do
      # Get a reference to the unfiltered method or, more accurately, the original method with
      # all previous filters already applied.  This new filtered method builds up on the filters
      # already applied.
      unfiltered_method = instance_method "#{meth}_without_#{filter_type.to_s}_filter_wrapper"

      # Define the newly filtered method.
      case filter_type
        when :before
          define_method("#{meth}_with_before_filter_wrapper") do |*args|
            send(filter_method, *args)
            unfiltered_method.bind(self).call(*args)
          end

        when :around
          define_method("#{meth}_with_around_filter_wrapper") do |*args|
            send(filter_method, *args) do |*ar_args|
              unfiltered_method.bind(self).call(*ar_args)
            end
          end

        when :after
          define_method("#{meth}_with_after_filter_wrapper") do |*args|
            unfiltered_method.bind(self).call(*args)
            send(filter_method, *args)
          end
      end
    end

    # If the method to filter has been defined already.
    if self.instance_methods.include?(meth.to_s)

      # Filter the method with before_method.
      ninja_method_chain meth, "#{filter_type.to_s}_filter_wrapper", &filtered_method_builder

    # If the method to filter has not been defined already, delay wrapping until it has.
    else
      delayed_alias_method_chains[meth.to_s] ||= []
      delayed_alias_method_chains[meth.to_s] << {"#{filter_type.to_s}_filter_wrapper" => filtered_method_builder}
    end
  end
end

#method_added(meth) ⇒ Object



14
15
16
17
18
19
20
21
22
23
# File 'lib/ninja_decorators.rb', line 14

def method_added(meth)
  if delayed_alias_method_chains[meth.to_s]
    chains_arr = delayed_alias_method_chains.delete(meth.to_s)
    chains_arr.each do |chain|
      chain.each_pair do |filter_type, filtered_method_builder|
        ninja_method_chain meth, filter_type, &filtered_method_builder
      end
    end
  end
end

#ninja_method_chain(target, feature) ⇒ Object

This was largely lifted from ActiveSupport’s alias_method_chain. We needed to be able to yield to a block that could construct the with_* methods while having access to the aliased without_* methods.



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/ninja_decorators.rb', line 87

def ninja_method_chain(target, feature)
  # Strip out punctuation on predicates or bang methods since
  # e.g. target?_without_feature is not a valid method name.
  aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1

  with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}"

  alias_method without_method, target

  yield if block_given?

  alias_method target, with_method

  case
  when public_method_defined?(without_method)
    public target
  when protected_method_defined?(without_method)
    protected target
  when private_method_defined?(without_method)
    private target
  end
end