public_suffix 2.0.5 → 4.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +5 -5
  2. data/.github/FUNDING.yml +12 -0
  3. data/.github/dependabot.yml +8 -0
  4. data/.github/workflows/release.yml +16 -0
  5. data/.github/workflows/tests.yml +28 -0
  6. data/.gitignore +5 -8
  7. data/.rubocop.yml +19 -1
  8. data/{.rubocop_defaults.yml → .rubocop_opinionated.yml} +62 -34
  9. data/CHANGELOG.md +156 -54
  10. data/Gemfile +9 -5
  11. data/LICENSE.txt +1 -1
  12. data/README.md +44 -15
  13. data/Rakefile +9 -4
  14. data/SECURITY.md +104 -0
  15. data/bin/console +15 -0
  16. data/data/list.txt +3163 -973
  17. data/lib/public_suffix/domain.rb +4 -4
  18. data/lib/public_suffix/errors.rb +3 -1
  19. data/lib/public_suffix/list.rb +78 -117
  20. data/lib/public_suffix/rule.rb +54 -62
  21. data/lib/public_suffix/version.rb +8 -3
  22. data/lib/public_suffix.rb +38 -32
  23. data/public_suffix.gemspec +9 -5
  24. data/test/.empty +2 -0
  25. data/test/acceptance_test.rb +43 -31
  26. data/test/benchmarks/bm_find.rb +66 -0
  27. data/test/benchmarks/bm_find_all.rb +102 -0
  28. data/test/benchmarks/bm_names.rb +91 -0
  29. data/test/benchmarks/bm_select.rb +26 -0
  30. data/test/benchmarks/bm_select_incremental.rb +25 -0
  31. data/test/benchmarks/bm_valid.rb +101 -0
  32. data/test/profilers/domain_profiler.rb +12 -0
  33. data/test/profilers/find_profiler.rb +12 -0
  34. data/test/profilers/find_profiler_jp.rb +12 -0
  35. data/test/{initialization_profiler.rb → profilers/initialization_profiler.rb} +1 -1
  36. data/test/profilers/list_profsize.rb +11 -0
  37. data/test/profilers/object_binsize.rb +57 -0
  38. data/test/psl_test.rb +7 -4
  39. data/test/test_helper.rb +3 -14
  40. data/test/unit/domain_test.rb +17 -15
  41. data/test/unit/errors_test.rb +2 -0
  42. data/test/unit/list_test.rb +54 -72
  43. data/test/unit/public_suffix_test.rb +24 -22
  44. data/test/unit/rule_test.rb +77 -79
  45. metadata +32 -70
  46. data/.ruby-gemset +0 -1
  47. data/.travis.yml +0 -23
  48. data/test/benchmark_helper.rb +0 -4
  49. data/test/execution_profiler.rb +0 -14
  50. data/test/performance_benchmark.rb +0 -38
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # = Public Suffix
2
4
  #
3
5
  # Domain name parser based on the Public Suffix List.
4
6
  #
5
- # Copyright (c) 2009-2017 Simone Carletti <weppos@weppos.net>
7
+ # Copyright (c) 2009-2022 Simone Carletti <weppos@weppos.net>
6
8
 
7
9
  module PublicSuffix
8
10
 
@@ -43,7 +45,7 @@ module PublicSuffix
43
45
  # Initializes with a +tld+, +sld+ and +trd+.
44
46
  # @param [String] tld The TLD (extension)
45
47
  # @param [String] sld The SLD (domain)
46
- # @param [String] tld The TRD (subdomain)
48
+ # @param [String] trd The TRD (subdomain)
47
49
  #
48
50
  # @yield [self] Yields on self.
49
51
  # @yieldparam [PublicSuffix::Domain] self The newly creates instance
@@ -173,8 +175,6 @@ module PublicSuffix
173
175
  # This method doesn't actually validate the domain.
174
176
  # It only checks whether the instance contains
175
177
  # a value for the {#tld} and {#sld} attributes.
176
- # If you also want to validate the domain,
177
- # use {#valid_domain?} instead.
178
178
  #
179
179
  # @example
180
180
  #
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # = Public Suffix
2
4
  #
3
5
  # Domain name parser based on the Public Suffix List.
4
6
  #
5
- # Copyright (c) 2009-2017 Simone Carletti <weppos@weppos.net>
7
+ # Copyright (c) 2009-2022 Simone Carletti <weppos@weppos.net>
6
8
 
7
9
  module PublicSuffix
