barley 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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: