paramore 1.1.0 → 3.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2a45a04ffeaabb270fd7b57a2e27e195205a26ad281c46d69561040d71fb6961
4
- data.tar.gz: 0df60483be44946c6e1fedb711039d5a915787c54e46c5f841c73172393e35f9
3
+ metadata.gz: fb73686fb48a297c559c211476f565f8aba18a2e93a10936b75cacd4eab2bcb2
4
+ data.tar.gz: df12fd5715e1c4e3851a1ed2f9748f24cccec9d9ccc817c7b6ba74960370f426
5
5
  SHA512:
6
- metadata.gz: f0f3ed2da9f139467f5d4f6db3d7cbc7f0b29c3c1385a4eb8835d0cbffced915d0c77700d69722d11807984624e611856b2acd36462fe6ea4cb577736f8c6255
7
- data.tar.gz: decf8bc3cbaa99c75ce7070b59f58f4e84d5cd0b3d22681836ae56f61596a600f3777e58c929d9db31a698689ea07a7b00330d45f54a9706262b512734b07393
6
+ metadata.gz: 5d7f2e6f52065f18ebb5b7d51577950ceb234db5f5a21fd3a20fdb987066c451da22beda1bc86a4101ad9bfaf4ccf195a38b1281b02c92c4d8cc2d1fa6602958
7
+ data.tar.gz: b5dd8ae054488a628c580924cfdfb0355ef063a43eb7e46fe249ae9ecad62a4a80d5603b4eed182d1b69c1d691926c97165c605b11d05eec4b5ed66efbc3cac3
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Paramore
2
2
 
3
- Paramore is a small gem intended to make strong parameter definitions declarative
4
- and provide a unified way to typecast and sanitize their values outside of controllers.
3
+ Paramore allows you to declare a typed schema for your params,
4
+ so that any downstream code can work with the data it expects.
5
5
 
6
6
  # Installation
7
7
 
@@ -20,7 +20,7 @@ $ bundle
20
20
  <h3>Without typing</h3>
21
21
 
22
22
  ```ruby
23
- paramorize :item_params,
23
+ param_schema :item_params,
24
24
  item: [:name, :description, :for_sale, :price, metadata: [tags: []]]
25
25
  ```
26
26
 
@@ -74,16 +74,16 @@ class ItemsController < ApplicationController
74
74
  Item.create(item_params)
75
75
  end
76
76
 
77
- paramorize :item_params,
78
- item: {
79
- name: Paratype[Paramore::SanitizedString],
80
- description: Paratype[Paramore::StrippedString, null: true],
81
- for_sale: Paratype[Paramore::Boolean],
82
- price: Paratype[Paramore::Decimal],
83
- metadata: Paratype[{
84
- tags: Paratype[[Types::ItemTag], compact: true]
85
- }]
86
- }
77
+ param_schema :item_params,
78
+ item: Paramore.field({
79
+ name: Paramore.field(Paramore::SanitizedString),
80
+ description: Paramore.field(Paramore::StrippedString, null: true),
81
+ for_sale: Paramore.field(Paramore::Boolean, default: false),
82
+ price: Paramore.field(Paramore::Decimal),
83
+ metadata: Paramore.field({
84
+ tags: Paramore.field([Types::ItemTag], compact: true)
85
+ })
86
+ })
87
87
  end
