Repository: Shopify/liquid (Ruby)

Issue:
duplicate inclusion condition logic error of Liquid::Strainer.add_filter  method  
In `Liquid::Strainer `class,  method `add_filter` has logic to avoid duplicate inclusion of the same filter module. But its judgement condition seems be wrong.

[ current codes](https://github.com/Shopify/liquid/blob/master/lib/liquid/strainer.rb#L30)
```ruby
def self.add_filter(filter)
      raise ArgumentError, "Expected module but got: #{filter.class}" unless filter.is_a?(Module)
      unless self.class.include?(filter)
        invokable_non_public_methods = (filter.private_instance_methods + filter.protected_instance_methods).select { |m| invokable?(m) }
        if invokable_non_public_methods.any?
          raise MethodOverrideError, "Filter overrides registered public methods as non public: #{invokable_non_public_methods.join(', ')}"
        else
          send(:include, filter)
          @filter_methods.merge(filter.public_instance_methods.map(&:to_s))
        end
      end
    end
```

In the 3th line ` unless self.class.include?(filter)>` , since method `add_filter` is a class method, the result of  ` self.class` is   not `Strainer` (or Strainer child class)  but `Class` , which is not we expect.  

[test case](https://github.com/chenxianyu2015/liquid/blob/aa79c33dda95b3b7ab2812e30db191a113763516/test/unit/strainer_unit_test.rb#L149)
```ruby
  def test_add_filter_returns_nil_when_a_filter_module_was_added
    a = Module.new
    strainer = Context.new.strainer
    result1 = strainer.class.add_filter(a)
    result2 = strainer.class.add_filter(a)
    assert_kind_of Set, result1
    assert_kind_of NilClass, result2
  end
```

[fix:](https://github.com/chenxianyu2015/liquid/blob/aa79c33dda95b3b7ab2812e30db191a113763516/lib/liquid/strainer.rb#L30)
```ruby
    def self.add_filter(filter)
      raise ArgumentError, "Expected module but got: #{filter.class}" unless filter.is_a?(Module)
      unless self.include?(filter)
        invokable_non_public_methods = (filter.private_instance_methods + filter.protected_instance_methods).select { |m| invokable?(m) }
        if invokable_non_public_methods.any?
          raise MethodOverrideError, "Filter overrides registered public methods as non public: #{invokable_non_public_methods.join(', ')}"
        else
          send(:include, filter)
          @filter_methods.merge(filter.public_instance_methods.map(&:to_s))
        end
      end
    end
```



Discussion:
Thanks for reporting this. Can you make a PR?



Patch:
diff --git a/lib/liquid/strainer.rb b/lib/liquid/strainer.rb
index 99a9c29d5..76d56d231 100644
--- a/lib/liquid/strainer.rb
+++ b/lib/liquid/strainer.rb
@@ -27,7 +27,7 @@ class << self
 
     def self.add_filter(filter)
       raise ArgumentError, "Expected module but got: #{filter.class}" unless filter.is_a?(Module)
-      unless self.class.include?(filter)
+      unless self.include?(filter)
         invokable_non_public_methods = (filter.private_instance_methods + filter.protected_instance_methods).select { |m| invokable?(m) }
         if invokable_non_public_methods.any?
           raise MethodOverrideError, "Filter overrides registered public methods as non public: #{invokable_non_public_methods.join(', ')}"


Test patch:
diff --git a/test/unit/strainer_unit_test.rb b/test/unit/strainer_unit_test.rb
index 6ccdc278a..5ce210041 100644
--- a/test/unit/strainer_unit_test.rb
+++ b/test/unit/strainer_unit_test.rb
@@ -145,4 +145,20 @@ def test_global_filter_clears_cache
     Strainer.global_filter(LateAddedFilter)
     assert_equal 'filtered', Strainer.create(nil).invoke('late_added_filter', 'input')
   end
+
+  def test_add_filter_does_not_include_already_included_module
+    mod = Module.new do
+      class << self
+        attr_accessor :include_count
+        def included(mod)
+          self.include_count += 1
+        end
+      end
+      self.include_count = 0
+    end
+    strainer = Context.new.strainer
+    strainer.class.add_filter(mod)
+    strainer.class.add_filter(mod)
+    assert_equal 1, mod.include_count
+  end
 end # StrainerTest