codeless_code 0.1.7 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +1 -1
  3. data/.reek.yml +14 -0
  4. data/.rubocop.yml +18 -0
  5. data/.ruby-version +1 -0
  6. data/{test/test_codeless_code.rb → .travis.yml} +12 -10
  7. data/Gemfile +5 -0
  8. data/Gemfile.lock +21 -0
  9. data/Guardfile +13 -1
  10. data/README.md +6 -0
  11. data/Rakefile +26 -11
  12. data/VERSION +1 -1
  13. data/bin/codeless_code +3 -1
  14. data/codeless_code.gemspec +33 -12
  15. data/lib/codeless_code/catalog.rb +3 -1
  16. data/lib/codeless_code/cli.rb +56 -24
  17. data/lib/codeless_code/commands/filter_fables.rb +15 -10
  18. data/lib/codeless_code/commands/list_translations.rb +3 -0
  19. data/lib/codeless_code/fable.rb +42 -53
  20. data/lib/codeless_code/fable_set.rb +8 -6
  21. data/lib/codeless_code/filters/builders.rb +4 -2
  22. data/lib/codeless_code/filters/composite.rb +3 -1
  23. data/lib/codeless_code/filters/date.rb +40 -30
  24. data/lib/codeless_code/filters/from_options.rb +45 -28
  25. data/lib/codeless_code/filters/headers/base.rb +61 -0
  26. data/lib/codeless_code/filters/{header_integer.rb → headers/integer.rb} +16 -19
  27. data/lib/codeless_code/filters/{header_string.rb → headers/string.rb} +12 -22
  28. data/lib/codeless_code/filters/lang.rb +3 -0
  29. data/lib/codeless_code/filters/translator.rb +7 -2
  30. data/lib/codeless_code/filters.rb +16 -8
  31. data/lib/codeless_code/formats/base.rb +20 -7
  32. data/lib/codeless_code/formats/parsers/base.rb +106 -0
  33. data/lib/codeless_code/formats/parsers/plain.rb +49 -0
  34. data/lib/codeless_code/formats/parsers/term.rb +79 -0
  35. data/lib/codeless_code/formats/plain.rb +21 -12
  36. data/lib/codeless_code/formats/raw.rb +2 -0
  37. data/lib/codeless_code/formats/term.rb +21 -15
  38. data/lib/codeless_code/language_set.rb +7 -4
  39. data/lib/codeless_code/options.rb +5 -3
  40. data/lib/codeless_code/renderers/fable.rb +13 -8
  41. data/lib/codeless_code/renderers/term_page.rb +46 -31
  42. data/lib/codeless_code.rb +119 -106
  43. data/test/codeless_code/commands/test_filter_fables.rb +89 -0
  44. data/test/codeless_code/filters/headers/test_integer.rb +86 -0
  45. data/test/codeless_code/filters/headers/test_string.rb +86 -0
  46. data/test/codeless_code/filters/test_builders.rb +4 -2
  47. data/test/codeless_code/filters/test_composite.rb +70 -0
  48. data/test/codeless_code/filters/test_date.rb +99 -0
  49. data/test/codeless_code/filters/test_langs.rb +50 -0
  50. data/test/codeless_code/filters/test_translator.rb +51 -0
  51. data/test/codeless_code/renderers/test_fable.rb +98 -0
  52. data/test/codeless_code/renderers/test_term_page.rb +87 -0
  53. data/test/codeless_code/test_catalog.rb +12 -5
  54. data/test/codeless_code/test_cli.rb +85 -0
  55. data/test/codeless_code/test_fable.rb +19 -10
  56. data/test/codeless_code/test_fable_set.rb +17 -5
  57. data/test/codeless_code/test_language_set.rb +16 -3
  58. data/test/codeless_code/test_options.rb +3 -11
  59. data/test/helper.rb +36 -10
  60. data/test/support/fs.rb +103 -0
  61. metadata +65 -11
  62. data/.document +0 -5
  63. data/data/README.md +0 -34
  64. data/lib/codeless_code/formats/plain_generator.rb +0 -157
  65. data/lib/codeless_code/formats/term_generator.rb +0 -167
  66. data/test/codeless_code/filters/test_header_integer.rb +0 -82
  67. data/test/codeless_code/filters/test_header_string.rb +0 -82
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # codeless_code filters and prints fables from http://thecodelesscode.com
2
4
  # Copyright (C) 2018 Jon Sangster
