active_record_extended 1.4.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -7
  3. data/lib/active_record_extended/active_record.rb +1 -10
  4. data/lib/active_record_extended/active_record/relation_patch.rb +16 -1
  5. data/lib/active_record_extended/arel.rb +1 -0
  6. data/lib/active_record_extended/arel/nodes.rb +22 -21
  7. data/lib/active_record_extended/arel/sql_literal.rb +16 -0
  8. data/lib/active_record_extended/query_methods/any_of.rb +5 -4
  9. data/lib/active_record_extended/query_methods/either.rb +1 -1
  10. data/lib/active_record_extended/query_methods/inet.rb +6 -2
  11. data/lib/active_record_extended/query_methods/json.rb +13 -16
  12. data/lib/active_record_extended/query_methods/select.rb +11 -10
  13. data/lib/active_record_extended/query_methods/unionize.rb +10 -4
  14. data/lib/active_record_extended/query_methods/where_chain.rb +14 -6
  15. data/lib/active_record_extended/query_methods/window.rb +4 -3
  16. data/lib/active_record_extended/query_methods/with_cte.rb +102 -35
  17. data/lib/active_record_extended/utilities/order_by.rb +9 -28
  18. data/lib/active_record_extended/utilities/support.rb +8 -15
  19. data/lib/active_record_extended/version.rb +1 -1
  20. data/spec/query_methods/any_of_spec.rb +2 -2
  21. data/spec/query_methods/json_spec.rb +5 -5
  22. data/spec/query_methods/select_spec.rb +13 -13
  23. data/spec/query_methods/unionize_spec.rb +5 -5
  24. data/spec/query_methods/with_cte_spec.rb +12 -2
  25. data/spec/spec_helper.rb +1 -1
  26. data/spec/sql_inspections/any_of_sql_spec.rb +2 -2
  27. data/spec/sql_inspections/contains_sql_queries_spec.rb +8 -8
  28. data/spec/sql_inspections/either_sql_spec.rb +3 -3
  29. data/spec/sql_inspections/json_sql_spec.rb +0 -1
  30. data/spec/sql_inspections/unionize_sql_spec.rb +2 -2
  31. data/spec/sql_inspections/window_sql_spec.rb +12 -0
  32. data/spec/sql_inspections/with_cte_sql_spec.rb +30 -1
  33. metadata +18 -20
  34. data/lib/active_record_extended/patch/5_0/predicate_builder_decorator.rb +0 -87
  35. data/lib/active_record_extended/patch/5_0/regex_match.rb +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 29516f7d7b4854e6a04653d6e146e5945efa9ade39ad99d171e17bd2f11e1fe3
4
- data.tar.gz: f898fa543fe7d79f9fa98e015e86044cccf7ae4c940abe13c912fbb9d435a010
3
+ metadata.gz: 9934d5a90324b46dc3faa7b496724ac219a2582d264db89e64e2b9be574adab6
4
+ data.tar.gz: c1359988f9a6b83148f36548e842a25cab68a191e7a839caef7e783b1415b346
5
5
  SHA512:
6
- metadata.gz: b10866d1c5e95ddb6e37df09085b1e86183bfa9b4b217b71f5f3ce8973926fc6f83591c352515098d5800276afaf1ff538ac50bea8cd5f355da7d67768570970
7
- data.tar.gz: 654a00730852609edf1ef1e47893896647d06548bc563aa4546713b7398981fbe050d7951dee47eb2b97c673e370ff40b0949aa763c6665662deb3c08625bc53
6
+ metadata.gz: e7385b414ef4c8d8b0c5b29eaeb70e77ba79f07828255deef5db79e99cf33f18de7bab696501711dee1f8e056ca3bdf628cdd498b341a91b5ad5cd2c766bd61c
7
+ data.tar.gz: 10e8d4f65c570dba757b2cf47aaaff7f8a5538974e9c8aadb3c267e3e3fccb9508b05a8722eb5d6680937cb15827f6fa4f88d6dd219d8240c58323385fe44eb2
data/README.md CHANGED
@@ -52,11 +52,12 @@ Active Record Extended is essentially providing users with the other half of Pos
52
52
  ## Compatibility
53
53
 
