• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

marian13 / semantic_boolean / 15761323522

19 Jun 2025 03:22PM UTC coverage: 91.15% (-1.6%) from 92.727%
15761323522

push

github

marian13
test(semantic_boolean): add more specs

45 of 53 branches covered (84.91%)

Branch coverage included in aggregate %.

1 of 1 new or added line in 1 file covered. (100.0%)

1 existing line in 1 file now uncovered.

58 of 60 relevant lines covered (96.67%)

1140.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.15
/lib/semantic_boolean.rb
1
# frozen_string_literal: true
2

3
require_relative "semantic_boolean/version"
14✔
4

5
require "set"
14✔
6

7
# rubocop:disable Lint/BooleanSymbol
8
module SemanticBoolean
14✔
9
  class << self
14✔
10
    ##
11
    # Truthy values in `SemanticBoolean.to_env_bool` terms.
12
    #
13
    # @return [Array<String>]
14
    #
15
    TO_ENV_BOOL_TRUE_VALUES = ["t", "T", "true", "True", "TRUE", "on", "On", "ON", "y", "Y", "yes", "Yes", "YES"].freeze
14✔
16

17
    ##
18
    # Falsy values in `ActiveModel::Type::Boolean` terms.
19
    #
20
    # @return [Array<String>]
21
    #
22
    # @see https://github.com/rails/rails/blob/v8.0.2/activemodel/lib/active_model/type/boolean.rb#L15
23
    #
24
    TO_ACTIVE_MODEL_BOOLEAN_TYPE_FALSE_VALUES = [false, 0, "0", :"0", "f", :f, "F", :F, "false", :false, "FALSE", :FALSE, "off", :off, "OFF", :OFF].to_set.freeze
14✔
25

26
    ##
27
    # Regexp to match falsy string values in Rails `blank?` terms.
28
    #
29
    # @return [Regexp]
30
    #
31
    # @see https://github.com/rails/rails/blob/v8.0.2/activesupport/lib/active_support/core_ext/object/blank.rb#L136
32
    #
33
    ACTIVE_SUPPORT_CORE_EXT_BLANK_RE = /\A[[:space:]]*\z/
14✔
34

35
    ##
36
    # Cache of regexp objects to match falsy string values in Rails `blank?` terms with non-default encodings.
37
    #
38
    # @return [Hash]
39
    #
40
    # @see https://github.com/rails/rails/blob/v8.0.2/activesupport/lib/active_support/core_ext/object/blank.rb#L137
41
    #
42
    ACTIVE_SUPPORT_CORE_EXT_ENCODED_BLANKS = ::Hash.new do |h, enc|
14✔
43
      h[enc] = ::Regexp.new(ACTIVE_SUPPORT_CORE_EXT_BLANK_RE.source.encode(enc), ACTIVE_SUPPORT_CORE_EXT_BLANK_RE.options | ::Regexp::FIXEDENCODING)
52✔
44
    end
45

46
    ##
47
    # Returns `false` when `object` is `false` or `nil`.
48
    # Returns `true` for all the other cases.
49
    # Just like Ruby does in the control expressions.
50
    #
51
    # @param object [Object] Can be any type.
52
    # @return [Boolean]
53
    #
54
    # @note If performance is a concern, prefer to use `!!` directly.
55
    # @see https://docs.ruby-lang.org/en/3.4/syntax/control_expressions_rdoc.html
56
    #
57
    def to_ruby_bool(object)
14✔
58
      !!object
5,138✔
59
    end
60

61
    ##
62
    # A handy alias for `to_ruby_bool`.
63
    #
64
    # @return [Boolean]
65
    #
66
    alias_method :to_bool, :to_ruby_bool
14✔
67

68
    if ::Gem::Version.create(::RUBY_VERSION) >= ::Gem::Version.create("2.6")
14✔
69
      ##
70
      # Converts `object` to a boolean by the following logic:
71
      # - Converts `object` to a string by the `#to_s` method and checks whether it is one of `["t", "T", "true", "True", "TRUE", "on", "On", "ON", "y", "Y", "yes", "Yes", "YES"]`.
72
      # - If yes, returns `true`, otherwise it converts `object` to an integer by `Kernel.Integer` and checks whether it is greater than zero.
73
      # - If yes, returns `true`, otherwise returns `false`.
74
      #
75
      # @param object [Object] Can be any type.
76
      # @return [Boolean]
77
      #
8✔
78
      def to_env_bool(object)
12✔
79
        string = object.to_s
3,762✔
80

81
        return false if string.empty?
3,750✔
82

83
        return true if TO_ENV_BOOL_TRUE_VALUES.include?(string)
3,726✔
84

85
        integer = ::Kernel.Integer(string, exception: false)
3,378✔
86

87
        return false unless integer
3,298✔
88

89
        integer > 0
144✔
90
      rescue ::Encoding::CompatibilityError
91
        false
80✔
92
      end
93
    else
94
      ##
95
      # Converts `object` to a boolean by the following logic:
96
      # - Converts `object` to a string by the `#to_s` method and checks whether it is one of `["t", "T", "true", "True", "TRUE", "on", "On", "ON", "y", "Y", "yes", "Yes", "YES"]`.
97
      # - If yes, returns `true`, otherwise it converts `object` to an integer by `Kernel.Integer` and checks whether it is greater than zero.
98
      # - If yes, returns `true`, otherwise returns `false`.
99
      #
100
      # @param object [Object] Can be any type.
101
      # @return [Boolean]
102
      #
103
      # rubocop:disable Lint/SuppressedExceptionInNumberConversion
1!
104
      def to_env_bool(object)
2✔
105
        string = object.to_s
620✔
106

107
        return false if string.empty?
618!
108

109
        return true if TO_ENV_BOOL_TRUE_VALUES.include?(string)
614!
110

111
        integer =
556✔
112
          begin
113
            ::Kernel.Integer(string)
556✔
114
          rescue
115
            nil
532✔
116
          end
117

118
        return false unless integer
556!
119

120
        integer > 0
24✔
121
      rescue ::Encoding::CompatibilityError
122
        false
×
123
      end
124
      # rubocop:enable Lint/SuppressedExceptionInNumberConversion
125
    end
126

127
    ##
128
    # Converts `object` to boolean using exactly the same logic as `blank?` in Rails does (but with `Hash` instead of `Concurent::Map` for string encodings storage).
129
    #
130
    # @param object [Object] Can be any type.
131
    # @return [Boolean]
132
    #
133
    # @note If performance is a concern, prefer to load Rails (or just `activesupport`) and use `blank?` directly.
134
    # @see https://api.rubyonrails.org/classes/Object.html#method-i-blank-3F
135
    # @see https://api.rubyonrails.org/classes/ActiveSupport/TimeWithZone.html#method-i-blank-3F
136
    # @see https://api.rubyonrails.org/classes/ActiveRecord/Relation.html#method-i-blank-3F
137
    #
138
    def blank?(object)
14✔
139
      return object.__send__(:blank?) if object.respond_to?(:blank?)
8,540✔
140

141
      case object
7,268✔
142
      when NilClass
18✔
143
        true
28✔
144
      when FalseClass
18✔
145
        true
28✔
146
      when TrueClass
18✔
147
        false
28✔
148
      when Array
36✔
149
        object.empty?
56✔
150
      when Hash
36✔
151
        object.empty?
56✔
152
      when Symbol
684✔
153
        object.empty?
1,064✔
154
      when String
4,138✔
155
        object.empty? ||
5,986✔
156
          begin
157
            ACTIVE_SUPPORT_CORE_EXT_BLANK_RE.match?(object)
6,412✔
158
          rescue ::Encoding::CompatibilityError
159
            ACTIVE_SUPPORT_CORE_EXT_ENCODED_BLANKS[object.encoding].match?(object)
224✔
160
          end
454✔
161
      when Numeric
288✔
162
        false
448✔
163
      when Time
18✔
164
        false
28✔
165
      when Object
198✔
166
        object.respond_to?(:empty?) ? !!object.empty? : false
316✔
167
      else
×
UNCOV
168
        object.__send__(:blank?)
×
169
      end
170
    end
171

172
    ##
173
    # Converts `object` to boolean using exactly the same logic as `present?` in Rails does (but with `Hash` instead of `Concurent::Map` for string encodings storage).
174
    #
175
    # @param object [Object] Can be any type.
176
    # @return [Boolean]
177
    #
178
    # @note If performance is a concern, prefer to load Rails (or just `activesupport`) and use `present?` directly.