8
10
 
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # = Public Suffix
2
4
  #
3
5
  # Domain name parser based on the Public Suffix List.
4
6
  #
5
- # Copyright (c) 2009-2017 Simone Carletti <weppos@weppos.net>
7
+ # Copyright (c) 2009-2022 Simone Carletti <weppos@weppos.net>
6
8
 
7
9
  module PublicSuffix
8
10
 
@@ -35,12 +37,9 @@ module PublicSuffix
35
37
  # The {PublicSuffix::List.default} rule list is used
36
38
  # to tokenize and validate a domain.
37
39
  #
38
- # {PublicSuffix::List} implements +Enumerable+ module.
39
- #
40
40
  class List
41
- include Enumerable
42
41
 
43
- DEFAULT_LIST_PATH = File.join(File.dirname(__FILE__), "..", "..", "data", "list.txt")
42
+ DEFAULT_LIST_PATH = File.expand_path("../../data/list.txt", __dir__)
44
43
 
45
44
  # Gets the default rule list.
46
45
  #
@@ -49,39 +48,27 @@ module PublicSuffix
49
48
  #
50
49
  # @return [PublicSuffix::List]
51
50
  def self.default(**options)
52
- @default ||= parse(File.read(DEFAULT_LIST_PATH), options)
51
+ @default ||= parse(File.read(DEFAULT_LIST_PATH), **options)
53
52
  end
54
53
 
55
54
  # Sets the default rule list to +value+.
56
55
  #
57
- # @param [PublicSuffix::List] value
58
- # The new rule list.
59
- #
56
+ # @param value [PublicSuffix::List] the new list
60
57
  # @return [PublicSuffix::List]
61
58
  def self.default=(value)
62
59
  @default = value
63
60
  end
64
61
 
65
- # Sets the default rule list to +nil+.
66
- #
67
- # @return [self]
68
- def self.clear
69
- self.default = nil
70
- self
71
- end
72
-
73
- # rubocop:disable Metrics/MethodLength
74
-
75
62
  # Parse given +input+ treating the content as Public Suffix List.
76
63
  #
77
64
  # See http://publicsuffix.org/format/ for more details about input format.
78
65
  #
79
- # @param string [#each_line] The list to parse.
80
- # @param private_domain [Boolean] whether to ignore the private domains section.
81
- # @return [Array<PublicSuffix::Rule::*>]
66
+ # @param input [#each_line] the list to parse
67
+ # @param private_domains [Boolean] whether to ignore the private domains section
68
+ # @return [PublicSuffix::List]
82
69
  def self.parse(input, private_domains: true)
83
- comment_token = "//".freeze
84
- private_token = "===BEGIN PRIVATE DOMAINS===".freeze
70
+ comment_token = "//"
71
+ private_token = "===BEGIN PRIVATE DOMAINS==="
85
72
  section = nil # 1 == ICANN, 2 == PRIVATE
86
73
 
87
74
  new do |list|
@@ -96,6 +83,7 @@ module PublicSuffix
96
83
  # include private domains or stop scanner
97
84
  when line.include?(private_token)
98
85
  break if !private_domains
86
+
99
87
  section = 2
100
88
 
101
89
  # skip comments
@@ -103,53 +91,21 @@ module PublicSuffix
103
91
  next
104
92
 
105
93
  else
106
- list.add(Rule.factory(line, private: section == 2), reindex: false)
94
+ list.add(Rule.factory(line, private: section == 2))
107
95
 
108
96
  end
109
97
  end
110
98
  end
111
99
  end
112
- # rubocop:enable Metrics/MethodLength
113
-
114
-
115
- # Gets the array of rules.
116
- #
117
- # @return [Array<PublicSuffix::Rule::*>]
118
- attr_reader :rules
119
100
 
120
101
 
121
102
  # Initializes an empty {PublicSuffix::List}.
122
103
  #
123
104
  # @yield [self] Yields on self.
124
105
  # @yieldparam [PublicSuffix::List] self The newly created instance.
125
- #
126
106
  def initialize
127
- @rules = []
107
+ @rules = {}
128
108
  yield(self) if block_given?