54
54
  This package is designed align and work with any officially supported Ruby and Rails versions.
55
- - Minimum Ruby Version: 2.3.x **(EOL warning!)**
56
- - Minimum Rails Version: 5.0.x **(EOL warning!)**
57
- - Latest Ruby supported: 2.6.x
58
- - Latest Rails supported: 6.0.x
59
- - Postgres: 9.6-current(11) (probably works with most older versions to a certain point)
55
+ - Minimum Ruby Version: 2.4.x **(EOL warning!)**
56
+ - Minimum Rails Version: 5.1.x **(EOL warning!)**
57
+ - Minimum Postgres Version: 9.6.x **(EOL warning!)**
58
+ - Latest Ruby supported: 2.7.x
59
+ - Latest Rails supported: 6.1.x
60
+ - Postgres: 9.6-current(13) (probably works with most older versions to a certain point)
60
61
 
61
62
  ## Installation
62
63
 
@@ -430,7 +431,7 @@ While quite the mouthful of an explanation. The implementation of combining unre
430
431
  product_query =
431
432
  Product.select(:id)
432
433
  .joins(:items)
433
- .select_row_to_json(item_query, key: :outer_items, as: :items, cast_as_array: true) do |item_scope|
434
+ .select_row_to_json(item_query, key: :outer_items, as: :items, cast_with: :array) do |item_scope|
434
435
  item_scope.where("outer_items.product_id = products.id")
435
436
  # Results to:
436
437
  # SELECT ..., ARRAY(SELECT ROW_TO_JSON("outer_items")
@@ -440,7 +441,7 @@ While quite the mouthful of an explanation. The implementation of combining unre
440
441
  end
441
442
 
442
443
  # Not defining a key will automatically generate a random key between a-z
443
- category_query = Category.select(:name, :id).select_row_to_json(product_query, as: :products, cast_as_array: true)
444
+ category_query = Category.select(:name, :id).select_row_to_json(product_query, as: :products, cast_with: :array)
444
445
  Category.json_build_object(:physical_category, category_query.where(id: physical_cat.id)).results
445
446
  #=> {
446
447
  # "physical_category" => {
@@ -1,10 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # TODO: Remove this when ruby 2.3 support is dropped
4
- unless Hash.instance_methods(false).include?(:compact!)
5
- require "active_support/all"
6
- end
7
-
8
3
  require "active_record"
9
4
  require "active_record/relation"
10
5
  require "active_record/relation/merger"
@@ -23,11 +18,7 @@ require "active_record_extended/query_methods/inet"
23
18
  require "active_record_extended/query_methods/json"
24
19
  require "active_record_extended/query_methods/select"
25
20
 
26
- if ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR <= 1
27
- if ActiveRecord::VERSION::MINOR.zero?
28
- require "active_record_extended/patch/5_0/regex_match"
29
- require "active_record_extended/patch/5_0/predicate_builder_decorator"
30
- end
21
+ if ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR == 1
31
22
  require "active_record_extended/patch/5_1/where_clause"
32
23
  elsif ActiveRecord::VERSION::MAJOR >= 5
33
24
  require "active_record_extended/patch/5_2/where_clause"
@@ -14,7 +14,22 @@ module ActiveRecordExtended
14
14
 
15
15
  module Merger
16
16
  def normal_values
17
- super + [:with, :union, :define_window]
17
+ super + [:union, :define_window]
18
+ end
19
+
20
+ def merge
21
+ merge_ctes!
22
+ super
23
+ end
24
+
25
+ def merge_ctes!
26
+ return unless other.with_values?
27
+
28
+ if other.recursive_value? && !relation.recursive_value?
29
+ relation.with!(:chain).recursive(other.cte)
30
+ else
31
+ relation.with!(other.cte)
32
+ end
18
33
  end
19
34
  end
20
35
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_record_extended/arel/nodes"
4
+ require "active_record_extended/arel/sql_literal"
4
5
  require "active_record_extended/arel/aggregate_function_name"
5
6
  require "active_record_extended/arel/predications"
6
7
  require "active_record_extended/arel/visitors/postgresql_decorator"
@@ -5,28 +5,29 @@ require "arel/nodes/function"
5
5
 