88
88
  ```
89
89
 
@@ -129,13 +129,12 @@ Notice that the `Paramore::StrippedString` does not perform `.squeeze(' ')`, onl
129
129
  <h3>nil</h3>
130
130
 
131
131
  Types are non-nullable by default and raise exceptions if the param hash misses any.
132
- This can be disabled for any type by declaring `Paratype[Paramore::Int, null: true]`.
132
+ This can be disabled for any type by declaring `Paramore.field(Paramore::Int, null: true)`.
133
133
 
134
134
  nils will usually not reach any of the type classes - if some parameter is nullable, the class will not be called.
135
135
  If a parameter is non-nullable, then a `Paramore::NilParameter` error will be raised before calling the class.
136
- If a, say, `item_ids` array is non-nullable, but the received parameter is `['1', '', '3']`, only the `'1'` and `'2'` will get passed to type classes, and the resulting array will contain a nil, eg.: `['1', nil, '3']`.
137
- nils inside arrays can still be passed to type classes by declaring `Paratype[[Paramore::Int], empty: true]`.
138
- If you wish to get rid of empty array elements, declare `Paratype[Paramore::Int, compact: true]`.
136
+ If an incoming array contains nils, they will get passed to type classes.
137
+ If you wish to get rid of empty array elements, declare `Paramore.field(Paramore::Int, compact: true)`.
139
138
 
140
139
  <h3>Configuration</h3>
141
140
 
@@ -3,58 +3,38 @@ require_relative 'errors'
3
3
  module Paramore
4
4
  module CastParameters
5
5
  module_function
6
- def run(types_definition, permitted_params)
7
- recursive_merge(
8
- recursive_typecast(
9
- types_definition, permitted_params
10
- )
11
- )
6
+ def run(field, data)
7
+ cast(field, data, 'data')
12
8
  end
13
9
 
14
- def recursive_merge(nested_hash_array)
15
- nested_hash_array.reduce(:merge).map do |param_name, value|
16
- if value.is_a?(Array) && value.all? { |_value| _value.is_a?(Hash) }
17
- { param_name => recursive_merge(value) }
10
+ def cast(field, value, name = nil)
11
+ if value.nil?
12
+ if field.nullable? || field.default
13
+ return field.default
18
14
  else
19
- { param_name => value }
15
+ raise Paramore::NilParameter, name
20
16
  end
21
- end.reduce(:merge)
22
- end
23
-
24
- def recursive_typecast(types_definition, permitted_params)
25
- types_definition.map do |param_name, definition|
26
- value = permitted_params[param_name]
27
-
28
- if value.nil?
29
- if definition.nullable?
30
- next { param_name => nil }
31
- else
32
- raise Paramore::NilParameter, param_name
33
- end
34
- end
35
-
36
- { param_name => cast(definition, value) }
37
17
  end
38
- end
39
18
 
40
- def cast(definition, value)
41
- case definition.type
19
+ case field.type
42
20
  when Hash
43
- recursive_typecast(definition.type, value || {})
21
+ typecast_hash(field.type, value || {})
44
22
  when Array
45
- typecast_array(definition, value)
23
+ typecast_array(field, value)
46
24
  else
47
- typecast_value(definition.type, value)
25
+ typecast_value(field.type, value)
48
26
  end
49
27
  end
50
28
 
51
- def typecast_array(definition, array)
29
+ def typecast_hash(field, hash)
30
+ field.to_h { |name, field| [name, cast(field, hash[name], name)] }
31
+ end
32
+
33
+ def typecast_array(field, array)
52
34
  array
53
- .reject { |unit| unit.to_s == '' && definition.compact? }
35
+ .reject { |unit| unit.to_s == '' && field.compact? }
54
36
  .map do |unit|
55
- if unit.to_s != '' || definition.use_empty_strings?
56
- typecast_value(definition.type.first, unit)
57
- end
37
+ cast(Paramore.field(field.type.first, null: true), unit)
58
38
  end
59
39
  end
60
40
 
@@ -4,8 +4,8 @@ class Paramore::NilParameter < StandardError
4
4
  end
5
5
  end
6
6
 
7
- class Paramore::NonParatype < StandardError
7
+ class Paramore::NonField < StandardError
8
8
  def initialize(param_name, type)
9
- super("`#{param_name}` defined as a `#{type.class}`, expected `Paratype`! Perhaps you declared a plain hash instead of Paratype[{}]?")
9
+ super("`#{param_name}` defined as a `#{type.class}`, expected a call of `Paramore.field()`! Perhaps you declared a plain hash instead of Paramore.field({})?")
10
10
  end
11
11
  end
@@ -8,9 +8,7 @@ module Paramore
8
8
  default
