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,42 @@
1
+ require_relative '../extensions/undefined'
2
+ require_relative '../extensions/hash'
3
+
4
+
5
+
6
+ module ParamsReady
7
+ module Helpers
8
+ class Memo
9
+ def initialize(slots = 1)
10
+ raise ParamsReadyError, "Expected positive value for number of slots, got: '#{slots}'" unless slots > 0
11
+ @slots = slots
12
+ @cache = nil
13
+ end
14
+
15
+ def cached_value(key)
16
+ cache = @cache
17
+ return Extensions::Undefined if cache.nil?
18
+ return Extensions::Undefined unless cache.key? key
19
+
20
+ cache[key]
21
+ end
22
+
23
+ def cache_value(value, key)
24
+
25
+ stale = @cache
26
+ return if stale&.key? key
27
+
28
+ frozen = Extensions::Hash.try_deep_freeze(value)
29
+
30
+ fresh = if stale.nil? || @slots == 1
31
+ { key => frozen }
32
+ else
33
+ kept = stale.to_a.last(@slots - 1)
34
+
35
+ [*kept, [key, frozen]].to_h
36
+ end
37
+
38
+ @cache = fresh.freeze
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,39 @@
1
+ require_relative 'storage'
2
+ require_relative 'usage_rule'
3
+
4
+ module ParamsReady
5
+ module Helpers
6
+ class Options < Storage
7
+ attr_reader :parameters, :relations
8
+
9
+ def initialize
10
+ super
11
+ @parameter_rules = Hash.new
12
+ @relation_rules = Hash.new
13
+ @state = nil
14
+ end
15
+
16
+ def use_parameter(param, rule_args = :all)
17
+ rule = UsageRule.new(param, rule_args)
18
+ @parameter_rules[param.name] = rule
19
+ end
20
+
21
+ def use_relation(relation, rule_args = :all)
22
+ rule = UsageRule.new(relation, rule_args)
23
+ @relation_rules[relation.name] = rule
24
+ end
25
+
26
+ def parameter_rules
27
+ @parameter_rules.each_value do |rule|
28
+ yield rule
29
+ end
30
+ end
31
+
32
+ def relation_rules
33
+ @relation_rules.each_value do |rule|
34
+ yield rule
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,39 @@
1
+ require_relative 'relation_builder_wrapper'
2
+ require_relative '../builder'
3
+
4
+ module ParamsReady
5
+ module Helpers
6
+ module ParameterDefinerClassMethods
7
+ def define_relation(*args, **opts, &block)
8
+ wrapper = ParamsReady::Helpers::RelationBuilderWrapper.new self, *args, **opts
9
+ wrapper.instance_eval(&block) unless block.nil?
10
+ relation = wrapper.build
11
+ params_ready_storage.add_relation relation
12
+ end
13
+
14
+ def define_parameter(type, *args, **opts, &block)
15
+ full_name = "define_#{type}"
16
+ parameter = Builder.send(full_name, *args, **opts, &block)
17
+ params_ready_storage.add_parameter parameter
18
+ end
19
+
20
+ def all_relations
21
+ relations = if superclass.respond_to? :all_relations
22
+ superclass.all_relations
23
+ else
24
+ {}
25
+ end
26
+ relations.merge(params_ready_storage.relations)
27
+ end
28
+
29
+ def all_parameters
30
+ parameters = if superclass.respond_to? :all_parameters
31
+ superclass.all_parameters
32
+ else
33
+ {}
34
+ end
35
+ parameters.merge(params_ready_storage.parameters)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,36 @@
1
+ require_relative 'storage'
2
+ require_relative '../error'
3
+
4
+ module ParamsReady
5
+ module Helpers
6
+ module ParameterStorageClassMethods
7
+ def params_ready_storage
8
+ @params_ready_storage ||= Storage.new
9
+ end
10
+
11
+ def relation_definition(key)
12
+ relations = params_ready_storage.relations
13
+ sym_key = key.to_sym
14
+ if relations.key?(sym_key)
15
+ relations[sym_key]
16
+ elsif superclass.respond_to? :relation_definition
17
+ superclass.relation_definition sym_key
18
+ else
19
+ raise ParamsReadyError, "Unknown relation '#{sym_key}'"
20
+ end
21
+ end
22
+
23
+ def parameter_definition(key)
24
+ parameters = params_ready_storage.parameters
25
+ sym_key = key.to_sym
26
+ if parameters.key? sym_key
27
+ parameters[sym_key]
28
+ elsif superclass.respond_to? :parameter_definition
29
+ superclass.parameter_definition sym_key
30
+ else
31
+ raise ParamsReadyError, "Unknown parameter '#{sym_key}'"
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,31 @@
1
+ module ParamsReady
2
+ module Helpers
3
+ module ParameterUserClassMethods
4
+ def params_ready_storage
5
+ @params_ready_storage ||= ParamsReady::Helpers::Options.new
6
+ end
7
+
8
+ def use_parameter(name, rule = :all)
9
+ parameter = parameter_definition name
10
+ params_ready_storage.use_parameter parameter, rule
11
+ end
12
+
13
+ def use_relation(name, rule = :all)
14
+ relation = relation_definition name
15
+ params_ready_storage.use_relation relation, rule
16
+ end
17
+
18
+ def include_parameters(parameter_definer)
19
+ parameter_definer.all_parameters.values.each do |p|
20
+ params_ready_storage.add_parameter(p)
21
+ end
22
+ end
23
+
24
+ def include_relations(parameter_definer)
25
+ parameter_definer.all_relations.values.each do |d|
26
+ params_ready_storage.add_relation(d)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,35 @@
1
+ require_relative '../query/relation'
2
+
3
+ module ParamsReady
4
+ module Helpers
5
+ class RelationBuilderWrapper
6
+ def initialize(cache, *args, **opts)
7
+ @cache = cache
8
+ @builder = Query::RelationParameterBuilder.instance *args, **opts
9
+ end
10
+
11
+ def capture(*names)
12
+ names.each do |name|
13
+ definition = @cache.parameter_definition(name)
14
+ @builder.add definition
15
+ end
16
+ end
17
+
18
+ ruby2_keywords def method_missing(name, *args, &block)
19
+ if @builder.respond_to? name
20
+ @builder.send name, *args, &block
21
+ else
22
+ super
23
+ end
24
+ end
25
+
26
+ def respond_to_missing?(name, include_private = false)
27
+ if @builder.respond_to? name
28
+ true
29
+ else
30
+ super
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,57 @@
1
+ require 'set'
2
+
3
+ module ParamsReady
4
+ module Helpers
5
+ def self.Rule(input)
6
+ return input if input.nil?
7
+ return input if input.is_a? Rule
8
+
9
+ Rule.new(input).freeze
10
+ end
11
+
12
+ class Rule
13
+ attr_reader :hash
14
+
15
+ def initialize(value)
16
+ @mode, @values = case value
17
+ when :none, :all then [value, nil]
18
+ when Hash
19
+ if value.length > 1 || value.length < 1
20
+ raise ParamsReadyError, "Unexpected hash for rule: '#{value}'"
21
+ end
22
+ key, values = value.first
23
+ case key
24
+ when :except, :only then [key, values.to_set.freeze]
25
+ else
26
+ raise ParamsReadyError, "Unexpected mode for rule: '#{key}'"
27
+ end
28
+ else
29
+ raise ParamsReadyError, "Unexpected input for rule: '#{value}'"
30
+ end
31
+ @values.freeze
32
+ @hash = [@mode, @values].hash
33
+ freeze
34
+ end
35
+
36
+ def include?(name)
37
+ case @mode
38
+ when :none then false
39
+ when :all then true
40
+ when :only then @values.member? name
41
+ when :except
42
+ !@values.member? name
43
+ else
44
+ raise ParamsReadyError, "Unexpected mode for rule: '#{@mode}'"
45
+ end
46
+ end
47
+
48
+ def ==(other)
49
+ return false unless other.is_a? Rule
50
+ return true if object_id == other.object_id
51
+ return false unless @mode == other.instance_variable_get(:@mode)
52
+
53
+ @values == other.instance_variable_get(:@values)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,30 @@
1
+ module ParamsReady
2
+ module Helpers
3
+ class Storage
4
+ attr_reader :parameters, :relations
5
+
6
+ def initialize
7
+ @parameters = Hash.new
8
+ @relations = Hash.new
9
+ end
10
+
11
+ def has_relation?(name)
12
+ relations.key? name
13
+ end
14
+
15
+ def has_parameter?(name)
16
+ parameters.key? name
17
+ end
18
+
19
+ def add_relation(relation)
20
+ raise ParamsReadyError, "Relation already exists: #{relation.name}" if self.has_relation?(relation.name)
21
+ @relations[relation.name] = relation
22
+ end
23
+
24
+ def add_parameter(param)
25
+ raise ParamsReadyError, "Parameter already exists: #{param.name}" if self.has_parameter?(param.name)
26
+ @parameters[param.name] = param
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,18 @@
1
+ require_relative 'rule'
2
+
3
+ module ParamsReady
4
+ module Helpers
5
+ class UsageRule
6
+ attr_reader :parameter_definition
7
+
8
+ def initialize(parameter_definition, rule = :all)
9
+ @parameter_definition = parameter_definition
10
+ @rule = ParamsReady::Helpers::Rule(rule)
11
+ end
12
+
13
+ def valid_for(method)
14
+ @rule.include? method
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,31 @@
1
+ require 'forwardable'
2
+ require_relative 'format'
3
+
4
+ module ParamsReady
5
+ class InputContext
6
+ include Format::Wrapper
7
+ extend Forwardable
8
+
9
+ attr_reader :data
10
+
11
+ def_delegator :data, :[]
12
+
13
+ def initialize(format, data = {})
14
+ @format = Format.resolve(format).freeze
15
+ @data = data.freeze
16
+ end
17
+
18
+ def self.resolve(unknown)
19
+ case unknown
20
+ when nil
21
+ Format.instance(:frontend)
22
+ when InputContext, Format
23
+ unknown
24
+ when Symbol
25
+ Format.instance(unknown)
26
+ else
27
+ raise ParamsReadyError, "Unexpected type for InputContext: #{unknown.class.name}"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,70 @@
1
+ require 'forwardable'
2
+ require_relative 'restriction'
3
+ require_relative 'format'
4
+ require_relative 'parameter/parameter'
5
+
6
+ module ParamsReady
7
+ class Intent
8
+ extend Forwardable
9
+ include Restriction::Wrapper
10
+ include Format::Wrapper
11
+
12
+ def clone(restriction:)
13
+ Intent.new @format, restriction, data: @data
14
+ end
15
+
16
+ attr_reader :data, :hash
17
+
18
+ def initialize(format, restriction = Restriction.blanket_permission, data: nil)
19
+ @format = Format.resolve(format).freeze
20
+ raise ParamsReadyError, "Restriction expected, got: #{restriction.inspect}" unless restriction.is_a? Restriction
21
+ @restriction = restriction
22
+ @data = check_data(data)
23
+ @hash = [@format, @restriction, @data].hash
24
+ freeze
25
+ end
26
+
27
+ def check_data(data)
28
+ return if data.nil?
29
+ # The reason we require data object to be
30
+ # a Parameter is that it must be deep frozen
31
+ # for the output memoizing feature to work properly.
32
+ raise 'Data object must be a parameter' unless data.is_a? Parameter::Parameter
33
+ raise 'Data object must be frozen' unless data.frozen?
34
+
35
+ data
36
+ end
37
+
38
+ def omit?(parameter)
39
+ return true unless permitted?(parameter)
40
+ @format.omit?(parameter)
41
+ end
42
+
43
+ def preserve?(parameter)
44
+ !omit?(parameter)
45
+ end
46
+
47
+ def self.instance(name)
48
+ format = Format.instance(name)
49
+ Intent.new(format)
50
+ end
51
+
52
+ def self.resolve(intent_or_name)
53
+ if intent_or_name.is_a? Intent
54
+ intent_or_name
55
+ else
56
+ instance(intent_or_name)
57
+ end
58
+ end
59
+
60
+ def ==(other)
61
+ return false unless other.is_a?(Intent)
62
+ return true if object_id == other.object_id
63
+ restriction == other.restriction && format == other.format && data == other.data
64
+ end
65
+
66
+ def eql?(other)
67
+ self == other
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,132 @@
1
+ require_relative 'collection'
2
+ require_relative '../extensions/undefined'
3
+ require_relative '../extensions/hash'
4
+ require_relative '../helpers/find_in_hash'
5
+
6
+ module ParamsReady
7
+ module Marshaller
8
+ class ArrayMarshallers
9
+ module AbstractMarshaller
10
+ def marshal(parameter, intent)
11
+ array = parameter.send(:bare_value)
12
+ definition = parameter.definition
13
+ compact = definition.compact?
14
+
15
+ elements = array.map do |element|
16
+ if element.eligible_for_output?(intent)
17
+ element.format_self_permitted(intent)
18
+ end
19
+ end
20
+ elements = elements.compact if compact
21
+ do_marshal(elements, intent, compact)
22
+ end
23
+ end
24
+
25
+ module ArrayMarshaller
26
+ extend AbstractMarshaller
27
+
28
+ def self.canonicalize(definition, array, context, validator, freeze: false)
29
+ canonical = array.map do |value|
30
+ next if definition.compact? && value.nil?
31
+
32
+ element = definition.prototype.create
33
+ element.set_from_input(value, context, validator)
34
+ next if definition.compact? && element.is_nil?
35
+
36
+ element.freeze if freeze
37
+ element
38
+ end.compact
39
+
40
+ [canonical, validator]
41
+ end
42
+
43
+ def self.do_marshal(array, _, _)
44
+ array
45
+ end
46
+
47
+ freeze
48
+ end
49
+
50
+ module HashMarshaller
51
+ extend AbstractMarshaller
52
+
53
+ def self.canonicalize(definition, hash, context, validator)
54
+ if definition.compact?
55
+ ArrayMarshaller.canonicalize(definition, hash.values, context, validator)
56
+ else
57
+ count_key = :cnt
58
+ found, count = Helpers::FindInHash.find_in_hash hash, count_key
59
+ raise ParamsReadyError, "Count not found" unless found
60
+
61
+ count = Integer(count)
62
+ array = (0...count).map do |index|
63
+ found, value = Helpers::FindInHash.find_in_hash hash, index
64
+ element = definition.prototype.create
65
+ element.set_from_input(value, context, validator) if found
66
+ element
67
+ end
68
+ [array, validator]
69
+ end
70
+ end
71
+
72
+ def self.do_marshal(array, _, compact)
73
+ return array if compact
74
+
75
+ result = array.each_with_index.reduce({}) do |result, (element, index)|
76
+ index = index.to_s
77
+ result[index] = element
78
+ result
79
+ end
80
+
81
+ result['cnt'] = array.length.to_s
82
+ result
83
+ end
84
+
85
+ freeze
86
+ end
87
+
88
+ class StringMarshaller
89
+ include AbstractMarshaller
90
+
91
+ attr_reader :separator
92
+
93
+ def self.instance(separator:, split_pattern: nil)
94
+ instance = new separator, split_pattern
95
+ [String, instance.freeze]
96
+ end
97
+
98
+ def initialize(separator, split_pattern)
99
+ @separator = separator.to_s.freeze
100
+ @split_pattern = split_pattern.freeze
101
+ end
102
+
103
+ def split_pattern
104
+ @split_pattern || @separator
105
+ end
106
+
107
+ def canonicalize(definition, string, context, validator)
108
+ array = string.split(split_pattern).map(&:strip).reject(&:empty?)
109
+ ArrayMarshaller.canonicalize(definition, array, context, validator)
110
+ end
111
+
112
+ def do_marshal(array, _, _)
113
+ array.join(separator)
114
+ end
115
+
116
+ freeze
117
+ end
118
+
119
+ def self.collection
120
+ @collection ||= begin
121
+ c = ClassCollection.new Array
122
+ c.add_instance Array, ArrayMarshaller
123
+ c.add_instance Hash, HashMarshaller
124
+ c.add_factory :string, StringMarshaller
125
+ c.default!(Hash)
126
+ c.freeze
127
+ c
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end