129
- reindex!
130
- end
131
-
132
-
133
- # Creates a naive index for +@rules+. Just a hash that will tell
134
- # us where the elements of +@rules+ are relative to its first
135
- # {PublicSuffix::Rule::Base#labels} element.
136
- #
137
- # For instance if @rules[5] and @rules[4] are the only elements of the list
138
- # where Rule#labels.first is 'us' @indexes['us'] #=> [5,4], that way in
139
- # select we can avoid mapping every single rule against the candidate domain.
140
- def reindex!
141
- @indexes = {}
142
- @rules.each_with_index do |rule, index|
143
- tld = Domain.name_to_labels(rule.value).last
144
- @indexes[tld] ||= []
145
- @indexes[tld] << index
146
- end
147
- end
148
-
149
- # Gets the naive index, a hash that with the keys being the first label of
150
- # every rule pointing to an array of integers (indexes of the rules in @rules).
151
- def indexes
152
- @indexes.dup
153
109
  end
154
110
 
155
111
 
@@ -159,42 +115,36 @@ module PublicSuffix
159
115
  # {PublicSuffix::List} and each +PublicSuffix::Rule::*+
160
116
  # in list <tt>one</tt> is available in list <tt>two</tt>, in the same order.
161
117
  #
162
- # @param [PublicSuffix::List] other
163
- # The List to compare.
164
- #
118
+ # @param other [PublicSuffix::List] the List to compare
165
119
  # @return [Boolean]
166
120
  def ==(other)
167
121
  return false unless other.is_a?(List)
168
- equal?(other) || rules == other.rules
122
+
123
+ equal?(other) || @rules == other.rules
169
124
  end
170
125
  alias eql? ==
171
126
 
172
127
  # Iterates each rule in the list.
173
- def each(*args, &block)
174
- @rules.each(*args, &block)
128
+ def each(&block)
129
+ Enumerator.new do |y|
130
+ @rules.each do |key, node|
131
+ y << entry_to_rule(node, key)
132
+ end
133
+ end.each(&block)
175
134
  end
176
135
 
177
136
 
178
137
  # Adds the given object to the list and optionally refreshes the rule index.
179
138
  #
180
- # @param [PublicSuffix::Rule::*] rule
181
- # The rule to add to the list.
182
- # @param [Boolean] reindex
183
- # Set to true to recreate the rule index
184
- # after the rule has been added to the list.
185
- #
139
+ # @param rule [PublicSuffix::Rule::*] the rule to add to the list
186
140
  # @return [self]
187
- #
188
- # @see #reindex!
189
- #
190
- def add(rule, reindex: true)
191
- @rules << rule
192
- reindex! if reindex
141
+ def add(rule)
142
+ @rules[rule.value] = rule_to_entry(rule)
193
143
  self
194
144
  end
195
145
  alias << add
196
146
 
197
- # Gets the number of elements in the list.
147
+ # Gets the number of rules in the list.
198
148
  #
199
149
  # @return [Integer]
200
150
  def size
@@ -208,70 +158,65 @@ module PublicSuffix
208
158
  @rules.empty?
209
159
  end
210
160
 
211
- # Removes all elements.
161
+ # Removes all rules.
212
162
  #
213
163
  # @return [self]
214
164
  def clear
215
165
  @rules.clear
216
- reindex!
217
166
  self
218
167
  end
219
168
 
220
- # Finds and returns the most appropriate rule for the domain name.
221
- #
222
- # From the Public Suffix List documentation:
223
- #
224
- # - If a hostname matches more than one rule in the file,
225
- # the longest matching rule (the one with the most levels) will be used.
226
- # - An exclamation mark (!) at the start of a rule marks an exception to a previous wildcard rule.
227
- # An exception rule takes priority over any other matching rule.
228
- #
229
- # ## Algorithm description
169
+ # Finds and returns the rule corresponding to the longest public suffix for the hostname.
230
170
  #
231
- # 1. Match domain against all rules and take note of the matching ones.
232
- # 2. If no rules match, the prevailing rule is "*".
233
- # 3. If more than one rule matches, the prevailing rule is the one which is an exception rule.
234
- # 4. If there is no matching exception rule, the prevailing rule is the one with the most labels.
235
- # 5. If the prevailing rule is a exception rule, modify it by removing the leftmost label.
236
- # 6. The public suffix is the set of labels from the domain
237
- # which directly match the labels of the prevailing rule (joined by dots).
238
- # 7. The registered domain is the public suffix plus one additional label.
239
- #
240
- # @param name [String, #to_s] The domain name.
241
- # @param [PublicSuffix::Rule::*] default The default rule to return in case no rule matches.
171
+ # @param name [#to_s] the hostname
172
+ # @param default [PublicSuffix::Rule::*] the default rule to return in case no rule matches
242
173
  # @return [PublicSuffix::Rule::*]
243
174
  def find(name, default: default_rule, **options)
244
175
  rule = select(name, **options).inject do |l, r|
245
- return r if r.class == Rule::Exception
176
+ return r if r.instance_of?(Rule::Exception)
177
+
246
178
  l.length > r.length ? l : r
247
179
  end
248
180
  rule || default
249
181
  end
250
182
 
251
- # Selects all the rules matching given domain.
252
- #
253
- # Internally, the lookup heavily rely on the `@indexes`. The input is split into labels,
254
- # and we retriever from the index only the rules that end with the input label. After that,
255
- # a sequential scan is performed. In most cases, where the number of rules for the same label
256
- # is limited, this algorithm is efficient enough.
183
+ # Selects all the rules matching given hostame.
257
184
  #
258
- # If `ignore_private` is set to true, the algorithm will skip the rules that are flagged as private domain.
259
- # Note that the rules will still be part of the loop. If you frequently need to access lists
260
- # ignoring the private domains, you should create a list that doesn't include these domains setting the
185
+ # If `ignore_private` is set to true, the algorithm will skip the rules that are flagged as
186
+ # private domain. Note that the rules will still be part of the loop.
187
+ # If you frequently need to access lists ignoring the private domains,
188
+ # you should create a list that doesn't include these domains setting the
261
189
  # `private_domains: false` option when calling {.parse}.
262
190
  #
263
- # @param [String, #to_s] name The domain name.
264
- # @param [Boolean] ignore_private
191
+ # Note that this method is currently private, as you should not rely on it. Instead,
192
+ # the public interface is {#find}. The current internal algorithm allows to return all
193
+ # matching rules, but different data structures may not be able to do it, and instead would
194
+ # return only the match. For this reason, you should rely on {#find}.
195
+ #
196
+ # @param name [#to_s] the hostname
197
+ # @param ignore_private [Boolean]
265
198
  # @return [Array<PublicSuffix::Rule::*>]
266
199
  def select(name, ignore_private: false)
267
200
  name = name.to_s
268
- indices = (@indexes[Domain.name_to_labels(name).last] || [])
269
201
 
270
- finder = @rules.values_at(*indices).lazy
271
- finder = finder.select { |rule| rule.match?(name) }
272
- finder = finder.select { |rule| !rule.private } if ignore_private
273
- finder.to_a
202
+ parts = name.split(DOT).reverse!
203
+ index = 0
204
+ query = parts[index]
205
+ rules = []
206
+
207
+ loop do
208
+ match = @rules[query]
209
+ rules << entry_to_rule(match, query) if !match.nil? && (ignore_private == false || match.private == false)
210
+
211
+ index += 1
212
+ break if index >= parts.size
213
+
214
+ query = parts[index] + DOT + query
215
+ end
216
+
217
+ rules
274
218
  end
219
+ private :select
275
220
 
276
221
  # Gets the default rule.
277
222
  #
@@ -282,5 +227,21 @@ module PublicSuffix
282
227
  PublicSuffix::Rule.default
283
228
  end
284
229
 
230
+
231
+ protected
232
+
233
+ attr_reader :rules
234
+
235
+
236
+ private
237
+
238
+ def entry_to_rule(entry, value)
239
+ entry.type.new(value: value, length: entry.length, private: entry.private)
240
+ end
241
+
242
+ def rule_to_entry(rule)
243
+ Rule::Entry.new(rule.class, rule.length, rule.private)
244
+ end
245
+
285
246
  end
286
247
  end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # = Public Suffix
2
4
  #
3
5
  # Domain name parser based on the Public Suffix List.
4
6
  #
5
- # Copyright (c) 2009-2017 Simone Carletti <weppos@weppos.net>
7
+ # Copyright (c) 2009-2022 Simone Carletti <weppos@weppos.net>
6
8
 
7
9
  module PublicSuffix
8
10
 
@@ -19,6 +21,9 @@ module PublicSuffix
19
21
  #
20
22
  module Rule
21
23
 
24
+ # @api internal
25
+ Entry = Struct.new(:type, :length, :private) # rubocop:disable Lint/StructNewOverride
26
+
22
27
  # = Abstract rule class
23
28
  #
24
29
  # This represent the base class for a Rule definition
@@ -99,25 +104,36 @@ module PublicSuffix
99
104
  # @return [String] the rule definition
100
105
  attr_reader :value
101
106
 