9
9
  ].freeze
10
10
 
11
- def paramorize(accessor_name, configuration)
12
- parameter_configuration = configuration.except(*OPTIONS)
13
-
11
+ def param_schema(accessor_name, parameter_configuration)
14
12
  unless parameter_configuration.keys.size == 1
15
13
  raise ArgumentError,
16
14
  "Paramore: exactly one required attribute allowed! Given: #{parameter_configuration.keys}"
@@ -19,10 +17,10 @@ module Paramore
19
17
  required_parameter_name = parameter_configuration.keys.first
20
18
  types_definition = parameter_configuration.values.first
21
19
 
22
- Paramore::Validate.run(types_definition) if types_definition.is_a?(Hash)
20
+ Paramore::Validate.run(types_definition) if types_definition.is_a?(Paramore::Field)
23
21
 
24
22
  permitted_parameter_argument =
25
- if types_definition.is_a?(Hash)
23
+ if types_definition.is_a?(Paramore::Field)
26
24
  Paramore::PermittedParameterArgument.parse(types_definition)
27
25
  else
28
26
  types_definition
@@ -31,8 +29,8 @@ module Paramore
31
29
  define_method(accessor_name) do |rails_parameters = params|
32
30
  return instance_variable_get("@#{accessor_name}") if instance_variable_defined?("@#{accessor_name}")
33
31
 
34
- if rails_parameters[required_parameter_name].nil? && configuration[:default]
35
- instance_variable_set("@#{accessor_name}", configuration[:default])
32
+ if rails_parameters[required_parameter_name].nil? && types_definition.default
33
+ instance_variable_set("@#{accessor_name}", types_definition.default)
36
34
  return instance_variable_get("@#{accessor_name}")
37
35
  end
38
36
 
@@ -41,7 +39,7 @@ module Paramore
41
39
  .permit(permitted_parameter_argument)
42
40
 
43
41
  parameter_values =
44
- if types_definition.is_a?(Hash)
42
+ if types_definition.is_a?(Paramore::Field)
45
43
  permitted_params.merge(
46
44
  Paramore::CastParameters.run(types_definition, permitted_params)
47
45
  ).permit!
@@ -0,0 +1,32 @@
1
+ module Paramore
2
+ class Field
3
+ DEFAULT_OPTIONS = {
4
+ null: false,
5
+ compact: false,
6
+ default: nil,
7
+ }.freeze
8
+
9
+ def initialize(given_type, null:, compact:, default:)
10
+ @given_type = given_type
11
+ @nullable = null
12
+ @compact = compact
13
+ @default = default
14
+ end
15
+
16
+ def default
17
+ @default
18
+ end
19
+
20
+ def compact?
21
+ @compact
22
+ end
23
+
24
+ def nullable?
25
+ @nullable
26
+ end
27
+
28
+ def type
29
+ @given_type
30
+ end
31
+ end
32
+ end
@@ -2,22 +2,26 @@ module Paramore
2
2
  module PermittedParameterArgument
3
3
  module_function
4
4
 
5
- def parse(types_definition)
5
+ def parse(field)
6
+ parse_type(field.type)
7
+ end
8
+
9
+ def parse_type(type)
6
10
  merge_hashes(
7
- types_definition.map do |key, definition|
8
- case definition
9
- when Hash
10
- { key => merge_hashes(parse(definition)) }
11
- when Paratype
12
- case definition.type
13
- when Array
14
- { key => [] }
15
- when Hash
16
- { key => merge_hashes(parse(definition.type)) }
11
+ case type
12
+ when Array
13
+ parse_type(type.first)
14
+ when Hash
15
+ type.map do |name, field|
16
+ case field.type
17
+ when Array, Hash
18
+ { name => parse_type(field.type) }
17
19
  else
18
- key
20
+ name
19
21
  end
20
22
  end
23
+ else
24
+ []
21
25
  end
22
26
  )
23
27
  end
@@ -2,23 +2,34 @@ module Paramore
2
2
  module Validate