3
5
  #
@@ -23,27 +25,43 @@ module CodelessCode
23
25
 
24
26
  HEADER_PATTERN = /([^:\s]+)\s*:\s*(.+)\s*$/.freeze
25
27
 
26
- attr_reader :file, :read_headers
28
+ RAW_ATTRS = %w[Title Tagline Credits].freeze
29
+ INTEGER_ATTRS = %w[Number Geekiness].freeze
30
+ LIST_ATTRS = %w[Names Topics].freeze
31
+
32
+ attr_reader :file, :has_read_headers
27
33
 
28
- alias_method :read_headers?, :read_headers
34
+ alias read_headers? has_read_headers
29
35
  def_delegators :headers, :[], :fetch, :key?
30
36
 
31
37
  def initialize(file)
32
38
  @file = file
33
- @read_headers = false
34
- @body_pos = nil
39
+ @has_read_headers = false
40
+ @body_pos = 0
35
41
  end
36
42
 
37
43
  # @return [String] the actual story, including MediaWiki markup
38
44
  def body
39
45
  @body ||= read_body.freeze
40
46
  end
41
- alias_method :to_s, :body
47
+ alias to_s body
48
+
49
+ # Read, or re-read, the body of the fable from the disk
50
+ # @see #body
51
+ def read_body
52
+ headers unless read_headers?
53
+
54
+ io = file.open
55
+ io.seek @body_pos
56
+ io.read.strip
57
+ ensure
58
+ io&.close
59
+ end
42
60
 
43
61
  # @return [Hash<String, String>] the story's metadata
44
62
  def headers
45
63
  @headers ||= begin
46
- @read_headers = true
64
+ @has_read_headers = true
47
65
  read_headers.freeze
48
66
  end
49
67
  end
@@ -52,11 +70,6 @@ module CodelessCode
52
70
  headers.key?(key)
53
71
  end
54
72
 
55
- # @return [::Date, nil]
56
- def date
57
- ::Date.parse(self['Date']) if header?('Date')
58
- end
59
-
60
73
  # @return [Symbol]
61
74
  def lang
62
75
  @lang ||= dir_parts.first.to_sym
@@ -66,70 +79,46 @@ module CodelessCode
66
79
  @translator ||= dir_parts.last
67
80
  end
68
81
 
69
- def title
70
- self['Title']
71
- end
72
-
73
- def tagline
74
- self['Tagline']
82
+ RAW_ATTRS.each do |attr|
83
+ define_method(attr.downcase.to_sym) { self[attr] }
75
84
  end
76
85
 
77
- def credits
78
- self['Credits']
86
+ INTEGER_ATTRS.each do |attr|
87
+ define_method(attr.downcase.to_sym) { self[attr].to_i }
79
88
  end
80
89
 
81
- def number
82
- self['Number'].to_i
83
- end
84
-
85
- def geekiness
86
- self['Geekiness'].to_i
87
- end
88
-
89
- def names
90
- list('Names')
91
- end
92
-
93
- def topics
94
- list('Topics')
90
+ LIST_ATTRS.each do |attr|
91
+ define_method(attr.downcase.to_sym) { list(attr) }
95
92
  end
96
93
 
97
94
  private
98
95
 
99
- def read_body
100
- headers unless read_headers?
101
-
96
+ def read_headers
102
97
  io = file.open
103
- io.seek @body_pos
104
- io.read.strip
98
+ parse_headers(io)
105
99
  ensure
106
100
  io&.close
107
101
  end
108
102
 
109
- def read_headers
110
- io = file.open
103
+ # :reek:FeatureEnvy
104
+ def parse_headers(io, head = {})
105
+ until io.eof?
106
+ @body_pos = io.pos
107
+ match = HEADER_PATTERN.match(io.gets)
108
+ break if match.nil?
111
109
 
112
- {}.tap do |head|
113
- until io.eof?
114
- @body_pos = io.pos
115
- if (m = HEADER_PATTERN.match(io.gets))
116
- head[m[1].strip] = m[2]&.strip
117
- else
118
- break
119
- end
120
- end
121
-
122
- massage_headers(head)
110
+ head[match[1].strip] = match[2]&.strip
123
111
  end
124
- ensure
125
- io&.close
112
+ massage_headers(head)
126
113
  end