107
+ # @return [String] the length of the rule
108
+ attr_reader :length
109
+
102
110
  # @return [Boolean] true if the rule is a private domain
103
111
  attr_reader :private
104
112
 
105
113
 
106
- # Initializes a new rule with name and value.
107
- # If value is +nil+, name also becomes the value for this rule.
114
+ # Initializes a new rule from the content.
115
+ #
116
+ # @param content [String] the content of the rule
117
+ # @param private [Boolean]
118
+ def self.build(content, private: false)
119
+ new(value: content, private: private)
120
+ end
121
+
122
+ # Initializes a new rule.
108
123
  #
109
- # @param value [String] the value of the rule
110
- def initialize(value, private: false)
124
+ # @param value [String]
125
+ # @param private [Boolean]
126
+ def initialize(value:, length: nil, private: false)
111
127
  @value = value.to_s
128
+ @length = length || @value.count(DOT) + 1
112
129
  @private = private
113
130
  end
114
131
 
115
132
  # Checks whether this rule is equal to <tt>other</tt>.
116
133
  #
117
- # @param [PublicSuffix::Rule::*] other The rule to compare
118
- # @return [Boolean]
119
- # Returns true if this rule and other are instances of the same class
120
- # and has the same value, false otherwise.
134
+ # @param other [PublicSuffix::Rule::*] The rule to compare
135
+ # @return [Boolean] true if this rule and other are instances of the same class
136
+ # and has the same value, false otherwise.
121
137
  def ==(other)
122
138
  equal?(other) || (self.class == other.class && value == other.value)
123
139
  end
@@ -137,12 +153,12 @@ module PublicSuffix
137
153
  # @see https://publicsuffix.org/list/
138
154
  #
139
155
  # @example
140
- # Rule.factory("com").match?("example.com")
156
+ # PublicSuffix::Rule.factory("com").match?("example.com")
141
157
  # # => true
142
- # Rule.factory("com").match?("example.net")
158
+ # PublicSuffix::Rule.factory("com").match?("example.net")
143
159
  # # => false
144
160
  #
145
- # @param name [String, #to_s] The domain name to check.
161
+ # @param name [String] the domain name to check
146
162
  # @return [Boolean]
147
163
  def match?(name)
148
164
  # Note: it works because of the assumption there are no
@@ -150,7 +166,7 @@ module PublicSuffix
150
166
  # we need to properly walk the input and skip parts according
151
167
  # to wildcard component.
152
168
  diff = name.chomp(value)
153
- diff.empty? || diff[-1] == "."
169
+ diff.empty? || diff.end_with?(DOT)
154
170
  end
155
171
 
156
172
  # @abstract
@@ -159,12 +175,7 @@ module PublicSuffix
159
175
  end
160
176
 
161
177
  # @abstract
162
- def length
163
- raise NotImplementedError
164
- end
165
-
166
- # @abstract
167
- # @param [String, #to_s] name The domain name to decompose
178
+ # @param domain [#to_s] The domain name to decompose
168
179
  # @return [Array<String, nil>]
169
180
  def decompose(*)
170
181
  raise NotImplementedError
@@ -184,7 +195,7 @@ module PublicSuffix
184
195
 
185
196
  # Decomposes the domain name according to rule properties.
186
197
  #
187
- # @param [String, #to_s] name The domain name to decompose
198
+ # @param domain [#to_s] The domain name to decompose
188
199
  # @return [Array<String>] The array with [trd + sld, tld].
189
200
  def decompose(domain)
190
201
  suffix = parts.join('\.')
@@ -200,27 +211,27 @@ module PublicSuffix
200
211
  @value.split(DOT)
201
212
  end
202
213
 
203
- # Gets the length of this rule for comparison,
204
- # represented by the number of dot-separated parts in the rule.
205
- #
206
- # @return [Integer] The length of the rule.
207
- def length
208
- @length ||= parts.length
209
- end
210
-
211
214
  end
212
215
 
213
216
  # Wildcard represents a wildcard rule (e.g. *.co.uk).
214
217
  class Wildcard < Base
215
218
 
216
- # Initializes a new rule from +definition+.
219
+ # Initializes a new rule from the content.
217
220
  #
218
- # The wildcard "*" is removed from the value, as it's common
219
- # for each wildcard rule.
221
+ # @param content [String] the content of the rule
222
+ # @param private [Boolean]
223
+ def self.build(content, private: false)
224
+ new(value: content.to_s[2..-1], private: private)
225
+ end
226
+
227
+ # Initializes a new rule.
220
228
  #
