mutations 0.5.11 → 0.5.12
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +22 -0
- data/Gemfile.lock +1 -1
- data/lib/mutations.rb +2 -0
- data/lib/mutations/array_filter.rb +8 -1
- data/lib/mutations/command.rb +10 -11
- data/lib/mutations/duck_filter.rb +25 -0
- data/lib/mutations/file_filter.rb +32 -0
- data/lib/mutations/hash_filter.rb +8 -1
- data/lib/mutations/integer_filter.rb +6 -2
- data/lib/mutations/version.rb +1 -1
- data/spec/array_filter_spec.rb +18 -0
- data/spec/command_spec.rb +18 -0
- data/spec/duck_filter_spec.rb +49 -0
- data/spec/file_filter_spec.rb +91 -0
- data/spec/hash_filter_spec.rb +21 -1
- data/spec/inheritance_spec.rb +1 -1
- data/spec/integer_filter_spec.rb +15 -1
- metadata +7 -2
data/CHANGELOG.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
0.5.12
|
2
|
+
-----------
|
3
|
+
|
4
|
+
- Added a duck filter: ```duck :lengthy, methods: :length``` to ensure all values respond_to? :length [#14]
|
5
|
+
- Added a file filter: ```file :data``` to ensure the data is a File-like object [#15]
|
6
|
+
- Allow raw_inputs to be used as method inside of execute to access the original data passed in. (@tomtaylor)
|
7
|
+
- integer filter now allows the ```in``` option. Eg: ```integer :http_code, in: (200, 404, 401)``` (/via @tomtaylor)
|
8
|
+
- Added a changelog. [#10]
|
9
|
+
|
10
|
+
0.5.11
|
11
|
+
-----------
|
12
|
+
|
13
|
+
- Float filter (@aq1018)
|
14
|
+
- Clean up public API + code (@blambeau)
|
15
|
+
- Model filters should lazily resolve their classes so that mocks can be used (@edwinv)
|
16
|
+
- Filtered inputs should be included in the Outcome
|
17
|
+
- Fix typos (@frodsan)
|
18
|
+
|
19
|
+
0.5.10 and earlier
|
20
|
+
-----------
|
21
|
+
|
22
|
+
- Initial versions
|
data/Gemfile.lock
CHANGED
data/lib/mutations.rb
CHANGED
@@ -10,6 +10,8 @@ require 'mutations/string_filter'
|
|
10
10
|
require 'mutations/integer_filter'
|
11
11
|
require 'mutations/float_filter'
|
12
12
|
require 'mutations/boolean_filter'
|
13
|
+
require 'mutations/duck_filter'
|
14
|
+
require 'mutations/file_filter'
|
13
15
|
require 'mutations/model_filter'
|
14
16
|
require 'mutations/array_filter'
|
15
17
|
require 'mutations/hash_filter'
|
@@ -34,6 +34,14 @@ module Mutations
|
|
34
34
|
def boolean(options = {})
|
35
35
|
@element_filter = BooleanFilter.new(options)
|
36
36
|
end
|
37
|
+
|
38
|
+
def duck(options = {})
|
39
|
+
@element_filter = DuckFilter.new(options)
|
40
|
+
end
|
41
|
+
|
42
|
+
def file(options = {})
|
43
|
+
@element_filter = FileFilter.new(options)
|
44
|
+
end
|
37
45
|
|
38
46
|
def hash(options = {}, &block)
|
39
47
|
@element_filter = HashFilter.new(options, &block)
|
@@ -87,7 +95,6 @@ module Mutations
|
|
87
95
|
|
88
96
|
# Returns [filtered, errors]
|
89
97
|
def filter_element(data)
|
90
|
-
|
91
98
|
if @element_filter
|
92
99
|
data, el_errors = @element_filter.filter(data)
|
93
100
|
return [data, el_errors] if el_errors
|
data/lib/mutations/command.rb
CHANGED
@@ -6,15 +6,15 @@ module Mutations
|
|
6
6
|
keys = self.input_filters.send("#{meth}_keys")
|
7
7
|
keys.each do |key|
|
8
8
|
define_method(key) do
|
9
|
-
@
|
9
|
+
@inputs[key]
|
10
10
|
end
|
11
11
|
|
12
12
|
define_method("#{key}_present?") do
|
13
|
-
@
|
13
|
+
@inputs.has_key?(key)
|
14
14
|
end
|
15
15
|
|
16
16
|
define_method("#{key}=") do |v|
|
17
|
-
@
|
17
|
+
@inputs[key] = v
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
@@ -55,11 +55,12 @@ module Mutations
|
|
55
55
|
|
56
56
|
# Instance methods
|
57
57
|
def initialize(*args)
|
58
|
-
@
|
58
|
+
@raw_inputs = args.each_with_object({}.with_indifferent_access) do |arg, h|
|
59
59
|
raise ArgumentError.new("All arguments must be hashes") unless arg.is_a?(Hash)
|
60
60
|
h.merge!(arg)
|
61
61
|
end
|
62
|
-
|
62
|
+
|
63
|
+
@inputs, @errors = self.input_filters.filter(@raw_inputs)
|
63
64
|
end
|
64
65
|
|
65
66
|
def input_filters
|
@@ -88,13 +89,14 @@ module Mutations
|
|
88
89
|
validation_outcome
|
89
90
|
end
|
90
91
|
|
91
|
-
# Runs input thru the filter and sets @filtered_input and @errors
|
92
92
|
def validation_outcome(result = nil)
|
93
|
-
Outcome.new(!has_errors?, has_errors? ? nil : result, @errors, @
|
93
|
+
Outcome.new(!has_errors?, has_errors? ? nil : result, @errors, @inputs)
|
94
94
|
end
|
95
95
|
|
96
96
|
protected
|
97
97
|
|
98
|
+
attr_reader :inputs, :raw_inputs
|
99
|
+
|
98
100
|
def execute
|
99
101
|
# Meant to be overridden
|
100
102
|
end
|
@@ -121,8 +123,5 @@ module Mutations
|
|
121
123
|
@errors.merge!(hash)
|
122
124
|
end
|
123
125
|
|
124
|
-
def inputs
|
125
|
-
@filtered_input
|
126
|
-
end
|
127
126
|
end
|
128
|
-
end
|
127
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Mutations
|
2
|
+
class DuckFilter < InputFilter
|
3
|
+
@default_options = {
|
4
|
+
nils: false, # true allows an explicit nil to be valid. Overrides any other options
|
5
|
+
methods: nil # The object needs to respond to each of the symbols in this array.
|
6
|
+
}
|
7
|
+
|
8
|
+
def filter(data)
|
9
|
+
|
10
|
+
# Handle nil case
|
11
|
+
if data.nil?
|
12
|
+
return [nil, nil] if options[:nils]
|
13
|
+
return [nil, :nils]
|
14
|
+
end
|
15
|
+
|
16
|
+
# Ensure the data responds to each of the methods
|
17
|
+
Array(options[:methods]).each do |method|
|
18
|
+
return [data, :duck] unless data.respond_to?(method)
|
19
|
+
end
|
20
|
+
|
21
|
+
# We win, it's valid!
|
22
|
+
[data, nil]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Mutations
|
2
|
+
class FileFilter < InputFilter
|
3
|
+
@default_options = {
|
4
|
+
nils: false, # true allows an explicit nil to be valid. Overrides any other options
|
5
|
+
upload: false, # if true, also checks the file is has original_filename and content_type methods.
|
6
|
+
size: nil # An integer value like 1_000_000 limits the size of the file to 1M bytes
|
7
|
+
}
|
8
|
+
|
9
|
+
def filter(data)
|
10
|
+
|
11
|
+
# Handle nil case
|
12
|
+
if data.nil?
|
13
|
+
return [nil, nil] if options[:nils]
|
14
|
+
return [nil, :nils]
|
15
|
+
end
|
16
|
+
|
17
|
+
# Ensure the data responds to each of the methods
|
18
|
+
methods = [:read, :size]
|
19
|
+
methods.concat([:original_filename, :content_type]) if options[:upload]
|
20
|
+
methods.each do |method|
|
21
|
+
return [data, :file] unless data.respond_to?(method)
|
22
|
+
end
|
23
|
+
|
24
|
+
if options[:size].is_a?(Fixnum)
|
25
|
+
return [data, :size] if data.size > options[:size]
|
26
|
+
end
|
27
|
+
|
28
|
+
# We win, it's valid!
|
29
|
+
[data, nil]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -64,12 +64,19 @@ module Mutations
|
|
64
64
|
def boolean(name, options = {})
|
65
65
|
@current_inputs[name.to_sym] = BooleanFilter.new(options)
|
66
66
|
end
|
67
|
+
|
68
|
+
def duck(name, options = {})
|
69
|
+
@current_inputs[name.to_sym] = DuckFilter.new(options)
|
70
|
+
end
|
71
|
+
|
72
|
+
def file(name, options = {})
|
73
|
+
@current_inputs[name.to_sym] = FileFilter.new(options)
|
74
|
+
end
|
67
75
|
|
68
76
|
def hash(name, options = {}, &block)
|
69
77
|
@current_inputs[name.to_sym] = HashFilter.new(options, &block)
|
70
78
|
end
|
71
79
|
|
72
|
-
# Advanced types
|
73
80
|
def model(name, options = {})
|
74
81
|
name_sym = name.to_sym
|
75
82
|
@current_inputs[name_sym] = ModelFilter.new(name_sym, options)
|
@@ -3,7 +3,8 @@ module Mutations
|
|
3
3
|
@default_options = {
|
4
4
|
nils: false, # true allows an explicit nil to be valid. Overrides any other options
|
5
5
|
min: nil, # lowest value, inclusive
|
6
|
-
max: nil
|
6
|
+
max: nil, # highest value, inclusive
|
7
|
+
in: nil # Can be an array like %w(3 4 5)
|
7
8
|
}
|
8
9
|
|
9
10
|
def filter(data)
|
@@ -26,8 +27,11 @@ module Mutations
|
|
26
27
|
return [data, :min] if options[:min] && data < options[:min]
|
27
28
|
return [data, :max] if options[:max] && data > options[:max]
|
28
29
|
|
30
|
+
# Ensure it matches `in`
|
31
|
+
return [data, :in] if options[:in] && !options[:in].include?(data)
|
32
|
+
|
29
33
|
# We win, it's valid!
|
30
34
|
[data, nil]
|
31
35
|
end
|
32
36
|
end
|
33
|
-
end
|
37
|
+
end
|
data/lib/mutations/version.rb
CHANGED
data/spec/array_filter_spec.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative 'spec_helper'
|
2
|
+
require 'stringio'
|
2
3
|
|
3
4
|
describe "Mutations::ArrayFilter" do
|
4
5
|
|
@@ -91,6 +92,23 @@ describe "Mutations::ArrayFilter" do
|
|
91
92
|
assert_equal [5.0,6.0,1.0,"bob"], filtered
|
92
93
|
assert_equal [nil, nil, :min, :float], errors.symbolic
|
93
94
|
end
|
95
|
+
|
96
|
+
it "lets you pass ducks in arrays" do
|
97
|
+
f = Mutations::ArrayFilter.new(:arr) { duck(methods: :length) }
|
98
|
+
|
99
|
+
filtered, errors = f.filter(["hi", [1], true])
|
100
|
+
assert_equal ["hi", [1], true], filtered
|
101
|
+
assert_equal [nil, nil, :duck], errors.symbolic
|
102
|
+
end
|
103
|
+
|
104
|
+
it "lets you pass files in arrays" do
|
105
|
+
sio = StringIO.new("bob")
|
106
|
+
f = Mutations::ArrayFilter.new(:arr) { file }
|
107
|
+
|
108
|
+
filtered, errors = f.filter([sio, "bob"])
|
109
|
+
assert_equal [sio, "bob"], filtered
|
110
|
+
assert_equal [nil, :file], errors.symbolic
|
111
|
+
end
|
94
112
|
|
95
113
|
it "lets you pass booleans in arrays" do
|
96
114
|
f = Mutations::ArrayFilter.new(:arr) { boolean }
|
data/spec/command_spec.rb
CHANGED
@@ -201,4 +201,22 @@ describe "Command" do
|
|
201
201
|
end
|
202
202
|
end
|
203
203
|
|
204
|
+
describe "RawInputsCommand" do
|
205
|
+
class RawInputsCommand < Mutations::Command
|
206
|
+
|
207
|
+
required do
|
208
|
+
string :name
|
209
|
+
end
|
210
|
+
|
211
|
+
def execute
|
212
|
+
return raw_inputs
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
it "should return the raw input data" do
|
217
|
+
input = { "name" => "Hello World", "other" => "Foo Bar Baz" }
|
218
|
+
assert_equal input, RawInputsCommand.run!(input)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
204
222
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe "Mutations::DuckFilter" do
|
4
|
+
|
5
|
+
it "allows objects that respond to a single specified method" do
|
6
|
+
f = Mutations::DuckFilter.new(methods: [:length])
|
7
|
+
filtered, errors = f.filter("test")
|
8
|
+
assert_equal "test", filtered
|
9
|
+
assert_equal nil, errors
|
10
|
+
|
11
|
+
filtered, errors = f.filter([1, 2])
|
12
|
+
assert_equal [1, 2], filtered
|
13
|
+
assert_equal nil, errors
|
14
|
+
end
|
15
|
+
|
16
|
+
it "doesn't allow objects that respond to a single specified method" do
|
17
|
+
f = Mutations::DuckFilter.new(methods: [:length])
|
18
|
+
filtered, errors = f.filter(true)
|
19
|
+
assert_equal true, filtered
|
20
|
+
assert_equal :duck, errors
|
21
|
+
|
22
|
+
filtered, errors = f.filter(12)
|
23
|
+
assert_equal 12, filtered
|
24
|
+
assert_equal :duck, errors
|
25
|
+
end
|
26
|
+
|
27
|
+
it "considers nil to be invalid" do
|
28
|
+
f = Mutations::DuckFilter.new(nils: false)
|
29
|
+
filtered, errors = f.filter(nil)
|
30
|
+
assert_equal nil, filtered
|
31
|
+
assert_equal :nils, errors
|
32
|
+
end
|
33
|
+
|
34
|
+
it "considers nil to be valid" do
|
35
|
+
f = Mutations::DuckFilter.new(nils: true)
|
36
|
+
filtered, errors = f.filter(nil)
|
37
|
+
assert_equal nil, filtered
|
38
|
+
assert_equal nil, errors
|
39
|
+
end
|
40
|
+
|
41
|
+
it "Allows anything if no methods are specified" do
|
42
|
+
f = Mutations::DuckFilter.new
|
43
|
+
[true, "hi", 1, [1, 2, 3]].each do |v|
|
44
|
+
filtered, errors = f.filter(v)
|
45
|
+
assert_equal v, filtered
|
46
|
+
assert_equal nil, errors
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require 'stringio'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
describe "Mutations::FileFilter" do
|
6
|
+
|
7
|
+
class UploadedStringIO < StringIO
|
8
|
+
attr_accessor :content_type, :original_filename
|
9
|
+
end
|
10
|
+
|
11
|
+
it "allows files - file class" do
|
12
|
+
file = File.new("README.md")
|
13
|
+
f = Mutations::FileFilter.new
|
14
|
+
filtered, errors = f.filter(file)
|
15
|
+
assert_equal file, filtered
|
16
|
+
assert_equal nil, errors
|
17
|
+
end
|
18
|
+
|
19
|
+
it "allows files - stringio class" do
|
20
|
+
file = StringIO.new("bob")
|
21
|
+
f = Mutations::FileFilter.new
|
22
|
+
filtered, errors = f.filter(file)
|
23
|
+
assert_equal file, filtered
|
24
|
+
assert_equal nil, errors
|
25
|
+
end
|
26
|
+
|
27
|
+
it "allows files - tempfile" do
|
28
|
+
file = Tempfile.new("bob")
|
29
|
+
f = Mutations::FileFilter.new
|
30
|
+
filtered, errors = f.filter(file)
|
31
|
+
assert_equal file, filtered
|
32
|
+
assert_equal nil, errors
|
33
|
+
end
|
34
|
+
|
35
|
+
it "doesn't allow non-files" do
|
36
|
+
f = Mutations::FileFilter.new
|
37
|
+
filtered, errors = f.filter("string")
|
38
|
+
assert_equal "string", filtered
|
39
|
+
assert_equal :file, errors
|
40
|
+
|
41
|
+
filtered, errors = f.filter(12)
|
42
|
+
assert_equal 12, filtered
|
43
|
+
assert_equal :file, errors
|
44
|
+
end
|
45
|
+
|
46
|
+
it "considers nil to be invalid" do
|
47
|
+
f = Mutations::FileFilter.new(nils: false)
|
48
|
+
filtered, errors = f.filter(nil)
|
49
|
+
assert_equal nil, filtered
|
50
|
+
assert_equal :nils, errors
|
51
|
+
end
|
52
|
+
|
53
|
+
it "considers nil to be valid" do
|
54
|
+
f = Mutations::FileFilter.new(nils: true)
|
55
|
+
filtered, errors = f.filter(nil)
|
56
|
+
assert_equal nil, filtered
|
57
|
+
assert_equal nil, errors
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should allow small files" do
|
61
|
+
file = StringIO.new("bob")
|
62
|
+
f = Mutations::FileFilter.new(size: 4)
|
63
|
+
filtered, errors = f.filter(file)
|
64
|
+
assert_equal file, filtered
|
65
|
+
assert_equal nil, errors
|
66
|
+
end
|
67
|
+
|
68
|
+
it "shouldn't allow big files" do
|
69
|
+
file = StringIO.new("bob")
|
70
|
+
f = Mutations::FileFilter.new(size: 2)
|
71
|
+
filtered, errors = f.filter(file)
|
72
|
+
assert_equal file, filtered
|
73
|
+
assert_equal :size, errors
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should require extra methods if uploaded file: accept" do
|
77
|
+
file = UploadedStringIO.new("bob")
|
78
|
+
f = Mutations::FileFilter.new(upload: true)
|
79
|
+
filtered, errors = f.filter(file)
|
80
|
+
assert_equal file, filtered
|
81
|
+
assert_equal nil, errors
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should require extra methods if uploaded file: deny" do
|
85
|
+
file = StringIO.new("bob")
|
86
|
+
f = Mutations::FileFilter.new(upload: true)
|
87
|
+
filtered, errors = f.filter(file)
|
88
|
+
assert_equal file, filtered
|
89
|
+
assert_equal :file, errors
|
90
|
+
end
|
91
|
+
end
|
data/spec/hash_filter_spec.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative 'spec_helper'
|
2
|
+
require 'stringio'
|
2
3
|
|
3
4
|
describe "Mutations::HashFilter" do
|
4
5
|
|
@@ -28,7 +29,7 @@ describe "Mutations::HashFilter" do
|
|
28
29
|
assert_equal nil, errors
|
29
30
|
end
|
30
31
|
|
31
|
-
it "
|
32
|
+
it "allows floats in hashes" do
|
32
33
|
hf = Mutations::HashFilter.new do
|
33
34
|
float :foo
|
34
35
|
end
|
@@ -36,6 +37,25 @@ describe "Mutations::HashFilter" do
|
|
36
37
|
assert_equal ({"foo" => 3.14}), filtered
|
37
38
|
assert_equal nil, errors
|
38
39
|
end
|
40
|
+
|
41
|
+
it "allows ducks in hashes" do
|
42
|
+
hf = Mutations::HashFilter.new do
|
43
|
+
duck :foo, methods: [:length]
|
44
|
+
end
|
45
|
+
filtered, errors = hf.filter(foo: "123")
|
46
|
+
assert_equal ({"foo" => "123"}), filtered
|
47
|
+
assert_equal nil, errors
|
48
|
+
end
|
49
|
+
|
50
|
+
it "allows files in hashes" do
|
51
|
+
sio = StringIO.new("bob")
|
52
|
+
hf = Mutations::HashFilter.new do
|
53
|
+
file :foo
|
54
|
+
end
|
55
|
+
filtered, errors = hf.filter(foo: sio)
|
56
|
+
assert_equal ({"foo" => sio}), filtered
|
57
|
+
assert_equal nil, errors
|
58
|
+
end
|
39
59
|
|
40
60
|
it "doesn't allow wildcards in hashes" do
|
41
61
|
hf = Mutations::HashFilter.new do
|
data/spec/inheritance_spec.rb
CHANGED
data/spec/integer_filter_spec.rb
CHANGED
@@ -73,4 +73,18 @@ describe "Mutations::IntegerFilter" do
|
|
73
73
|
assert_equal nil, errors
|
74
74
|
end
|
75
75
|
|
76
|
-
|
76
|
+
it "considers not matching numbers to be invalid" do
|
77
|
+
f = Mutations::IntegerFilter.new(in: [3, 4, 5])
|
78
|
+
filtered, errors = f.filter(6)
|
79
|
+
assert_equal 6, filtered
|
80
|
+
assert_equal :in, errors
|
81
|
+
end
|
82
|
+
|
83
|
+
it "considers matching numbers to be valid" do
|
84
|
+
f = Mutations::IntegerFilter.new(in: [3, 4, 5])
|
85
|
+
filtered, errors = f.filter(3)
|
86
|
+
assert_equal 3, filtered
|
87
|
+
assert_nil errors
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mutations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.12
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-03-
|
12
|
+
date: 2013-03-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -68,6 +68,7 @@ extra_rdoc_files: []
|
|
68
68
|
files:
|
69
69
|
- .gitignore
|
70
70
|
- .travis
|
71
|
+
- CHANGELOG.md
|
71
72
|
- Gemfile
|
72
73
|
- Gemfile.lock
|
73
74
|
- MIT-LICENSE
|
@@ -78,8 +79,10 @@ files:
|
|
78
79
|
- lib/mutations/array_filter.rb
|
79
80
|
- lib/mutations/boolean_filter.rb
|
80
81
|
- lib/mutations/command.rb
|
82
|
+
- lib/mutations/duck_filter.rb
|
81
83
|
- lib/mutations/errors.rb
|
82
84
|
- lib/mutations/exception.rb
|
85
|
+
- lib/mutations/file_filter.rb
|
83
86
|
- lib/mutations/float_filter.rb
|
84
87
|
- lib/mutations/hash_filter.rb
|
85
88
|
- lib/mutations/input_filter.rb
|
@@ -93,7 +96,9 @@ files:
|
|
93
96
|
- spec/boolean_filter_spec.rb
|
94
97
|
- spec/command_spec.rb
|
95
98
|
- spec/default_spec.rb
|
99
|
+
- spec/duck_filter_spec.rb
|
96
100
|
- spec/errors_spec.rb
|
101
|
+
- spec/file_filter_spec.rb
|
97
102
|
- spec/float_filter_spec.rb
|
98
103
|
- spec/hash_filter_spec.rb
|
99
104
|
- spec/inheritance_spec.rb
|