127
114
 
115
+ # :reek:UtilityFunction
128
116
  def massage_headers(head)
129
117
  if head.key?('Series')
130
118
  head['Series'] = head['Title']
131
119
  head['Title'] = head.delete('Subtitle')
132
120
  end
121
+ head
133
122
  end
134
123
 
135
124
  def list(key)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # codeless_code filters and prints fables from http://thecodelesscode.com
2
4
  # Copyright (C) 2018 Jon Sangster
3
5
  #
@@ -22,14 +24,14 @@ module CodelessCode
22
24
  extend Forwardable
23
25
  include Enumerable
24
26
 
25
- FILE_PATTERN = '*.txt'.freeze
26
- LANG_SEP = '-'.freeze
27
+ FILE_PATTERN = '*.txt'
28
+ LANG_SEP = '-'
27
29
 
28
- attr_accessor :dir
30
+ attr_reader :dir
29
31
  def_delegator :fables, :each
30
32
 
31
33
  def initialize(dir)
32
- self.dir = dir
34
+ @dir = dir
33
35
  end
34
36
 
35
37
  def lang
@@ -41,11 +43,11 @@ module CodelessCode
41
43
  end
42
44
 
43
45
  def fables
44
- @fables ||= files.map { |f| Fable.new(f) }.sort_by(&:number)
46
+ @fables ||= files.map { |file| Fable.new(file) }.sort_by(&:number)
45
47
  end
46
48
 
47
49
  def filter(filt)
48
- select { |f| filt.call(f) }
50
+ select { |fable| filt.call(fable) }
49
51
  end
50
52
 
51
53
  private
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # codeless_code filters and prints fables from http://thecodelesscode.com
2
4
  # Copyright (C) 2018 Jon Sangster
3
5
  #
@@ -20,11 +22,11 @@ module CodelessCode
20
22
  module_function
21
23
 
22
24
  def header_integer_filter(name)
23
- subclass_filter(HeaderInteger, name)
25
+ subclass_filter(Headers::Integer, name)
24
26
  end
25
27
 
26
28
  def header_string_filter(name)
27
- subclass_filter(HeaderString, name)
29
+ subclass_filter(Headers::String, name)
28
30
  end
29
31
 
30
32
  def subclass_filter(klass, name)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # codeless_code filters and prints fables from http://thecodelesscode.com
2
4
  # Copyright (C) 2018 Jon Sangster
3
5
  #
@@ -35,7 +37,7 @@ module CodelessCode
35
37
  end
36
38
 
37
39
  def call(fable)
38
- select(&:enabled?).all? { |filter| filter.call(fable) }
40
+ any? && select(&:enabled?).all? { |filter| filter.call(fable) }
39
41
  end
40
42
  end
41
43
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # codeless_code filters and prints fables from http://thecodelesscode.com
2
4
  # Copyright (C) 2018 Jon Sangster
3
5
  #
@@ -17,43 +19,40 @@ require 'date'
17
19
 
18
20
  module CodelessCode
19
21
  module Filters
20
- class Date
22
+ # Matches {Fable fables} that were published on, before, or after a given
23
+ # date.
24
+ class Date < Headers::Base
25
+ # :reek:BooleanParameter
21
26
  def initialize(exact: nil, min: nil, max: nil, exclude: false)
22
- @exact = exact
23
- @min = min
24
- @max = max
25
- @exclude = exclude
27
+ super('Date', exclude, [Matcher.parse(exact), :==],
28
+ [Matcher.parse(min), :<=],
29
+ [Matcher.parse(max), :>=])
26
30
  end
27
31
 
28
- def enabled?
29
- @exact || @min || @max || @exclude
32
+ # :reek:UtilityFunction
33
+ def parse(val)
34
+ ::Date.parse(val)
30
35
  end
31
36
 
32
- def call(fable)
33
- if (val = fable.date)
34
- return false unless @exact.nil? || @exact == val
35
- return false unless @min.nil? || @min <= val
36
- return false unless @max.nil? || @max >= val
37
- true
38
- else
39
- @exclude
40
- end
37
+ def test_single(val, operator, test)
38
+ test.send(operator, val)
41
39
  end
42
40
 
41
+ # Wraps a {::Date} and matches it against "date substrings". ie:
42
+ # +2010-12+ will matches any dates in December, 2010 and +2010+ will
43
+ # match any dates in that year.
43
44
  class Matcher