221
- # @param definition [String] the rule as defined in the PSL
222
- def initialize(definition, private: false)
223
- super(definition.to_s[2..-1], private: private)
229
+ # @param value [String]
230
+ # @param length [Integer]
231
+ # @param private [Boolean]
232
+ def initialize(value:, length: nil, private: false)
233
+ super(value: value, length: length, private: private)
234
+ length or @length += 1 # * counts as 1
224
235
  end
225
236
 
226
237
  # Gets the original rule definition.
@@ -232,7 +243,7 @@ module PublicSuffix
232
243
 
233
244
  # Decomposes the domain name according to rule properties.
234
245
  #
235
- # @param [String, #to_s] name The domain name to decompose
246
+ # @param domain [#to_s] The domain name to decompose
236
247
  # @return [Array<String>] The array with [trd + sld, tld].
237
248
  def decompose(domain)
238
249
  suffix = ([".*?"] + parts).join('\.')
@@ -248,28 +259,17 @@ module PublicSuffix
248
259
  @value.split(DOT)
249
260
  end
250
261
 
251
- # Gets the length of this rule for comparison,
252
- # represented by the number of dot-separated parts in the rule
253
- # plus 1 for the *.
254
- #
255
- # @return [Integer] The length of the rule.
256
- def length
257
- @length ||= parts.length + 1 # * counts as 1
258
- end
259
-
260
262
  end
261
263
 
262
264
  # Exception represents an exception rule (e.g. !parliament.uk).
263
265
  class Exception < Base
264
266
 
265
- # Initializes a new rule from +definition+.
266
- #
267
- # The bang ! is removed from the value, as it's common
268
- # for each wildcard rule.
267
+ # Initializes a new rule from the content.
269
268
  #
270
- # @param definition [String] the rule as defined in the PSL
271
- def initialize(definition, private: false)
272
- super(definition.to_s[1..-1], private: private)
269
+ # @param content [#to_s] the content of the rule
270
+ # @param private [Boolean]
271
+ def self.build(content, private: false)
272
+ new(value: content.to_s[1..-1], private: private)
273
273
  end
274
274
 
275
275
  # Gets the original rule definition.
@@ -281,7 +281,7 @@ module PublicSuffix
281
281
 
282
282
  # Decomposes the domain name according to rule properties.
283
283
  #
284
- # @param [String, #to_s] name The domain name to decompose
284
+ # @param domain [#to_s] The domain name to decompose
285
285
  # @return [Array<String>] The array with [trd + sld, tld].
286
286
  def decompose(domain)
287
287
  suffix = parts.join('\.')
@@ -302,14 +302,6 @@ module PublicSuffix
302
302
  @value.split(DOT)[1..-1]
303
303
  end
304
304
 
305
- # Gets the length of this rule for comparison,
306
- # represented by the number of dot-separated parts in the rule.
307
- #
308
- # @return [Integer] The length of the rule.
309
- def length
310
- @length ||= parts.length
311
- end
312
-
313
305
  end
314
306
 
315
307
 
@@ -329,7 +321,7 @@ module PublicSuffix
329
321
  # PublicSuffix::Rule.factory("!congresodelalengua3.ar")
330
322
  # # => #<PublicSuffix::Rule::Exception>
331
323
  #
332
- # @param [String] content The rule content.
324
+ # @param content [#to_s] the content of the rule
333
325
  # @return [PublicSuffix::Rule::*] A rule instance.
334
326
  def self.factory(content, private: false)
335
327
  case content.to_s[0, 1]
@@ -339,7 +331,7 @@ module PublicSuffix
339
331
  Exception
340
332
  else
341
333
  Normal
342
- end.new(content, private: private)
334
+ end.build(content, private: private)
343
335
  end
344
336
 
345
337
  # The default rule to use if no rule match.
@@ -1,10 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
1
4
  # = Public Suffix
2
5
  #
3
6
  # Domain name parser based on the Public Suffix List.
4
7
  #
5
- # Copyright (c) 2009-2017 Simone Carletti <weppos@weppos.net>
8
+ # Copyright (c) 2009-2022 Simone Carletti <weppos@weppos.net>
6
9
 
7
10
  module PublicSuffix
8
- # The current library version.
9
- VERSION = "2.0.5".freeze
11
+
12
+ # @return [String] The current library version.
13
+ VERSION = "4.0.7"
14
+
10
15
  end