barley 0.3.0 → 0.4.0

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: be82895a01fc1f0be3170a9a105b7fdf20b7e98d31a77c74c5d9f67068814dec
4
- data.tar.gz: 276dcc80814079aed898b1e4d1d9e5b6bf3bc4b8b6b86f2b5cff701961d2b877
3
+ metadata.gz: faa7d627d11906ae112f0a8fd640f0a2d90acacc114c63297a26e227d5120c3d
4
+ data.tar.gz: c36d22bee59469e7e98f481086a5a32a75ac96d9bab984330a4d945c97efa5f6
5
5
  SHA512:
6
- metadata.gz: 9f3482dc0562669991a64403c9c5cee510d61cc5ec79affa3715eb203ac1a48f1f15c676778d380eccbfe6efc57281eae6c179ab4378a62d35c31b0fd65529ea
7
- data.tar.gz: b9d2a98edf8704acf8b8466042b924b771384e1756fd06f9d23939f9bd0fb4368a4934303057edd0a53d0c8f487dc93c10649e39fe17b3031189f383a668ec54
6
+ metadata.gz: 3adb8da65be384daf38feaf056541f6b3e2c2f7f400917ed2da43a9f382e3bbc28afdad2437fe7489bdcd1ccd3980a587697525f0b493e1ec7b5374acfb61bb8
7
+ data.tar.gz: a359ed1a3c15f8efdd62ec86112163edc8d423f9d0d8c8790e781c651ac208dfa722a42bb4c93f822b5bbfc2a072626bbf1c9ad088c24354828745476247fc58
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- ![Barley loqo](./img/barley.png)
1
+ ![Barley loqo](https://i.imgur.com/am0emi4.png)
2
2
 
3
3
  Barley is a dead simple, fast, and efficient ActiveModel JSON serializer.
4
4
 
@@ -6,6 +6,9 @@ Cerealize your ActiveModel objects into flat hashes with a dead simple, yet vers
6
6
 
7
7
  You don't believe us? Check out the [benchmarks](#benchmarks). 😎
8
8
 
9
+ ## API documentation
10
+ [Check out the API documentation here](https://rubydoc.info/github/MoskitoHero/barley/main).
11
+
9
12
  ## Usage
10
13
  Add the `Barley::Serializable` module to your ActiveModel object.
11
14
 
@@ -21,8 +24,9 @@ Then define your attributes and associations in a serializer class.
21
24
  ```ruby
22
25
  # /app/serializers/user_serializer.rb
23
26
  class UserSerializer < Barley::Serializer
24
- attributes :id, :name,
27
+ attributes id: Types::Strict::Integer, :name # multiple attributes, optional type checking with dry-types
25
28
  attribute :email # single attribute
29
+ attribute :value, type: Types::Coercible::Integer # optional type checking with dry-types
26
30
 
27
31
  many :posts # relations
28
32
  one :group, serializer: CustomGroupSerializer # custom serializer
@@ -243,6 +247,27 @@ Barley.configure do |config|
243
247
  end
244
248
  ```
245
249
 
250
+ ## Type checking
251
+ Barley can check the type of the object you are serializing with the [dry-types](https://dry-rb.org/gems/dry-types/main/) gem.
252
+
253
+ It will raise an error if the object is not of the expected type, or coerce it to the correct type and perform constraints checks.
254
+
255
+ ```ruby
256
+ module Types
257
+ include Dry.Types()
258
+ end
259
+
260
+ class UserSerializer < Barley::Serializer
261
+ attributes id: Types::Strict::Integer, name: Types::Strict::String, email: Types::Strict::String.constrained(format: URI::MailTo::EMAIL_REGEXP)
262
+
263
+ attribute :role, type: Types::Coercible::String do
264
+ object.role.integer_or_string_coercible_value
265
+ end
266
+ end
267
+ ```
268
+
269
+ Check out [dry-types](https://dry-rb.org/gems/dry-types/main/) for all options and available types.
270
+
246
271
  ## Breakfast mode 🤡 (coming soon)
247
272
  You will soon be able to replace all occurrences of `Serializer` with `Cerealizer` in your codebase. Just for fun. And for free.
248
273
 
@@ -364,4 +389,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
364
389
  ## Credits
365
390
  Barley is brought to you by the developer team from [StockPro](https://www.stock-pro.fr/).
366
391
 
367
- [![Barley is brought to you by StockPro](img/stockpro.png)](https://www.stock-pro.fr/)
392
+ [![Barley is brought to you by StockPro](https://i.imgur.com/5a0veEG.png)](https://www.stock-pro.fr/)
@@ -4,6 +4,7 @@ module Barley
4
4
  # Makes a Model serializable
5
5
  #
6
6
  # * Allows setting a default model Serializer
7
+ #
7
8
  # @example
8
9
  # class Item < ApplicationRecord
9
10
  # include Barley::Serializable
@@ -19,10 +20,15 @@ module Barley
19
20
  class_methods do
20
21
  # @example without cache
21
22
  # serializer ItemSerializer
23
+ #
22
24
  # @example with cache
23
25
  # serializer ItemSerializer, cache: true
26
+ #
24
27
  # @example with cache and expires_in
25
28
  # serializer ItemSerializer, cache: {expires_in: 1.hour}
29
+ #
30
+ # @param klass [Class] the serializer class
31
+ # @param cache [Boolean, Hash<Symbol, ActiveSupport::Duration>] whether to cache the result, or a hash with options for the cache
26
32
  def serializer(klass, cache: false)
27
33
  define_method(:serializer) do
28
34
  klass.new(self, cache: cache)
@@ -33,6 +39,16 @@ module Barley
33
39
  included do
34
40
  serializer "#{self}Serializer".constantize
35
41
 
42
+ # Serializes the model
43
+ #
44
+ # @note this method does not provide default rails options like `only` or `except`.
45
+ # This is because the Barley serializer should be the only place where the attributes are defined.
46
+ #
47
+ # @param serializer [Class] the serializer to use
48
+ # @param cache [Boolean, Hash<Symbol, ActiveSupport::Duration>] whether to cache the result, or a hash with options for the cache
49
+ # @param root [Boolean] whether to include the root key in the hash
50
+ #
51
+ # @return [Hash] the serialized attributes
36
52
  def as_json(serializer: nil, cache: false, root: false)
37
53
  serializer ||= self.serializer.class
38
54
  serializer.new(self, cache: cache, root: root).serializable_hash
@@ -5,29 +5,118 @@ module Barley
5
5
  attr_accessor :object
6
6
 
7
7
  class << self
8
+ # Defines attributes for the serializer
9
+ #
10
+ # Accepts either a list of symbols or a hash of symbols and Dry::Types, or a mix of both
11
+ #
12
+ # @example only symbols
13
+ # attributes :id, :name, :email
14
+ # # => {id: 1234, name: "John Doe", email: "john.doe@example"}
15
+ #
16
+ # @example with types
17
+ # attributes id: Types::Strict::Integer, name: Types::Strict::String, email: Types::Strict::String
18
+ # # => {id: 1234, name: "John Doe", email: "john.doe@example"}
19
+ #
20
+ # @example with types and symbols
21
+ # attributes :id, name: Types::Strict::String, email: Types::Strict::String
22
+ # # => {id: 1234, name: "John Doe", email: "john.doe@example"}
23
+ #
24
+ # @see Serializer#attribute
25
+ #
26
+ # @param keys [Hash<Symbol, Dry::Types>, Array<Symbol>] mix of symbols and hashes of symbols and Dry::Types
8
27
  def attributes(*keys)
28
+ if keys.last.is_a?(Hash)
29
+ keys.pop.each do |key, type|
30
+ attribute(key, type: type)
31
+ end
32
+ end
9
33
  keys.each do |key|
10
- define_method(key) do
11
- object.send(key)
34
+ if key.is_a?(Hash)
35
+ attribute(key.keys.first, type: key.values.first)
36
+ else
37
+ attribute(key)
12
38
  end
13
- set_class_iv(:@defined_attributes, key)
14
39
  end
15
40
  end
16
41
 
17
- def attribute(key, key_name: nil, &block)
42
+ # Defines a single attribute for the serializer
43
+ #
44
+ # Type checking is done with Dry::Types. If a type is not provided, the value is returned as is.
45
+ # Dry::Types can be used to coerce the value to the desired type and to check constraints.
46
+ #
47
+ # @see https://dry-rb.org/gems/dry-types/main/
48
+ #
49
+ # @raise [Dry::Types::ConstraintError] if the type does not match
50
+ #
51
+ # @example simple attribute
52
+ # attribute :id
53
+ # # => {id: 1234}
54
+ #
55
+ # @example attribute with a different key name
56
+ # attribute :name, key_name: :full_name
57
+ # # => {full_name: "John Doe"}
58
+ #
59
+ # @example attribute with a type
60
+ # attribute :email, type: Types::Strict::String
61
+ # # => {email: "john.doe@example"}
62
+ #
63
+ # @example attribute with a type and a block
64
+ # attribute :email, type: Types::Strict::String do
65
+ # object.email.upcase
66
+ # end
67
+ # # => {email: "JOHN.DOE@EXAMPLE"}
68
+ #
69
+ # @param key [Symbol] the attribute name
70
+ # @param key_name [Symbol] the key name in the hash
71
+ # @param type [Dry::Types] the type to use, or coerce the value to
72
+ # @param block [Proc] a block to use to compute the value
73
+ def attribute(key, key_name: nil, type: nil, &block)
18
74
  key_name ||= key
19
75
  if block
20
76
  define_method(key_name) do
21
- instance_eval(&block)
77
+ type.nil? ? instance_eval(&block) : type[instance_eval(&block)]
22
78
  end
23
79
  else
24
80
  define_method(key_name) do
25
- object.send(key)
81
+ type.nil? ? object.send(key) : type[object.send(key)]
26
82
  end
27
83
  end
28
84
  set_class_iv(:@defined_attributes, key_name)
29
85
  end
30
86
 
87
+ # Defines a single association for the serializer
88
+ #
89
+ # @example using the default serializer of the associated model
90
+ # one :group
91
+ # # => {group: {id: 1234, name: "Group 1"}}
92
+ #
93
+ # @example using a custom serializer
94
+ # one :group, serializer: MyCustomGroupSerializer
95
+ # # => {group: {id: 1234, name: "Group 1"}}
96
+ #
97
+ # @example using a block with an inline serializer definition
98
+ # one :group do
99
+ # attributes :id, :name
100
+ # end
101
+ # # => {group: {id: 1234, name: "Group 1"}}
102
+ #
103
+ # @example using a different key name
104
+ # one :group, key_name: :my_group
105
+ # # => {my_group: {id: 1234, name: "Group 1"}}
106
+ #
107
+ # @example using cache
108
+ # one :group, cache: true
109
+ # # => {group: {id: 1234, name: "Group 1"}}
110
+ #
111
+ # @example using cache and expires_in
112
+ # one :group, cache: {expires_in: 1.hour}
113
+ # # => {group: {id: 1234, name: "Group 1"}}
114
+ #
115
+ # @param key [Symbol] the association name
116
+ # @param key_name [Symbol] the key name in the hash
117
+ # @param serializer [Class] the serializer to use
118
+ # @param cache [Boolean, Hash<Symbol, ActiveSupport::Duration>] whether to cache the result, or a hash with options for the cache
119
+ # @param block [Proc] a block to use to define the serializer inline
31
120
  def one(key, key_name: nil, serializer: nil, cache: false, &block)
32
121
  key_name ||= key
33
122
  if block
@@ -45,6 +134,39 @@ module Barley
45
134
  set_class_iv(:@defined_attributes, key_name)
46
135
  end
47
136
 
137
+ # Defines a collection association for the serializer
138
+ #
139
+ # @example using the default serializer of the associated model
140
+ # many :groups
141
+ # # => {groups: [{id: 1234, name: "Group 1"}, {id: 5678, name: "Group 2"}]}
142
+ #
143
+ # @example using a custom serializer
144
+ # many :groups, serializer: MyCustomGroupSerializer
145
+ # # => {groups: [{id: 1234, name: "Group 1"}, {id: 5678, name: "Group 2"}]}
146
+ #
147
+ # @example using a block with an inline serializer definition
148
+ # many :groups do
149
+ # attributes :id, :name
150
+ # end
151
+ # # => {groups: [{id: 1234, name: "Group 1"}, {id: 5678, name: "Group 2"}]}
152
+ #
153
+ # @example using a different key name
154
+ # many :groups, key_name: :my_groups
155
+ # # => {my_groups: [{id: 1234, name: "Group 1"}, {id: 5678, name: "Group 2"}]}
156
+ #
157
+ # @example using cache
158
+ # many :groups, cache: true
159
+ # # => {groups: [{id: 1234, name: "Group 1"}, {id: 5678, name: "Group 2"}]}
160
+ #
161
+ # @example using cache and expires_in
162
+ # many :groups, cache: {expires_in: 1.hour}
163
+ # # => {groups: [{id: 1234, name: "Group 1"}, {id: 5678, name: "Group 2"}]}
164
+ #
165
+ # @param key [Symbol] the association name
166
+ # @param key_name [Symbol] the key name in the hash
167
+ # @param serializer [Class] the serializer to use
168
+ # @param cache [Boolean, Hash<Symbol, ActiveSupport::Duration>] whether to cache the result, or a hash with options for the cache
169
+ # @param block [Proc] a block to use to define the serializer inline
48
170
  def many(key, key_name: nil, serializer: nil, cache: false, &block)
49
171
  key_name ||= key
50
172
  if block
@@ -62,6 +184,12 @@ module Barley
62
184
  set_class_iv(:@defined_attributes, key_name)
63
185
  end
64
186
 
187
+ # Either sets or appends a key to an instance variable
188
+ #
189
+ # @api private
190
+ #
191
+ # @param iv [Symbol] the instance variable to set
192
+ # @param key [Symbol] the key to add to the instance variable
65
193
  def set_class_iv(iv, key)
66
194
  instance_variable_defined?(iv) ? instance_variable_get(iv) << key : instance_variable_set(iv, [key])
67
195
  end
@@ -69,8 +197,13 @@ module Barley
69
197
 
70
198
  # @example with cache
71
199
  # Barley::Serializer.new(object, cache: true)
200
+ #
72
201
  # @example with cache and expires_in
73
202
  # Barley::Serializer.new(object, cache: {expires_in: 1.hour})
203
+ #
204
+ # @param object [Object] the object to serialize
205
+ # @param cache [Boolean, Hash<Symbol, ActiveSupport::Duration>] a boolean to cache the result, or a hash with options for the cache
206
+ # @param root [Boolean] whether to include the root key in the hash
74
207
  def initialize(object, cache: false, root: false)
75
208
  @object = object
76
209
  @root = root
@@ -81,6 +214,9 @@ module Barley
81
214
  end
82
215
  end
83
216
 
217
+ # Serializes the object
218
+ #
219
+ # @return [Hash] the serializable hash
84
220
  def serializable_hash
85
221
  if @cache
86
222
  Barley::Cache.fetch(cache_base_key, expires_in: @expires_in) do
@@ -91,12 +227,20 @@ module Barley
91
227
  end
92
228
  end
93
229
 
230
+ # Clears the cache for the object
231
+ #
232
+ # @param key [String] the cache key
233
+ #
234
+ # @return [Boolean] whether the cache was cleared
94
235
  def clear_cache(key: cache_base_key)
95
236
  Barley::Cache.delete(key)
96
237
  end
97
238
 
98
239
  private
99
240
 
241
+ # @api private
242
+ #
243
+ # @return [String] the cache key
100
244
  def cache_base_key
101
245
  if object.updated_at.present?
102
246
  "#{object.class.name&.underscore}/#{object.id}/#{object.updated_at&.to_i}/barley_cache/"
@@ -105,10 +249,18 @@ module Barley
105
249
  end
106
250
  end
107
251
 
252
+ # @api private
253
+ #
254
+ # @return [Array<Symbol>] the defined attributes
108
255
  def defined_attributes
109
256
  self.class.instance_variable_get(:@defined_attributes)
110
257
  end
111
258
 
259
+ # Serializes the object
260
+ #
261
+ # @api private
262
+ #
263
+ # @return [Hash] the serializable hash
112
264
  def _serializable_hash
113
265
  hash = {}
114
266
 
@@ -119,6 +271,9 @@ module Barley
119
271
  @root ? {root_key => hash} : hash
120
272
  end
121
273
 
274
+ # @api private
275
+ #
276
+ # @return [Symbol] the root key, based on the class name
122
277
  def root_key
123
278
  object.class.name.underscore.to_sym
124
279
  end
@@ -1,3 +1,3 @@
1
1
  module Barley
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/barley.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require "barley/version"
2
2
  require "barley/railtie"
3
3
  require "barley/configuration"
4
+ require "dry-types"
4
5
 
5
6
  module Barley
6
7
  mattr_accessor :config
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: barley
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cedric Delalande
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-18 00:00:00.000000000 Z
11
+ date: 2023-10-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 6.1.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: dry-types
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.7.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.7.1
27
41
  description: Cerealize your ActiveModel objects into flat JSON objects with a dead
28
42
  simple DSL. Our daily bread is to make your API faster.
29
43
  email: