dry-logic 0.6.0 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +2 -5
  3. data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +10 -0
  4. data/.github/ISSUE_TEMPLATE/---bug-report.md +34 -0
  5. data/.github/ISSUE_TEMPLATE/---feature-request.md +18 -0
  6. data/.github/workflows/ci.yml +70 -0
  7. data/.github/workflows/docsite.yml +34 -0
  8. data/.github/workflows/sync_configs.yml +34 -0
  9. data/.gitignore +2 -1
  10. data/.rspec +1 -0
  11. data/.rubocop.yml +89 -0
  12. data/CHANGELOG.md +51 -2
  13. data/CODE_OF_CONDUCT.md +13 -0
  14. data/CONTRIBUTING.md +3 -3
  15. data/Gemfile +3 -0
  16. data/LICENSE +1 -1
  17. data/README.md +7 -7
  18. data/Rakefile +2 -0
  19. data/benchmarks/rule_application.rb +2 -0
  20. data/benchmarks/setup.rb +2 -0
  21. data/bin/console +1 -0
  22. data/docsite/source/index.html.md +54 -0
  23. data/docsite/source/operations.html.md +62 -0
  24. data/docsite/source/predicates.html.md +102 -0
  25. data/dry-logic.gemspec +8 -0
  26. data/examples/basic.rb +2 -0
  27. data/lib/dry-logic.rb +2 -0
  28. data/lib/dry/logic.rb +2 -0
  29. data/lib/dry/logic/appliable.rb +2 -0
  30. data/lib/dry/logic/evaluator.rb +2 -0
  31. data/lib/dry/logic/operations.rb +2 -0
  32. data/lib/dry/logic/operations/abstract.rb +5 -3
  33. data/lib/dry/logic/operations/and.rb +3 -1
  34. data/lib/dry/logic/operations/attr.rb +2 -0
  35. data/lib/dry/logic/operations/binary.rb +2 -0
  36. data/lib/dry/logic/operations/check.rb +3 -1
  37. data/lib/dry/logic/operations/each.rb +2 -0
  38. data/lib/dry/logic/operations/implication.rb +2 -0
  39. data/lib/dry/logic/operations/key.rb +4 -2
  40. data/lib/dry/logic/operations/negation.rb +2 -0
  41. data/lib/dry/logic/operations/or.rb +2 -0
  42. data/lib/dry/logic/operations/set.rb +2 -0
  43. data/lib/dry/logic/operations/unary.rb +2 -0
  44. data/lib/dry/logic/operations/xor.rb +2 -0
  45. data/lib/dry/logic/operators.rb +2 -0
  46. data/lib/dry/logic/predicates.rb +39 -21
  47. data/lib/dry/logic/result.rb +2 -0
  48. data/lib/dry/logic/rule.rb +5 -3
  49. data/lib/dry/logic/rule/interface.rb +20 -0
  50. data/lib/dry/logic/rule/predicate.rb +2 -0
  51. data/lib/dry/logic/rule_compiler.rb +2 -0
  52. data/lib/dry/logic/version.rb +3 -1
  53. data/spec/integration/result_spec.rb +2 -0
  54. data/spec/integration/rule_spec.rb +2 -0
  55. data/spec/shared/predicates.rb +2 -0
  56. data/spec/shared/rule.rb +2 -0
  57. data/spec/spec_helper.rb +6 -10
  58. data/spec/support/mutant.rb +2 -0
  59. data/spec/unit/operations/and_spec.rb +2 -0
  60. data/spec/unit/operations/attr_spec.rb +2 -0
  61. data/spec/unit/operations/check_spec.rb +2 -0
  62. data/spec/unit/operations/each_spec.rb +2 -0
  63. data/spec/unit/operations/implication_spec.rb +2 -0
  64. data/spec/unit/operations/key_spec.rb +2 -0
  65. data/spec/unit/operations/negation_spec.rb +2 -0
  66. data/spec/unit/operations/or_spec.rb +2 -0
  67. data/spec/unit/operations/set_spec.rb +2 -0
  68. data/spec/unit/operations/xor_spec.rb +2 -0
  69. data/spec/unit/predicates/array_spec.rb +2 -0
  70. data/spec/unit/predicates/attr_spec.rb +2 -0
  71. data/spec/unit/predicates/bool_spec.rb +3 -1
  72. data/spec/unit/predicates/bytesize_spec.rb +48 -0
  73. data/spec/unit/predicates/case_spec.rb +2 -0
  74. data/spec/unit/predicates/date_spec.rb +2 -0
  75. data/spec/unit/predicates/date_time_spec.rb +3 -1
  76. data/spec/unit/predicates/decimal_spec.rb +3 -1
  77. data/spec/unit/predicates/empty_spec.rb +2 -0
  78. data/spec/unit/predicates/eql_spec.rb +2 -0
  79. data/spec/unit/predicates/even_spec.rb +2 -0
  80. data/spec/unit/predicates/excluded_from_spec.rb +2 -0
  81. data/spec/unit/predicates/excludes_spec.rb +2 -0
  82. data/spec/unit/predicates/false_spec.rb +3 -1
  83. data/spec/unit/predicates/filled_spec.rb +2 -0
  84. data/spec/unit/predicates/float_spec.rb +2 -0
  85. data/spec/unit/predicates/format_spec.rb +2 -0
  86. data/spec/unit/predicates/gt_spec.rb +2 -0
  87. data/spec/unit/predicates/gteq_spec.rb +2 -0
  88. data/spec/unit/predicates/hash_spec.rb +40 -0
  89. data/spec/unit/predicates/included_in_spec.rb +2 -0
  90. data/spec/unit/predicates/int_spec.rb +2 -0
  91. data/spec/unit/predicates/key_spec.rb +2 -0
  92. data/spec/unit/predicates/lt_spec.rb +2 -0
  93. data/spec/unit/predicates/lteq_spec.rb +2 -0
  94. data/spec/unit/predicates/max_bytesize_spec.rb +39 -0
  95. data/spec/unit/predicates/max_size_spec.rb +2 -0
  96. data/spec/unit/predicates/min_bytesize_spec.rb +39 -0
  97. data/spec/unit/predicates/min_size_spec.rb +2 -0
  98. data/spec/unit/predicates/none_spec.rb +2 -0
  99. data/spec/unit/predicates/not_eql_spec.rb +2 -0
  100. data/spec/unit/predicates/number_spec.rb +2 -0
  101. data/spec/unit/predicates/odd_spec.rb +2 -0
  102. data/spec/unit/predicates/respond_to_spec.rb +31 -0
  103. data/spec/unit/predicates/size_spec.rb +2 -0
  104. data/spec/unit/predicates/str_spec.rb +2 -0
  105. data/spec/unit/predicates/time_spec.rb +3 -1
  106. data/spec/unit/predicates/true_spec.rb +3 -1
  107. data/spec/unit/predicates/type_spec.rb +2 -0
  108. data/spec/unit/predicates/uuid_v4_spec.rb +29 -0
  109. data/spec/unit/predicates_spec.rb +2 -0
  110. data/spec/unit/rule/predicate_spec.rb +2 -0
  111. data/spec/unit/rule_compiler_spec.rb +2 -0
  112. data/spec/unit/rule_spec.rb +14 -3
  113. metadata +30 -6
  114. data/.travis.yml +0 -31
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/logic/operations/unary'
2
4
  require 'dry/logic/result'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/logic/operations/binary'