6
6
  module Arel
7
7
  module Nodes
8
- %w[
9
- Overlap
10
- Contains
11
- ContainsHStore
12
- ContainsArray
13
- ContainedInArray
8
+ [
9
+ "Overlap",
10
+ "Contains",
11
+ "ContainsHStore",
12
+ "ContainsArray",
13
+ "ContainedInArray"
14
14
  ].each { |binary_node_name| const_set(binary_node_name, Class.new(::Arel::Nodes::Binary)) }
15
15
 
16
- %w[
17
- RowToJson
18
- JsonBuildObject
19
- JsonbBuildObject
20
- ToJson
21
- ToJsonb
22
- Array
23
- ArrayAgg
16
+ [
17
+ "RowToJson",
18
+ "JsonBuildObject",
19
+ "JsonbBuildObject",
20
+ "ToJson",
21
+ "ToJsonb",
22
+ "Array",
23
+ "ArrayAgg"
24
24
  ].each do |function_node_name|
25
25
  func_klass = Class.new(::Arel::Nodes::Function) do
26
26
  def initialize(*args)
27
27
  super
28
28
  return if @expressions.is_a?(::Array)
29
- @expressions = @expressions.is_a?(::Arel::Node) ? [@expressions] : [::Arel.sql(@expressions)]
29
+
30
+ @expressions = @expressions.is_a?(::Arel::Nodes::Node) ? [@expressions] : [::Arel.sql(@expressions)]
30
31
  end
31
32
  end
32
33
 
@@ -34,12 +35,12 @@ module Arel
34
35
  end
35
36
 
36
37
  module Inet
37
- %w[
38
- Contains
39
- ContainsEquals
40
- ContainedWithin
41
- ContainedWithinEquals
42
- ContainsOrContainedWithin
38
+ [
39
+ "Contains",
40
+ "ContainsEquals",
41
+ "ContainedWithin",
42
+ "ContainedWithinEquals",
43
+ "ContainsOrContainedWithin"
43
44
  ].each { |binary_node_name| const_set(binary_node_name, Class.new(::Arel::Nodes::Binary)) }
44
45
  end
45
46
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "arel/nodes/sql_literal"
4
+
5
+ # CTE alias fix for Rails 6.1
6
+ module Arel
7
+ module Nodes
8
+ module SqlLiteralDecorator
9
+ def name
10
+ self
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ Arel::Nodes::SqlLiteral.prepend(Arel::Nodes::SqlLiteralDecorator)
@@ -61,13 +61,14 @@ module ActiveRecordExtended
61
61
  # In Rails 5.2 the arel table maintains attribute binds
62
62
  def bind_attributes(query)
63
63
  return [] unless query.respond_to?(:bound_attributes)
64
+
64
65
  query.bound_attributes.map(&:value)
65
66
  end
66
67
 
67
68
  # Rails 5.1 fix
68
69
  def unprepared_query(query)
69
- query.gsub(/((?<!\\)'.*?(?<!\\)'|(?<!\\)".*?(?<!\\)")|(\=\ \$\d+)/) do |match|
70
- Regexp.last_match(2)&.gsub(/\=\ \$\d+/, "= ?") || match
70
+ query.gsub(/((?<!\\)'.*?(?<!\\)'|(?<!\\)".*?(?<!\\)")|(=\ \$\d+)/) do |match|
71
+ Regexp.last_match(2)&.gsub(/=\ \$\d+/, "= ?") || match
71
72
  end
72
73
  end
73
74
 
@@ -78,9 +79,9 @@ module ActiveRecordExtended
78
79
  def generate_where_clause(query)
79
80
  case query
80
81
  when String, Hash
81
- @scope.where(query)
82
+ @scope.unscoped.where(query)
82
83
  when Array
83
- @scope.where(*query)
84
+ @scope.unscoped.where(*query)
84
85
  else
85
86
  query
86
87
  end
@@ -30,7 +30,7 @@ module ActiveRecordExtended
30
30
  end
31
31
 
32
32
  def sort_order_sql(dir)
33
- %w[asc desc].include?(dir.to_s) ? dir.to_s : "asc"
33
+ ["asc", "desc"].include?(dir.to_s) ? dir.to_s : "asc"
34
34
  end
35
35
 
36
36
  def xor_field_options(options)
@@ -74,8 +74,12 @@ module ActiveRecordExtended
74
74
  # #=> "SELECT \"users\".* FROM \"users\" WHERE \"users\".\"ip\" && '127.0.0.255/32'"
75
75
  #
76
76
  def inet_contains_or_is_contained_within(opts, *rest)
77
- substitute_comparisons(opts, rest, Arel::Nodes::Inet::ContainsOrContainedWithin,
78
- "inet_contains_or_is_contained_within")
77
+ substitute_comparisons(
78
+ opts,
79
+ rest,
80
+ Arel::Nodes::Inet::ContainsOrContainedWithin,
81
+ "inet_contains_or_is_contained_within"
82
+ )
79
83
  end
80
84
  end
81
85
  end
@@ -8,7 +8,7 @@ module ActiveRecordExtended
8
8
  :json_build_object,
9
9
  :jsonb_build_object,
10
10
  :json_build_literal,
11
- :jsonb_build_literal,
11
+ :jsonb_build_literal
12
12
  ].freeze
13
13
 
14
14
  class JsonChain
@@ -77,7 +77,7 @@ module ActiveRecordExtended
77
77
  row_to_json = ::Arel::Nodes::ToJsonb.new(row_to_json) if options.dig(:cast_with, :to_jsonb)
78
78
 
79
79
  dummy_table = from_clause_constructor(from, key).select(row_to_json)
80
- dummy_table = dummy_table.instance_eval(&block) if block_given?
80
+ dummy_table = dummy_table.instance_eval(&block) if block
81
81
  return dummy_table if options[:col_alias].blank?
82
82
 
83
83
  query = wrap_row_to_json(dummy_table, options)
@@ -98,7 +98,6 @@ module ActiveRecordExtended
98
98
  end
99
99
  end
100
100
 
101
- # TODO: [V2 release] Drop support for option :cast_as_array in favor of a more versatile :cast_with option
102
101
  def json_object_options(args, except: [], only: []) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
103
102
  options = {}
104
103
  lean_opts = lambda do |key, &block|
@@ -113,12 +112,12 @@ module ActiveRecordExtended
113
112
  next if arg.nil?
114
113
 
115
114
  if arg.is_a?(Hash)
116
- lean_opts.call(:key) { arg.delete(:key) || key_generator }
117
- lean_opts.call(:value) { arg.delete(:value).presence }
118
- lean_opts.call(:col_alias) { arg.delete(:as) }
119
- lean_opts.call(:order_by) { order_by_expression(arg.delete(:order_by)) }
120
- lean_opts.call(:from) { arg.delete(:from).tap { |from_clause| pipe_cte_with!(from_clause) } }
121
- lean_opts.call(:cast_with) { casting_options(arg.delete(:cast_with) || arg.delete(:cast_as_array)) }
115
+ lean_opts.call(:key) { arg.fetch(:key, key_generator) }
116
+ lean_opts.call(:value) { arg[:value].presence }
117
+ lean_opts.call(:col_alias) { arg[:as] }
118
+ lean_opts.call(:order_by) { order_by_expression(arg[:order_by]) }
119
+ lean_opts.call(:from) { arg[:from].tap { |from_clause| pipe_cte_with!(from_clause) } }
120
+ lean_opts.call(:cast_with) { casting_options(arg[:cast_with]) }
122
121
  end
123
122
 
124
123
  unless except.include?(:values)
@@ -155,9 +154,6 @@ module ActiveRecordExtended
155
154
  # - key: [Symbol or String] (default=[random letter]) What the row clause will be set as.
156
155
  # - This is useful if you would like to add additional mid-level clauses (see mid-level scope example)
157
156
  #
158
- # - cast_as_array [boolean] (default=false): Determines if the query should be nested inside an Array() function
159
- # * Will be deprecated in V2.0 in favor of `cast_with` argument
160
- #
161
157
  # - cast_with [Symbol or Array of symbols]: Actions to transform your query
162
158
  # * :to_jsonb
163
159
  # * :array
@@ -172,13 +168,13 @@ module ActiveRecordExtended
172
168
  #
173
169
  # Examples:
174
170
  # subquery = Group.select(:name, :category_id).where("user_id = users.id")
175
- # User.select(:name, email).select_row_to_json(subquery, as: :users_groups, cast_as_array: true)
171
+ # User.select(:name, email).select_row_to_json(subquery, as: :users_groups, cast_with: :array)
176
172
  # #=> [<#User name:.., email:.., users_groups: [{ name: .., category_id: .. }, ..]]
177
173
  #
178
174
  # - Adding mid-level scopes:
179
175
  #
180
176
  # subquery = Group.select(:name, :category_id)
181
- # User.select_row_to_json(subquery, key: :group, cast_as_array: true) do |scope|
177
+ # User.select_row_to_json(subquery, key: :group, cast_with: :array) do |scope|
182
178
  # scope.where(group: { name: "Nerd Core" })
183
179
  # end
184
180
  # #=> ```sql
@@ -252,7 +248,8 @@ module ActiveRecordExtended
252
248
  def select_row_to_json(from = nil, **options, &block)
253
249
  from.is_a?(Hash) ? options.merge!(from) : options.reverse_merge!(from: from)
254
250
  options.compact!
255
- raise ArgumentError, "Required to provide a non-nilled from clause" unless options.key?(:from)
251
+ raise ArgumentError.new("Required to provide a non-nilled from clause") unless options.key?(:from)
252
+
256
253
  JsonChain.new(spawn).row_to_json!(**options, &block)
257
254
  end
258
255
 
@@ -273,7 +270,7 @@ module ActiveRecordExtended
273
270
  # - Generic example:
274
271
  #
275
272
  # subquery = Group.select(:name, :category_id).where("user_id = users.id")
276
- # User.select(:name, email).select_row_to_json(subquery, as: :users_groups, cast_as_array: true)
273
+ # User.select(:name, email).select_row_to_json(subquery, as: :users_groups, cast_with: :array)
277
274
  # #=> [<#User name:.., email:.., users_groups: [{ name: .., category_id: .. }, ..]]
278
275
  #
279
276
  # - Setting a custom value:
@@ -52,12 +52,12 @@ module ActiveRecordExtended
52
52
  # #=> SELECT (ARRAY_AGG(DISTINCT members.price)) AS past_purchases, ...
53
53
  def process_hash!(hash_of_options, alias_name)
54
54
  enforced_options = {
55
- cast_with: hash_of_options.delete(:cast_with),
56
- order_by: hash_of_options.delete(:order_by),
57
- distinct: !(!hash_of_options.delete(:distinct)),
55
+ cast_with: hash_of_options[:cast_with],
56
+ order_by: hash_of_options[:order_by],
57
+ distinct: !(!hash_of_options[:distinct])
58
58
  }
59
- query_statement = hash_to_dot_notation(hash_of_options.delete(:__select_statement) || hash_of_options.first)
60
- select!(query_statement, alias_name, enforced_options)
59
+ query_statement = hash_to_dot_notation(hash_of_options[:__select_statement] || hash_of_options.first)
60
+ select!(query_statement, alias_name, **enforced_options)
61
61
  end
62
62
 
63
63
  # Turn a hash chain into a query statement:
@@ -76,7 +76,7 @@ module ActiveRecordExtended
76
76
  # Add's select statement values to the current relation, select statement lists
77
77
  def select!(query, alias_name = nil, **options)
78
78
  pipe_cte_with!(query)
79
- @scope._select!(to_casted_query(query, alias_name, options))
79
+ @scope._select!(to_casted_query(query, alias_name, **options))
80
80
  end
81
81
 
82
82
  # Wraps the query with the requested query method
@@ -84,9 +84,9 @@ module ActiveRecordExtended
84
84
  # to_casted_query("memberships.cost", :total_revenue, :sum)
85
85
  # #=> SELECT (SUM(memberships.cost)) AS total_revenue
86
86
  def to_casted_query(query, alias_name, **options)
87
- cast_with = options.delete(:cast_with).to_s.downcase
88
- order_expr = order_by_expression(options.delete(:order_by))
89
- distinct = cast_with.chomp!("_distinct") || options.delete(:distinct) # account for [:agg_name:]_distinct
87
+ cast_with = options[:cast_with].to_s.downcase
88
+ order_expr = order_by_expression(options[:order_by])
89
+ distinct = cast_with.chomp!("_distinct") || options[:distinct] # account for [:agg_name:]_distinct
90
90
 
91
91
  case cast_with
92
92
  when "array", "true"
@@ -102,7 +102,8 @@ module ActiveRecordExtended
102
102
  end
103
103
 
104
104
  def foster_select(*args)
105
- raise ArgumentError, "Call `.forster_select' with at least one field" if args.empty?
105
+ raise ArgumentError.new("Call `.forster_select' with at least one field") if args.empty?
106
+
106
107
  spawn._foster_select!(*args)
107
108
  end
108
109
 
@@ -81,7 +81,7 @@ module ActiveRecordExtended
81
81
  union_values: [],
82
82
  union_operations: [],
83
83
  union_ordering_values: [],
84
- unionized_name: nil,
84
+ unionized_name: nil
85
85
  }
86
86
  end
87
87
 
@@ -89,10 +89,11 @@ module ActiveRecordExtended
89
89
  union_values: Array,
90
90
  union_operations: Array,
91
91
  union_ordering_values: Array,
92
- unionized_name: lambda { |klass| klass.arel_table.name },
92
+ unionized_name: lambda { |klass| klass.arel_table.name }
93
93
  }.each_pair do |method_name, default|
