params_ready 0.0.1

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 (83) hide show
  1. checksums.yaml +7 -0
  2. data/lib/arel/cte_name.rb +20 -0
  3. data/lib/params_ready.rb +36 -0
  4. data/lib/params_ready/builder.rb +140 -0
  5. data/lib/params_ready/error.rb +31 -0
  6. data/lib/params_ready/extensions/class_reader_writer.rb +33 -0
  7. data/lib/params_ready/extensions/collection.rb +43 -0
  8. data/lib/params_ready/extensions/delegation.rb +25 -0
  9. data/lib/params_ready/extensions/finalizer.rb +26 -0
  10. data/lib/params_ready/extensions/freezer.rb +49 -0
  11. data/lib/params_ready/extensions/hash.rb +46 -0
  12. data/lib/params_ready/extensions/late_init.rb +38 -0
  13. data/lib/params_ready/extensions/registry.rb +44 -0
  14. data/lib/params_ready/extensions/undefined.rb +15 -0
  15. data/lib/params_ready/format.rb +130 -0
  16. data/lib/params_ready/helpers/arel_builder.rb +68 -0
  17. data/lib/params_ready/helpers/conditional_block.rb +31 -0
  18. data/lib/params_ready/helpers/find_in_hash.rb +22 -0
  19. data/lib/params_ready/helpers/key_map.rb +176 -0
  20. data/lib/params_ready/helpers/memo.rb +42 -0
  21. data/lib/params_ready/helpers/options.rb +39 -0
  22. data/lib/params_ready/helpers/parameter_definer_class_methods.rb +39 -0
  23. data/lib/params_ready/helpers/parameter_storage_class_methods.rb +36 -0
  24. data/lib/params_ready/helpers/parameter_user_class_methods.rb +31 -0
  25. data/lib/params_ready/helpers/relation_builder_wrapper.rb +35 -0
  26. data/lib/params_ready/helpers/rule.rb +57 -0
  27. data/lib/params_ready/helpers/storage.rb +30 -0
  28. data/lib/params_ready/helpers/usage_rule.rb +18 -0
  29. data/lib/params_ready/input_context.rb +31 -0
  30. data/lib/params_ready/intent.rb +70 -0
  31. data/lib/params_ready/marshaller/array_marshallers.rb +132 -0
  32. data/lib/params_ready/marshaller/builder_module.rb +9 -0
  33. data/lib/params_ready/marshaller/collection.rb +165 -0
  34. data/lib/params_ready/marshaller/definition_module.rb +63 -0
  35. data/lib/params_ready/marshaller/hash_marshallers.rb +100 -0
  36. data/lib/params_ready/marshaller/hash_set_marshallers.rb +96 -0
  37. data/lib/params_ready/marshaller/parameter_module.rb +11 -0
  38. data/lib/params_ready/marshaller/polymorph_marshallers.rb +67 -0
  39. data/lib/params_ready/marshaller/tuple_marshallers.rb +103 -0
  40. data/lib/params_ready/ordering/column.rb +60 -0
  41. data/lib/params_ready/ordering/ordering.rb +276 -0
  42. data/lib/params_ready/output_parameters.rb +127 -0
  43. data/lib/params_ready/pagination/abstract_pagination.rb +18 -0
  44. data/lib/params_ready/pagination/cursor.rb +171 -0
  45. data/lib/params_ready/pagination/direction.rb +148 -0
  46. data/lib/params_ready/pagination/keyset_pagination.rb +254 -0
  47. data/lib/params_ready/pagination/keysets.rb +70 -0
  48. data/lib/params_ready/pagination/nulls.rb +31 -0
  49. data/lib/params_ready/pagination/offset_pagination.rb +130 -0
  50. data/lib/params_ready/pagination/tendency.rb +28 -0
  51. data/lib/params_ready/parameter/abstract_hash_parameter.rb +204 -0
  52. data/lib/params_ready/parameter/array_parameter.rb +197 -0
  53. data/lib/params_ready/parameter/definition.rb +264 -0
  54. data/lib/params_ready/parameter/hash_parameter.rb +63 -0
  55. data/lib/params_ready/parameter/hash_set_parameter.rb +101 -0
  56. data/lib/params_ready/parameter/parameter.rb +456 -0
  57. data/lib/params_ready/parameter/polymorph_parameter.rb +172 -0
  58. data/lib/params_ready/parameter/state.rb +132 -0
  59. data/lib/params_ready/parameter/tuple_parameter.rb +152 -0
  60. data/lib/params_ready/parameter/value_parameter.rb +182 -0
  61. data/lib/params_ready/parameter_definer.rb +14 -0
  62. data/lib/params_ready/parameter_user.rb +43 -0
  63. data/lib/params_ready/query/array_grouping.rb +68 -0
  64. data/lib/params_ready/query/custom_predicate.rb +102 -0
  65. data/lib/params_ready/query/exists_predicate.rb +103 -0
  66. data/lib/params_ready/query/fixed_operator_predicate.rb +77 -0
  67. data/lib/params_ready/query/grouping.rb +177 -0
  68. data/lib/params_ready/query/join_clause.rb +87 -0
  69. data/lib/params_ready/query/nullness_predicate.rb +71 -0
  70. data/lib/params_ready/query/polymorph_predicate.rb +77 -0
  71. data/lib/params_ready/query/predicate.rb +203 -0
  72. data/lib/params_ready/query/predicate_operator.rb +132 -0
  73. data/lib/params_ready/query/relation.rb +337 -0
  74. data/lib/params_ready/query/structured_grouping.rb +58 -0
  75. data/lib/params_ready/query/variable_operator_predicate.rb +125 -0
  76. data/lib/params_ready/query_context.rb +21 -0
  77. data/lib/params_ready/restriction.rb +252 -0
  78. data/lib/params_ready/result.rb +109 -0
  79. data/lib/params_ready/value/coder.rb +181 -0
  80. data/lib/params_ready/value/constraint.rb +198 -0
  81. data/lib/params_ready/value/custom.rb +56 -0
  82. data/lib/params_ready/value/validator.rb +68 -0
  83. metadata +181 -0