2
4
  require 'dry/logic/result'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/logic/operations/abstract'
2
4
  require 'dry/logic/result'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/logic/operations/abstract'
2
4
 
3
5
  module Dry
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/logic/operations/binary'
2
4
  require 'dry/logic/result'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  module Logic
3
5
  module Operators
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bigdecimal'
2
4
  require 'bigdecimal/util'
3
5
  require 'date'
@@ -57,11 +59,9 @@ module Dry
57
59
  end
58
60
 
59
61
  def number?(input)
60
- begin
61
- true if Float(input)
62
- rescue ArgumentError, TypeError
63
- false
64
- end
62
+ true if Float(input)
63
+ rescue ArgumentError, TypeError
64
+ false
65
65
  end
66
66
 
67
67
  def int?(input)
@@ -114,7 +114,7 @@ module Dry
114
114
 
115
115
  def size?(size, input)
116
116
  case size
117
- when Integer then size == input.size
117
+ when Integer then size.equal?(input.size)
118
118
  when Range, Array then size.include?(input.size)
119
119
  else
120
120
  raise ArgumentError, "+#{size}+ is not supported type for size? predicate."
@@ -129,6 +129,23 @@ module Dry
129
129
  input.size <= num
130
130
  end
131
131
 
132
+ def bytesize?(size, input)
133
+ case size
134
+ when Integer then size.equal?(input.bytesize)
135
+ when Range, Array then size.include?(input.bytesize)
136
+ else
137
+ raise ArgumentError, "+#{size}+ is not supported type for bytesize? predicate."
138
+ end
139
+ end
140
+
141
+ def min_bytesize?(num, input)
142
+ input.bytesize >= num
143
+ end
144
+
145
+ def max_bytesize?(num, input)
146
+ input.bytesize <= num
147
+ end
148
+
132
149
  def inclusion?(list, input)
133
150
  ::Kernel.warn 'inclusion is deprecated - use included_in instead.'
134
151
  included_in?(list, input)
@@ -148,15 +165,13 @@ module Dry
148
165
  end
149
166
 
150
167
  def includes?(value, input)
151
- begin
152
- if input.respond_to?(:include?)
153
- input.include?(value)
154
- else
155
- false
156
- end
157
- rescue TypeError
168
+ if input.respond_to?(:include?)
169
+ input.include?(value)
170
+ else
158
171
  false
159
172
  end
173
+ rescue TypeError
174
+ false
160
175
  end
161
176
 
162
177
  def excludes?(value, input)
@@ -183,20 +198,23 @@ module Dry
183
198
  value.equal?(false)
184
199
  end
185
200
 
186
- if RUBY_VERSION < '2.4'
187
- def format?(regex, input)
188
- !regex.match(input).nil?
189
- end
190
- else
191
- def format?(regex, input)
192
- regex.match?(input)
193
- end
201
+ def format?(regex, input)
202
+ regex.match?(input)
194
203
  end
195
204
 
196
205
  def case?(pattern, input)
197
206
  pattern === input
198
207
  end
199
208
 
209
+ def uuid_v4?(input)
210
+ uuid_v4_format = /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i
211
+ format?(uuid_v4_format, input)
212
+ end
213
+
214
+ def respond_to?(method, input)
215
+ input.respond_to?(method)
216
+ end
217
+
200
218
  def predicate(name, &block)
201
219
  define_singleton_method(name, &block)
202
220
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/core/constants'
2
4
 
3
5
  module Dry
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'concurrent/map'
2
4
  require 'dry/core/constants'
3
5
  require 'dry/equalizer'
@@ -66,11 +68,11 @@ module Dry
66
68
 
67
69
  def bind(object)
68
70
  if predicate.respond_to?(:bind)
69
- self.class.build(predicate.bind(object), options)
71
+ self.class.build(predicate.bind(object), **options)
70
72
  else
71
73
  self.class.build(
72
74
  -> *args { object.instance_exec(*args, &predicate) },
73
- options.merge(arity: arity, parameters: parameters)
75
+ **options, arity: arity, parameters: parameters
74
76
  )
75
77
  end
76
78
  end
@@ -80,7 +82,7 @@ module Dry
80
82
  end
81
83
 
82
84
  def with(new_opts)
83
- self.class.build(predicate, options.merge(new_opts))
85
+ self.class.build(predicate, **options, **new_opts)
84
86
  end
85
87
 
86
88
  def parameters
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  module Logic
3
5
  class Rule
@@ -18,6 +20,8 @@ module Dry
18
20
 
19
21
  if variable_arity?
20
22
  define_splat_application
23
+ elsif constant?
24
+ define_constant_application
21
25
  else
22
26
  define_fixed_application
23
27
  end
@@ -71,6 +75,22 @@ module Dry
71
75
  RUBY
72
76
  end
73
77
 
78
+ def define_constant_application
79
+ module_exec do
80
+ def call(*)
81
+ if @predicate[]
82
+ Result::SUCCESS
83
+ else
84
+ Result.new(false, id) { ast }
85
+ end
86
+ end
87
+
88
+ def [](*)
89
+ @predicate[]
90
+ end
91
+ end
92
+ end
93
+
74
94
  def define_splat_application
75
95
  application =
76
96
  if curried?
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/logic/rule'
2
4
 
3
5
  module Dry
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/core/constants'
2
4
 
3
5
  require 'dry/logic/rule'
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  module Logic
3
- VERSION = '0.6.0'.freeze
5
+ VERSION = '1.0.4'
4
6
  end