94
94
  define_method(method_name) do
95
95
  return unionize_storage[method_name] if send("#{method_name}?")
96
+
96
97
  (default.is_a?(Proc) ? default.call(@klass) : default.new)
97
98
  end
98
99
 
@@ -107,11 +108,13 @@ module ActiveRecordExtended
107
108
 
108
109
  def union(opts = :chain, *args)
109
110
  return UnionChain.new(spawn) if opts == :chain
111
+
110
112
  opts.nil? ? self : spawn.union!(opts, *args, chain_method: __callee__)
111
113
  end
112
114
 
113
115
  (UNIONIZE_METHODS + UNION_RELATION_METHODS).each do |union_method|
114
116
  next if union_method == :union
117
+
115
118
  alias_method union_method, :union
116
119
  end
117
120
 
@@ -126,11 +129,13 @@ module ActiveRecordExtended
126
129
  # Will construct *Just* the union SQL statement that was been built thus far
127
130
  def to_union_sql
128
131
  return unless union_values?
132
+
129
133
  apply_union_ordering(build_union_nodes!(false)).to_sql
130
134
  end
131
135
 
132
136
  def to_nice_union_sql(color = true)
133
137
  return to_union_sql unless defined?(::Niceql)
138
+
134
139
  ::Niceql::Prettifier.prettify_sql(to_union_sql, color)