44
45
  # param str [String] A date like +2010+, +2010-12+, or +2010-12-23+
45
46
  def self.parse(str)
46
- match = :day
47
+ return nil unless str
47
48
 
48
49
  if str.size == 4
49
- str = "#{str}-01-01"
50
- match = :year
50
+ new(::Date.parse("#{str}-01-01"), match: :year)
51
51
  elsif str.size == 7
52
- str = "#{str}-01"
53
- match = :month
52
+ new(::Date.parse("#{str}-01"), match: :month)
53
+ else
54
+ new(::Date.parse(str), match: :day)
54
55
  end
55
-
56
- new(::Date.parse(str), match: match)
57
56
  end
58
57
 
59
58
  def initialize(date, match:)
@@ -62,27 +61,38 @@ module CodelessCode
62
61
  end
63
62
 
64
63
  def ==(other)
65
- cmp(@date, other, :==, @match)
64
+ compare(@date, other, :==, @match)
66
65
  end
67
66
 
68
67
  def >=(other)
69
- cmp(@date, other, :>=, @match)
68
+ compare(@date, other, :>=, @match)
70
69
  end
71
70
 
72
71
  def <=(other)
73
- cmp(@date, other, :<=, @match)
72
+ compare(@date, other, :<=, @match)
74
73
  end
75
74
 
76
75
  private
77
76
 
78
- def cmp(a, b, op, match)
77
+ # :reek:ControlParameter
78
+ # :reek:FeatureEnvy
79
+ def compare(first, second, operator, match)
79
80
  case match
80
81
  when :year
81
- a.year.send(op, b.year)
82
+ first.year.send(operator, second.year)
82
83
  when :month
83
- a.year == b.year ? a.month.send(op, b.month) : cmp(a, b, op, :year)
84
+ compare_month(first, second, operator)
85
+ else
86
+ first.send(operator, second)
87
+ end
88
+ end
89
+
90
+ # :reek:FeatureEnvy
91
+ def compare_month(first, second, operator)
92
+ if first.year == second.year
93
+ first.month.send(operator, second.month)
84
94
  else
85
- a.send(op, b)
95
+ compare(first, second, operator, :year)
86
96
  end
87
97
  end
88
98
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # codeless_code filters and prints fables from http://thecodelesscode.com
2
4
  # Copyright (C) 2018 Jon Sangster
3
5
  #
@@ -37,28 +39,46 @@ module CodelessCode
37
39
  end
38
40
 
39
41
  def filters
40
- @filters ||= [
41
- Filters::Lang.new(exact: lang),
42
+ @filters ||=
43
+ string_filters +
44
+ integer_filters +
45
+ [
46
+ filter(:Date, :date),
47
+ new_filter(:Lang, exact: lang),
48
+ number_filter,
49
+ translator_filter
50
+ ]
51
+ end
42
52
 
43
- Filters::Translator.new(exact: @opts[:translator_exact],
44
- casecmp: @opts[:translator]),
53
+ private
45
54
 
46
- Filters::Credits.new(**str_args(:credits)),
47
- Filters::Name.new(**str_args(:name)),
48
- Filters::Series.new(**str_args(:series)),
49
- Filters::Tagline.new(**str_args(:tagline)),
50
- Filters::Title.new(**str_args(:title)),
55
+ def string_filters
56
+ %i[Credits Name Tagline Title Series].map { |name| filter(name, :str) }
57
+ end
51
58
 
52
- Filters::Geekiness.new(**int_args(:geekiness)),
53
- Filters::Number.new(**int_args(:number).tap do |n|
54
- n[:exact] ||= @opts.args.last&.to_i
55
- end),
59
+ def integer_filters
60
+ %i[Geekiness].map { |name| filter(name, :int) }
61
+ end
56
62
 
57
- Filters::Date.new(**date_args),
58
- ]
63
+ def number_filter
64
+ filter(:Number, :int) { |args| args[:exact] ||= @opts.args.last&.to_i }
59
65
  end
60
66
 