5
7
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Result do
2
4
  include_context 'predicates'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry-logic'
2
4
 
3
5
  RSpec.describe 'Rules' do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/logic/predicates'
2
4
 
3
5
  RSpec.shared_examples 'predicates' do
data/spec/shared/rule.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  shared_examples_for Dry::Logic::Rule do
2
4
  let(:arity) { 2 }
3
5
  let(:predicate) { double(:predicate, arity: arity, name: predicate_name) }
data/spec/spec_helper.rb CHANGED
@@ -1,13 +1,9 @@
1
- if RUBY_ENGINE == 'ruby' && ENV['COVERAGE'] == 'true'
2
- require 'yaml'
3
- rubies = YAML.load(File.read(File.join(__dir__, '..', '.travis.yml')))['rvm']
4
- latest_mri = rubies.select { |v| v =~ /\A\d+\.\d+.\d+\z/ }.max
5
-
6
- if RUBY_VERSION == latest_mri
7
- require 'simplecov'
8
- SimpleCov.start do
9
- add_filter '/spec/'
10
- end
1
+ # frozen_string_literal: true
2
+
3
+ if ENV['COVERAGE'] == 'true'
4
+ require 'simplecov'
5
+ SimpleCov.start do
6
+ add_filter '/spec/'
11
7
  end
12
8
  end
13
9
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Mutant
2
4
  class Selector
3
5
  class Expression < self
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Operations::And do
2
4
  subject(:operation) { Operations::And.new(left, right) }
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Operations::Attr do
2
4
  subject(:operation) { Operations::Attr.new(Rule::Predicate.build(str?), name: :name) }
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Operations::Check do
2
4
  include_context 'predicates'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Operations::Each do
2
4
  subject(:operation) { Operations::Each.new(is_string) }
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Operations::Implication do
2
4
  subject(:operation) { Operations::Implication.new(left, right) }
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Operations::Key do
2
4
  subject(:operation) { Operations::Key.new(predicate, name: :user) }
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Operations::Negation do
2
4
  subject(:operation) { Operations::Negation.new(is_int) }
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Operations::Or do
2
4
  subject(:operation) { Operations::Or.new(left, right) }
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Operations::Set do
2
4
  subject(:operation) { Operations::Set.new(is_int, gt_18) }
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe Operations::Xor do
2
4
  subject(:operation) { Operations::Xor.new(left, right) }
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/logic/predicates'
2
4
 
3
5
  RSpec.describe Dry::Logic::Predicates do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/logic/predicates'
2
4
 
3
5
  RSpec.describe Dry::Logic::Predicates do
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'dry/logic/predicates'
2
4
 
3
5
  RSpec.describe Dry::Logic::Predicates do
4
6
  describe '#bool?' do
5
7
  let(:predicate_name) { :bool? }
6
8
 
7
- context 'when value is a date' do
9
+ context 'when value is a boolean' do
8
10
  let(:arguments_list) do
9
11
  [[true], [false]]
10
12
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/logic/predicates'
4
+
5
+ RSpec.describe Dry::Logic::Predicates do
6
+ describe '#bytesize?' do
7
+ let(:predicate_name) { :bytesize? }
8
+
9
+ context 'when value size is equal to n' do
10
+ let(:arguments_list) do
11
+ [
12
+ [4, 'こa'],
13
+ [1..8, 'こa']
14
+ ]
15
+ end
16
+
17
+ it_behaves_like 'a passing predicate'
18
+ end
19
+
20
+ context 'when value size is greater than n' do
21
+ let(:arguments_list) do
22
+ [
23
+ [3, 'こa'],
24
+ [1..3, 'こa']
25
+ ]
26
+ end
27
+
28
+ it_behaves_like 'a failing predicate'
29
+ end
30
+
31
+ context 'with value size is less than n' do
32
+ let(:arguments_list) do
33
+ [
34
+ [5, 'こa'],
35
+ [5..10, 'こa']
36
+ ]
37
+ end
38
+
39
+ it_behaves_like 'a failing predicate'
40
+ end
41
+
42
+ context 'with an unsupported size' do
43
+ it 'raises an error' do
44
+ expect { Predicates[:bytesize?].call('oops', 1) }.to raise_error(ArgumentError, /oops/)
45
+ end
46
+ end
47
+ end
48
+ end