135
140
  end
136
141
 
@@ -172,7 +177,7 @@ module ActiveRecordExtended
172
177
 
173
178
  def build_union_nodes!(raise_error = true)
174
179
  unionize_error_or_warn!(raise_error)
175
- union_values.each_with_index.inject(nil) do |union_node, (relation_node, index)|
180
+ union_values.each_with_index.reduce(nil) do |union_node, (relation_node, index)|
176
181
  next resolve_relation_node(relation_node) if union_node.nil?
177
182
 
178
183
  operation = union_operations.fetch(index - 1, :union)
@@ -215,6 +220,7 @@ module ActiveRecordExtended
215
220
  #
216
221
  def apply_union_ordering(union_nodes)
217
222
  return union_nodes unless union_ordering_values?
223
+
218
224
  UnionChain.new(self).inline_order_by(union_nodes, union_ordering_values)
219
225
  end
220
226
 
@@ -222,7 +228,7 @@ module ActiveRecordExtended
222
228
 
223
229
  def unionize_error_or_warn!(raise_error = true)
224
230
  if raise_error && union_values.size <= 1
225
- raise ArgumentError, "You are required to provide 2 or more unions to join!"
231
+ raise ArgumentError.new("You are required to provide 2 or more unions to join!")
226
232
  elsif !raise_error && union_values.size <= 1
227
233
  warn("Warning: You are required to provide 2 or more unions to join.")
228
234
  end