@@ -0,0 +1,38 @@
1
+ require_relative '../error'
2
+
3
+ module ParamsReady
4
+ module Extensions
5
+ module LateInit
6
+ def late_init(
7
+ name,
8
+ obligatory: true,
9
+ freeze: true,
10
+ getter: true,
11
+ boolean: false,
12
+ once: true,
13
+ definite: true,
14
+ &block
15
+ )
16
+ ivar = :"@#{name}"
17
+ define_method "set_#{name}" do |value|
18
+ raise ParamsReadyError, "Can't initialize '#{name}' to nil" if value.nil? && definite
19
+ value = instance_exec(value, &block) unless block.nil?
20
+ next if value == Extensions::Undefined
21
+
22
+ current = instance_variable_get ivar
23
+ raise ParamsReadyError, "Variable '#{name}' already set" unless current.nil? || !once
24
+ value.freeze if freeze
25
+ instance_variable_set "@#{name}", value
26
+ end
27
+
28
+ if boolean
29
+ define_method "#{name}?" do
30
+ instance_variable_get ivar
31
+ end
32
+ end
33
+ attr_reader name if getter
34
+ obligatory! name if obligatory
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,44 @@
1
+ module ParamsReady
2
+ module Extensions
3
+ module Registry
4
+ def registry(registry_name, name_method: nil, as: nil, getter: false, &block)
5
+ class_variable_name = "@@#{registry_name}"
6
+
7
+ class_variable_set class_variable_name, {}
8
+ if as
9
+ if name_method.nil?
10
+ define_singleton_method "register_#{as}" do |name, entry|
11
+ registry = class_variable_get class_variable_name
12
+ raise ParamsReadyError, "Name '#{name}' already taken for '#{human_string(as)}'" if registry.key? name
13
+ instance_exec name, entry, &block unless block.nil?
14
+ registry[name] = entry
15
+ end
16
+ else
17
+ define_singleton_method "register_#{as}" do |entry|
18
+ name = entry.send name_method
19
+ registry = class_variable_get class_variable_name
20
+ raise ParamsReadyError, "Name '#{name}' already taken for '#{human_string(as)}'" if registry.key? name
21
+ instance_exec name, entry, &block unless block.nil?
22
+ registry[name] = entry
23
+ end
24
+ end
25
+ end
26
+ if getter
27
+ define_singleton_method as do |name|
28
+ registry = class_variable_get class_variable_name
29
+ raise ParamsReadyError, "Name '#{name}' not found in #{human_string(registry_name)}" unless registry.key? name
30
+ registry[name]
31
+ end
32
+ end
33
+ define_singleton_method "has_#{as}?" do |name|
34
+ registry = class_variable_get class_variable_name
35
+ registry.key? name
36
+ end
37
+ end
38
+
39
+ def human_string(string)
40
+ string.to_s.gsub("_", " ")
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,15 @@
1
+ module ParamsReady
2
+ module Extensions
3
+ class Undefined
4
+ def self.dup
5
+ self
6
+ end
7
+
8
+ def self.value_indefinite?(value)
9
+ value == self || value.nil?
10
+ end
11
+
12
+ freeze
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,130 @@
1
+ require 'set'
2
+ require_relative 'error'
3
+ require_relative 'helpers/rule'
4
+
5
+ module ParamsReady
6
+ class Format
7
+ module Wrapper
8
+ attr_reader :format
9
+
10
+ extend Forwardable
11
+ def_delegators :format,
12
+ :alternative?,
13
+ :standard?,
14
+ :hash_key,
15
+ :marshal,
16
+ :marshal?,
17
+ :remap?,
18
+ :local?,
19
+ :name
20
+ end
21
+
22
+ attr_reader :marshal, :naming_scheme, :name, :hash
23
+
24
+ OMIT_ALL = %i(undefined nil default no_output).freeze
25
+ def initialize(marshal:, naming_scheme:, remap:, omit:, local:, name: nil)
26
+ @marshal = Helpers::Rule(marshal)
27
+ @naming_scheme = naming_scheme
28
+ @remap = remap
29
+ @omit = omit.to_set.freeze
30
+ @local = local
31
+ @name = name.nil? ? name : name.to_sym
32
+ @hash = [@marshal, @naming_scheme, @remap, @omit, @local, @name].hash
33
+ freeze
34
+ end
35
+
36
+ def ==(other)
37
+ return false unless other.is_a? Format
38
+ return true if self.object_id == other.object_id
39
+ return false unless marshal == other.marshal
40
+ return false unless naming_scheme == other.naming_scheme
41
+ return false unless remap? == other.remap?
42
+ return false unless @omit == other.instance_variable_get(:@omit)
43
+ return false unless local? == other.local?
44
+ return false unless name == other.name
45
+
46
+ true
47
+ end
48
+
49
+ def alternative?
50
+ @naming_scheme == :alternative
51
+ end
52
+
53
+ def standard?
54
+ @naming_scheme == :standard
55
+ end
56
+
57
+ def remap?
58
+ @remap
59
+ end
60
+
61
+ def hash_key(parameter)
62
+ case @naming_scheme
63
+ when :standard then parameter.name
64
+ when :alternative then parameter.altn
65
+ else
66
+ raise ParamsReadyError, "Unexpected option: #{@naming_scheme}"
67
+ end
68
+ end
69
+
70
+ def omit?(parameter)
71
+ return true if parameter.no_output?(self)
72
+ return true if parameter.is_undefined? && @omit.member?(:undefined)
73
+ return true if parameter.is_nil? && @omit.member?(:nil)
74
+ return true if parameter.is_default? && @omit.member?(:default)
75
+ false
76
+ end
77
+
78
+ def local?
79
+ @local
80
+ end
81
+
82
+ def preserve?(parameter)
83
+ !omit?(parameter)
84
+ end
85
+
86
+ def marshal?(type)
87
+ @marshal.include?(type)
88
+ end
89
+
90
+ def update(**opts)
91
+ opts = instance_variables.reject { |ivar| ivar == :@hash }.map do |ivar|
92
+ value = instance_variable_get(ivar)
93
+ name = ivar.to_s.gsub(/^@/, '').to_sym
94
+ [name, value]
95
+ end.to_h.merge(opts)
96
+
97
+ Format.new(**opts)
98
+ end
99
+
100
+ @names = {
101
+ backend: Format.new(marshal: :none, omit: [], naming_scheme: :standard, remap: false, local: true, name: :backend),
102
+ frontend: Format.new(marshal: :all, omit: OMIT_ALL, naming_scheme: :alternative, remap: false, local: false, name: :frontend),
103
+ attributes: Format.new(marshal: :none, omit: %i(undefined), naming_scheme: :standard, remap: false, local: true, name: :attributes),
104
+ json: Format.new(marshal: { except: [:array, :tuple, :boolean, :number] }, omit: [], naming_scheme: :alternative, remap: true, local: false, name: :json)
105
+ }.freeze
106
+
107
+ def self.define(name, format)
108
+ @names = @names.dup
109
+ @names[name] = format
110
+ @names.freeze
111
+ end
112
+
113
+ def self.instance(name)
114
+ raise ParamsReadyError, "Unknown format '#{name}'" unless @names.key? name
115
+ @names[name]
116
+ end
117
+
118
+ def self.resolve(format_or_name)
119
+ if format_or_name.is_a? Format
120
+ format_or_name
121
+ elsif format_or_name.is_a? Symbol
122
+ instance(format_or_name)
123
+ elsif format_or_name.respond_to? :format
124
+ format_or_name.format
125
+ else
126
+ raise ParamsReadyError, "Not an acceptable format: #{format_or_name}"
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,68 @@
1
+ require_relative '../error'
2
+
3
+ module ParamsReady
4
+ module Helpers
5
+ class ArelBuilder
6
+ def self.instance(object, arel_table: nil)
7
+ case object
8
+ when Arel::Nodes::Node, Arel::Nodes::SqlLiteral, Arel::Attribute
9
+ raise ParamsReadyError, "Arel table unexpected" unless arel_table.nil? || arel_table == :none
10
+ ArelObject.new(object)
11
+ when Proc
12
+ raise ParamsReadyError, "Arel table unexpected" unless arel_table.nil? || arel_table == :none
13
+ Callable.new(object)
14
+ when String, Symbol
15
+ Literal.new(object, arel_table)
16
+ else
17
+ raise ParamsReadyError, "Unexpected type for arel builder: #{object.class.name}"
18
+ end
19
+ end
20
+
21
+ def self.safe_name(name)
22
+ name[0...64]
23
+ end
24
+
25
+ class Callable
26
+ def initialize(proc)
27
+ @proc = proc
28
+ end
29
+
30
+ def to_arel(*args)
31
+ result = @proc.call(*args)
32
+ case result
33
+ when String, Symbol
34
+ Helpers::ArelBuilder.instance(result).to_arel(*args)
35
+ else
36
+ result
37
+ end
38
+ end
39
+ end
40
+
41
+ class Literal
42
+ def initialize(literal, arel_table)
43
+ @literal = literal.to_s
44
+ @arel_table = arel_table
45
+ end
46
+
47
+ def to_arel(default_table, _, _)
48
+ arel_table = @arel_table || default_table
49
+ if arel_table == :none
50
+ Arel::Nodes::SqlLiteral.new(@literal)
51
+ else
52
+ arel_table[@literal]
53
+ end
54
+ end
55
+ end
56
+
57
+ class ArelObject
58
+ def initialize(node)
59
+ @node = node
60
+ end
61
+
62
+ def to_arel(_, _, _)
63
+ @node
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,31 @@
1
+ require_relative 'rule'
2
+ require_relative '../error'
3
+
4
+ module ParamsReady
5
+ module Helpers
6
+ class Conditional
7
+ def initialize(rule: nil)
8
+ @rule = Helpers::Rule(rule)
9
+ freeze
10
+ end
11
+
12
+ def perform?(general_rule, name)
13
+ if @rule.nil?
14
+ general_rule
15
+ else
16
+ @rule.include?(name)
17
+ end
18
+ end
19
+ end
20
+
21
+ class ConditionalBlock < Conditional
22
+ attr_reader :block
23
+
24
+ def initialize(rule: nil, &block)
25
+ raise ParamsReadyError, "Block must not be empty" if block.nil?
26
+ @block = block
27
+ super(rule: rule)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,22 @@
1
+ require_relative '../extensions/undefined'
2
+ require_relative 'key_map'
3
+
4
+ module ParamsReady
5
+ module Helpers
6
+ module FindInHash
7
+ def self.find_in_hash(hash, name_or_path)
8
+ return false, Extensions::Undefined if hash.nil?
9
+
10
+ found = if name_or_path.is_a? Array
11
+ *path, name = name_or_path
12
+ Helpers::KeyMap::Mapping::Path.dig(name, hash, path)
13
+ else
14
+ Extensions::Hash.indifferent_access(hash, name_or_path, Extensions::Undefined)
15
+ end
16
+
17
+ return false, Extensions::Undefined if found == Extensions::Undefined
18
+ return true, found
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,176 @@
1
+ require_relative '../error'
2
+ require_relative '../extensions/undefined'
3
+ require_relative '../extensions/hash'
4
+
5
+ module ParamsReady
6
+ module Helpers
7
+ class KeyMap
8
+ class Mapping
9
+ class Path
10
+ attr_reader :path, :names
11
+
12
+ def initialize(path, names = [])
13
+ @path = path.map(&:to_sym).freeze
14
+ @names = names
15
+ end
16
+
17
+ def add_names(names)
18
+ names.each{ |name| add_name(name) }
19
+ end
20
+
21
+ def add_name(name)
22
+ @names << name
23
+ end
24
+
25
+ def dig(name, hash)
26
+ self.class.dig(name, hash, @path)
27
+ end
28
+
29
+ def self.dig(name, hash, path)
30
+ result = path.reduce(hash) do |current, name|
31
+ next unless Extensions::Hash.acts_as_hash?(current)
32
+
33
+ Extensions::Hash.indifferent_access current, name, nil
34
+ end
35
+
36
+ return Extensions::Undefined unless Extensions::Hash.acts_as_hash?(result)
37
+
38
+ Extensions::Hash.indifferent_access result, name, Extensions::Undefined
39
+ end
40
+
41
+ def store(name, value, hash)
42
+ self.class.store(name, value, hash, @path)
43
+ end
44
+
45
+ def self.store(name, value, hash, path)
46
+ return if value == Extensions::Undefined
47
+
48
+ result = path.reduce(hash) do |current, name|
49
+ current[name] ||= {}
50
+ current[name]
51
+ end
52
+
53
+ result[name] = value
54
+ result
55
+ end
56
+
57
+ def =~(other)
58
+ raise ParamsReadyError, "Can't match path with #{other.class.name}" unless other.is_a? Path
59
+ path == other.path
60
+ end
61
+
62
+ def ==(other)
63
+ raise ParamsReadyError, "Can't compare path with #{other.class.name}" unless other.is_a? Path
64
+ path == other.path && names == other.names
65
+ end
66
+ end
67
+
68
+ def initialize(alt_path, alt_names, std_path, std_names)
69
+ if alt_names.length != std_names.length
70
+ msg = "Expected equal number of alternative and standard names, got #{alt_names.length}/#{std_names.length}"
71
+ raise ParamsReadyError, msg
72
+ end
73
+
74
+ @alt = Path.new(alt_path, alt_names)
75
+ @std = Path.new(std_path, std_names)
76
+ end
77
+
78
+ def add_names(altn, stdn)
79
+ @alt.add_name altn
80
+ @std.add_name stdn
81
+ end
82
+
83
+ def remap(from, to, input, target)
84
+ path(from).names.each_with_index do |input_name, idx|
85
+ value = dig(from, input_name, input)
86
+ target_name = path(to).names[idx]
87
+ store(to, target_name, value, target)
88
+ end
89
+ end
90
+
91
+ def merge!(other)
92
+ raise ParamsReadyError, "Can't merge non_matching mapping" unless self =~ other
93
+
94
+ @alt.add_names(other.alt.names)
95
+ @std.add_names(other.std.names)
96
+ end
97
+
98
+ def dig(schema, name, hash)
99
+ path(schema).dig(name, hash)
100
+ end
101
+
102
+ def store(schema, name, value, hash)
103
+ path(schema).store(name, value, hash)
104
+ end
105
+
106
+ def path(schema)
107
+ case schema
108
+ when :alt then @alt
109
+ when :std then @std
110
+ else
111
+ raise ParamsReadyError, "Unexpected naming schema: #{schema}"
112
+ end
113
+ end
114
+
115
+ def =~(other)
116
+ raise ParamsReadyError, "Can't match path with #{other.class.name}" unless other.is_a? Mapping
117
+ return false unless path(:alt) =~ other.path(:alt)
118
+ return false unless path(:std) =~ other.path(:std)
119
+
120
+ true
121
+ end
122
+
123
+ protected
124
+ attr_reader :alt, :std
125
+ end
126
+
127
+ def initialize
128
+ @mappings = []
129
+ end
130
+
131
+ def map(from, to:)
132
+ alt_path, alt_names = self.class.split_map(from)
133
+ std_path, std_names = self.class.split_map(to)
134
+ mapping = Mapping.new(alt_path, alt_names, std_path, std_names)
135
+ merge_or_add_mapping(mapping)
136
+ self
137
+ end
138
+
139
+ def merge_or_add_mapping(mapping)
140
+ if (existing = @mappings.find { |candidate| candidate =~ mapping })
141
+ existing.merge!(mapping)
142
+ else
143
+ @mappings << mapping
144
+ end
145
+ mapping
146
+ end
147
+
148
+ def to_standard(hash)
149
+ remap(:alt, :std, hash)
150
+ end
151
+
152
+ def to_alternative(hash)
153
+ remap(:std, :alt, hash)
154
+ end
155
+
156
+ def remap(from, to, input)
157
+ @mappings.each_with_object({}) do |mapping, result|
158
+ mapping.remap(from, to, input, result)
159
+ end
160
+ end
161
+
162
+ def freeze
163
+ @mappings.each(&:freeze)
164
+ super
165
+ end
166
+
167
+ def self.split_map(array)
168
+ raise ParamsReadyError, "Array expected, got: #{array.class.name}" unless array.is_a? Array
169
+ names = array.last || []
170
+ raise ParamsReadyError, "Array expected, got: #{names.class.name}" unless names.is_a? Array
171
+ paths = array[0...-1]
172
+ [paths, names]
173
+ end
174
+ end
175
+ end
176
+ end