61
- private
67
+ def translator_filter
68
+ new_filter(:Translator, exact: @opts[:translator_exact],
69
+ casecmp: @opts[:translator])
70
+ end
71
+
72
+ def filter(name, type, &blk)
73
+ new_filter(name,
74
+ **send(:"#{type.downcase}_args", name.to_s.downcase.to_s),
75
+ &blk)
76
+ end
77
+
78
+ def new_filter(name, **args)
79
+ yield args if block_given?
80
+ Filters.const_get(name).new(**args)
81
+ end
62
82
 
63
83
  def fallback_lang
64
84
  lang = ENV['LANG']&.split(/_|\./)&.first&.downcase&.to_sym
@@ -66,13 +86,14 @@ module CodelessCode
66
86
  end
67
87
 
68
88
  def non_defaults_enabled?
69
- filters.reject { |f| f.is_a?(Filters::Lang) }.any?(&:enabled?)
89
+ filters.reject { |filter| filter.is_a?(Filters::Lang) }.any?(&:enabled?)
70
90
  end
71
91
 
72
92
  def str_args(name)
73
93
  Hash[
74
94
  %i[exact start_with end_with exclude].zip(
75
- @opts.slice(*%I[#{name} #{name}_start #{name}_end no_#{name}]).values
95
+ @opts.slice(:"#{name}", :"#{name}_start", :"#{name}_end",
96
+ :"no_#{name}").values
76
97
  )
77
98
  ].tap do |args|
78
99
  args[:start_with] ||= '' if @opts[:"has_#{name}"]
@@ -82,7 +103,7 @@ module CodelessCode
82
103
  def int_args(name)
83
104
  Hash[
84
105
  %i[exact min max exclude].zip(
85
- @opts.slice(*%I[#{name} #{name}_gte #{name}_lte])
106
+ @opts.slice(:"#{name}", :"#{name}_gte", :"#{name}_lte")
86
107
  .values
87
108
  .map { |num| num&.to_i }
88
109
  .push(@opts[:"no_#{name}"])
@@ -90,18 +111,14 @@ module CodelessCode
90
111
  ]
91
112
  end
92
113
 
93
- def date_args
114
+ def date_args(name)
94
115
  {
95
- exact: date(@opts[:date]),
96
- min: date(@opts[:date_gte]),
97
- max: date(@opts[:date_lte]),
98
- exclude: @opts[:no_date],
116
+ exact: @opts[:"#{name}"],
117
+ min: @opts[:"#{name}_after"],
118
+ max: @opts[:"#{name}_before"],
119
+ exclude: @opts[:"no_#{name}"]
99
120
  }
100
121
  end
101
-
102
- def date(arg)
103
- arg && Filters::Date::Matcher.parse(arg)
104
- end
105
122
  end
106
123
  end
107
124
  end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ # codeless_code filters and prints fables from http://thecodelesscode.com
4
+ # Copyright (C) 2018 Jon Sangster
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify it under
7
+ # the terms of the GNU General Public License as published by the Free Software
8
+ # Foundation, either version 3 of the License, or (at your option) any later
9
+ # version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful, but WITHOUT
12
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
+ # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14
+ # details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License along with
17
+ # this program. If not, see <https://www.gnu.org/licenses/>.
18
+ module CodelessCode
19
+ module Filters
20
+ module Headers
21
+ # Abstract base class for all generic filters that test a {Fable fable's}
22
+ # headers.
23
+ class Base
24
+ def initialize(key, exclude, *tests)
25
+ @key = key
26
+ @exclude = exclude
27
+ @tests ||= tests.select(&:first).freeze
28
+ end
29
+
30
+ def enabled?
31
+ @tests.any? || @exclude
32
+ end
33
+
34
+ def call(fable)
35
+ if fable.header?(@key)
36
+ @tests.any? ? test(parse(fable[@key])) : !@exclude
37
+ else
38
+ @exclude
39
+ end
40
+ end
41
+
42
+ protected
43
+
44
+ # :reek:UtilityFunction
45
+ def parse(val)
46
+ val
47
+ end
48
+
49
+ def test_single(val, operator, test)
50
+ val.send(operator, test)
51
+ end
52
+
53
+ private
54
+
55
+ def test(val)
56
+ @tests.any? { |(test, op)| test_single(val, op, test) }
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # codeless_code filters and prints fables from http://thecodelesscode.com
2
4
  # Copyright (C) 2018 Jon Sangster
3
5
  #
@@ -15,27 +17,22 @@
15
17
  # this program. If not, see <https://www.gnu.org/licenses/>.
16
18
  module CodelessCode
17
19
  module Filters
18
- class HeaderInteger
19
- def initialize(key, exact: nil, min: nil, max: nil, exclude: false)
20
- @key = key
21
- @exact = exact
22
- @min = min
23
- @max = max
24
- @exclude = exclude
25
- end
20
+ module Headers
21
+ # Matches {Fable fables} that have an integer header equal to, less than,
22
+ # or greater than the given parameters.
23
+ class Integer < Base
24
+ # :reek:BooleanParameter
25
+ def initialize(key, exact: nil, min: nil, max: nil, exclude: false)
26
+ super(key, exclude, [exact, :==],
27
+ [min, :>=],
28
+ [max, :<=])
29
+ end
26
30
 
27
- def enabled?
28
- @exact || @min || @max || @exclude
29
- end
31
+ protected
30
32
 
31
- def call(fable)
32
- if fable.header?(@key) && (val = fable[@key]&.to_i)
33
- return false unless @exact.nil? || val == @exact
34
- return false unless @min.nil? || val >= @min.to_i
35
- return false unless @max.nil? || val <= @max.to_i
36
- !@exclude
37
- else
38
- @exclude
33
+ # :reek:UtilityFunction
34
+ def parse(val)
35
+ val.to_i
39
36
  end
40
37
  end
41
38
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # codeless_code filters and prints fables from http://thecodelesscode.com
2
4
  # Copyright (C) 2018 Jon Sangster
3
5
  #
@@ -15,28 +17,16 @@
15
17
  # this program. If not, see <https://www.gnu.org/licenses/>.
16
18
  module CodelessCode
17
19
  module Filters
18
- class HeaderString
19
- def initialize(key, exact: nil, start_with: nil, end_with: nil,
20
- exclude: false)
21
- @key = key
22
- @exact = exact
23
- @start_with = start_with
24
- @end_with = end_with
25
- @exclude = exclude
26
- end
27
-
28
- def enabled?
29
- @exact || @start_with || @end_with || @exclude
30
- end
31
-
32
- def call(fable)
33
- if fable.header?(@key) && (val = fable[@key])
34
- return false unless @exact.nil? || val == @exact
35
- return false unless @start_with.nil? || val.start_with?(@start_with)
36
- return false unless @end_with.nil? || val.end_with?(@end_with)
37
- !@exclude
38
- else
39
- @exclude
20
+ module Headers
21
+ # Matches {Fable fables} that have an string header equal to, that starts
22
+ # with, or ends with the given parameters.
23
+ class String < Base
24
+ # :reek:BooleanParameter
25
+ def initialize(key, exact: nil, start_with: nil, end_with: nil,
26
+ exclude: false)
27
+ super(key, exclude, [exact, :==],
28
+ [start_with, :start_with?],
29
+ [end_with, :end_with?])
40
30
  end
41
31
  end
42
32
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # codeless_code filters and prints fables from http://thecodelesscode.com
2
4
  # Copyright (C) 2018 Jon Sangster
3
5
  #
@@ -15,6 +17,7 @@
15
17
  # this program. If not, see <https://www.gnu.org/licenses/>.
16
18
  module CodelessCode
17
19
  module Filters
20
+ # Matches {Fable fables} in the given language (code).
18
21
  class Lang
19
22
  def initialize(exact: nil)
20
23
  @exact = exact
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # codeless_code filters and prints fables from http://thecodelesscode.com
2
4
  # Copyright (C) 2018 Jon Sangster
3
5
  #
@@ -15,6 +17,7 @@
15
17
  # this program. If not, see <https://www.gnu.org/licenses/>.
16
18
  module CodelessCode
17
19
  module Filters
20
+ # Matches {Fable fables} translated by the person with the given name.
18
21
  class Translator
19
22
  def initialize(exact: nil, casecmp: nil)
20
23
  @exact = exact
@@ -26,8 +29,10 @@ module CodelessCode
26
29
  end
27
30
 
28
31
  def call(fable)
29
- return false unless @exact.nil? || @exact == fable.translator
30
- @casecmp.nil? || @casecmp.casecmp?(fable.translator)
32
+ trans = fable.translator
33
+ return false unless @exact.nil? || @exact == trans
34
+
35
+ @casecmp.nil? || @casecmp.casecmp?(trans)
31
36
  end
32
37
  end
33
38
  end