3
3
  module_function
4
4
 
5
- def run(types_definition)
6
- types(types_definition).each do |type|
5
+ def run(root_field)
6
+ types(root_field.type).uniq.each do |type|
7
7
  unless type.respond_to?(Paramore.configuration.type_method_name)
8
8
  raise NoMethodError,
9
9
  "Paramore: type `#{type}` does not respond to " +
10
10
  "`#{Paramore.configuration.type_method_name}`!"
11
11
  end
12
12
  end
13
+
14
+ root_field
13
15
  end
14
16
 
15
- def types(types_definition)
16
- types_definition.flat_map do |param_name, paratype|
17
- unless paratype.is_a?(Paratype)
18
- raise Paramore::NonParatype.new(param_name, paratype)
19
- end
17
+ def types(type)
18
+ case type
19
+ when Hash
20
+ hash_types(type)
21
+ when Array
22
+ type.flat_map { |subtype| types(subtype) }
23
+ else
24
+ [type]
25
+ end
26
+ end
27
+
28
+ def hash_types(hash)
29
+ hash.flat_map do |param_name, field|
30
+ raise Paramore::NonField.new(param_name, field) unless field.is_a?(Paramore::Field)
20
31
 
21
- paratype.type.is_a?(Hash) ? types(paratype.type) : paratype.type
32
+ field.type.is_a?(Hash) ? types(field.type) : field.type
22
33
  end.uniq
23
34
  end
24
35
  end
data/lib/paramore.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require_relative 'paramore/configuration'
2
2
  require_relative 'paramore/railtie'
3
3
  require_relative 'paramore/types'
4
- require_relative 'paratype'
4
+ require_relative 'paramore/field'
5
5
 
6
6
  module Paramore
7
7
  class << self
@@ -15,4 +15,11 @@ module Paramore
15
15
  def self.configure
16
16
  yield(configuration)
17
17
  end
18
+
19
+ def self.field(given_type, options = {})
20
+ Paramore::Field.new(
21
+ given_type,
22
+ **Paramore::Field::DEFAULT_OPTIONS.merge(options)
23
+ )
24
+ end
18
25
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paramore
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 3.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lukas Kairevičius
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-27 00:00:00.000000000 Z
11
+ date: 2021-09-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec-rails
@@ -91,17 +91,17 @@ files:
91
91
  - lib/paramore/configuration.rb
92
92
  - lib/paramore/errors.rb
93
93
  - lib/paramore/extension.rb
94
+ - lib/paramore/field.rb
94
95
  - lib/paramore/permitted_parameter_argument.rb
95
96
  - lib/paramore/railtie.rb
96
97
  - lib/paramore/types.rb
97
98
  - lib/paramore/validate.rb
98
- - lib/paratype.rb
99
99
  homepage: https://github.com/lumzdas/paramore
100
100
  licenses:
101
101
  - MIT
102
102
  metadata: {}
103
103
  post_install_message: |
104
- Thank you for installing Paramore 1.1.0 !
104
+ Thank you for installing Paramore 3.1.1 !
105
105
  From the command line you can run `paramore` to generate a configuration file
106
106
 
107
107
  More details here : https://github.com/lumzdas/paramore/blob/master/README.md
data/lib/paratype.rb DELETED
@@ -1,32 +0,0 @@
1
- class Paratype
2
- def self.[](given_type, null: false, empty: false, compact: false)
3
- self.new(given_type, null: null, empty: empty, compact: compact)
4
- end
5
-
6
- def initialize(given_type, null:, empty:, compact:)
7
- @given_type = given_type
8
- @nullable = null
9
- @empty = empty
10
- @compact = compact
11
- end
12
-
13
- def compact?
14
- compact
15
- end
16
-
17
- def nullable?
18
- nullable
19
- end
20
-
21
- def use_empty_strings?
22
- empty
23
- end
24
-
25
- def type
26
- given_type
27
- end
28
-
29
- private
30
-
31
- attr_reader :given_type, :nullable, :empty, :compact
32
- end