179
    # @see https://api.rubyonrails.org/classes/Object.html#method-i-present-3F
180
    #
181
    def present?(object)
14✔
182
      !blank?(object)
4,576✔
183
    end
184

185
    ##
186
    # Converts `object` to boolean (or `nil`) using exactly the same logic as `ActiveModel::Type::Boolean.new.cast(object)` does.
187
    #
188
    # @param object [Object] Can be any type.
189
    # @return [Boolean, nil]
190
    #
191
    # @note If performance is a concern, prefer to load Rails (or just `activemodel`) and use `ActiveModel::Type::Boolean.new.cast(object)` directly.
192
    # @see https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html
193
    # @see https://github.com/rails/rails/blob/v8.0.2/activemodel/lib/active_model/type/boolean.rb#L39
194
    #
195
    def to_active_model_boolean_type(object)
14✔
196
      (object == "") ? nil : !TO_ACTIVE_MODEL_BOOLEAN_TYPE_FALSE_VALUES.include?(object)
4,695✔
197
    end
198

199
    ##
200
    # Converts `object` to `1` or `0`.
201
    # Uses `to_ruby_bool` method under the hood.
202
    # Accepts optional `by` keyword to rely on a different method.
203
    #
204
    # @param object [Object] Can be any type.
205
    # @param by [Symbol, String].
206
    # @return [Integer]
207
    #
208
    # @example Call without the `:by` keyword.
209
    #   SemanticBoolean.to_one_or_zero("")
210
    #   # => 1
211
    #
212
    # @example Call with the `:by` keyword.
213
    #   SemanticBoolean.to_one_or_zero("", by: :present?)
214
    #   # => 0
215
    #
216
    def to_one_or_zero(object, by: :to_ruby_bool)
14✔
217
      public_send(by, object) ? 1 : 0
140✔
218
    end
219

220
    ##
221
    # Converts `object` to `"y"` or `"n"`.
222
    # Uses `to_ruby_bool` method under the hood.
223
    # Accepts optional `by` keyword to rely on a different method.
224
    #
225
    # @param object [Object] Can be any type.
226
    # @param by [Symbol, String].
227
    # @return [String]
228
    #
229
    # @example Call without the `:by` keyword.
230
    #   SemanticBoolean.to_y_or_n("n")
231
    #   # => "y"
232
    #
233
    # @example Call with the `:by` keyword.
234
    #   SemanticBoolean.to_y_or_n("n", by: :to_env_bool)
235
    #   # => "n"
236
    #
237
    def to_y_or_n(object, by: :to_ruby_bool)
14✔
238
      public_send(by, object) ? "y" : "n"
140✔
239
    end
240

241
    ##
242
    # Converts `object` to `"yes"` or `"no"`.
243
    # Uses `to_ruby_bool` method under the hood.
244
    # Accepts optional `by` keyword to rely on a different method.
245
    #
246
    # @param object [Object] Can be any type.
247
    # @param by [Symbol, String].
248
    # @return [String]
249
    #
250
    # @example Call without the `:by` keyword.
251
    #   SemanticBoolean.to_yes_or_no([])
252
    #   # => "yes"
253
    #
254
    # @example Call with the `:by` keyword.
255
    #   SemanticBoolean.to_yes_or_no([], by: :present?)
256
    #   # => "no"
257
    #
258
    def to_yes_or_no(object, by: :to_ruby_bool)
14✔
259
      public_send(by, object) ? "yes" : "no"
140✔
260
    end
261

262
    ##
263
    # Converts `object` to `"on"` or `"off"`.
264
    # Uses `to_ruby_bool` method under the hood.
265
    # Accepts optional `by` keyword to rely on a different method.
266
    #
267
    # @param object [Object] Can be any type.
268
    # @param by [Symbol, String].
269
    # @return [String]
270
    #
271
    # @example Call without the `:by` keyword.
272
    #   SemanticBoolean.to_on_or_off(false)
273
    #   # => "off"
274
    #
275
    # @example Call with the `:by` keyword.
276
    #   SemanticBoolean.to_yes_or_no(false, by: :blank?)
277
    #   # => "on"
278
    #
279
    def to_on_or_off(object, by: :to_ruby_bool)
14✔
280
      public_send(by, object) ? "on" : "off"
140✔
281
    end
282
  end
283
end
284
# rubocop:enable Lint/BooleanSymbol
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc