barley 0.3.0 → 0.4.1

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: d882b89bf1bf77259eda96813c4732ef1b286142ecb29fcb945837b743e089c9
4
+ data.tar.gz: 87dc3b213a614957a668c08865b87eb44f805cf76b79a60c710025a7d18257d8
5
5
  SHA512:
6
- metadata.gz: 9f3482dc0562669991a64403c9c5cee510d61cc5ec79affa3715eb203ac1a48f1f15c676778d380eccbfe6efc57281eae6c179ab4378a62d35c31b0fd65529ea
7
- data.tar.gz: b9d2a98edf8704acf8b8466042b924b771384e1756fd06f9d23939f9bd0fb4368a4934303057edd0a53d0c8f487dc93c10649e39fe17b3031189f383a668ec54
6
+ metadata.gz: a7eb159a6e40374364504f5c828d7e1f82e7a581bd89fe374182916b0dbc6863caaea779acf409e58c8fe60ff3614acdfa944bb3f74dbd808957925db146dded
7
+ data.tar.gz: d84b0157c80e83cfa28dbafda0fd300f2774c4362b5e1e47f9fc0381fb0cd8713dce2f1b27f4cfb9896af2c11d354d92b8df0dcfaee6a94bb69e6aca738e45b2
data/README.md CHANGED
@@ -1,11 +1,18 @@
1
- ![Barley loqo](./img/barley.png)
1
+ ![Barley loqo](https://i.imgur.com/am0emi4.png)
2
2
 
3
- Barley is a dead simple, fast, and efficient ActiveModel JSON serializer.
3
+ ![Test suite badge](https://github.com/MoskitoHero/barley/actions/workflows/ruby.yml/badge.svg)
4
+ [![Gem Version](https://badge.fury.io/rb/barley.svg)](https://badge.fury.io/rb/barley)
5
+ ![Static Badge](https://img.shields.io/badge/Cereal%20-%20100%25%20-%20darklime)
4
6
 
5
- Cerealize your ActiveModel objects into flat hashes with a dead simple, yet versatile DSL, and caching baked in. Our daily bread is to make your API faster.
7
+ Barley is a dead simple, fast, and efficient ActiveModel serializer.
8
+
9
+ Cerealize your ActiveModel objects into flat hashes with a dead simple, yet versatile DSL, and caching and type-checking baked in. Our daily bread is to make your API faster.
6
10
 
7
11
  You don't believe us? Check out the [benchmarks](#benchmarks). 😎
8
12
 
13
+ ## API documentation
14
+ [Check out the API documentation here](https://rubydoc.info/github/MoskitoHero/barley/main).
15
+
9
16
  ## Usage
10
17
  Add the `Barley::Serializable` module to your ActiveModel object.
11
18
 
@@ -21,23 +28,30 @@ Then define your attributes and associations in a serializer class.
21
28
  ```ruby
22
29
  # /app/serializers/user_serializer.rb
23
30
  class UserSerializer < Barley::Serializer
24
- attributes :id, :name,
25
- attribute :email # single attribute
31
+
32
+ attributes id: Types::Strict::Integer, :name
33
+
34
+ attribute :email
35
+ attribute :value, type: Types::Coercible::Integer
26
36
 
27
- many :posts # relations
28
- one :group, serializer: CustomGroupSerializer # custom serializer
29
- many :related_users, key: :friends, cache: true # custom key, and caching
30
- one :profile, cache: { expires_in: 1.day } do # cache definition, and block (on associations) for nested, on-the-fly serializer
37
+ many :posts
38
+
39
+ one :group, serializer: CustomGroupSerializer
40
+
41
+ many :related_users, key: :friends, cache: true
42
+
43
+ one :profile, cache: { expires_in: 1.day } do
31
44
  attributes :avatar, :social_url
45
+
32
46
  attribute :badges do
33
- object.badges.map(&:display_name) # use object in a block to return custom code
47
+ object.badges.map(&:display_name)
34
48
  end
35
49
  end
36
50
 
37
51
  end
38
52
  ```
39
53
 
40
- The just use the `as_json` method on your model.
54
+ Then just use the `as_json` method on your model.
41
55
 
42
56
  ```ruby
43
57
  user = User.find(1)
@@ -243,6 +257,27 @@ Barley.configure do |config|
243
257
  end
244
258
  ```
245
259
 
260
+ ## Type checking
261
+ Barley can check the type of the object you are serializing with the [dry-types](https://dry-rb.org/gems/dry-types/main/) gem.
262
+
263
+ 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.
264
+
265
+ ```ruby
266
+ module Types
267
+ include Dry.Types()
268
+ end
269
+
270
+ class UserSerializer < Barley::Serializer
271
+ attributes id: Types::Strict::Integer, name: Types::Strict::String, email: Types::Strict::String.constrained(format: URI::MailTo::EMAIL_REGEXP)
272
+
273
+ attribute :role, type: Types::Coercible::String do
274
+ object.role.integer_or_string_coercible_value
275
+ end
276
+ end
277
+ ```
278
+
279
+ Check out [dry-types](https://dry-rb.org/gems/dry-types/main/) for all options and available types.
280
+
246
281
  ## Breakfast mode 🤡 (coming soon)
247
282
  You will soon be able to replace all occurrences of `Serializer` with `Cerealizer` in your codebase. Just for fun. And for free.
248
283
 
@@ -361,7 +396,16 @@ ams : 1299674 allocated - 28.20x more
361
396
  ## License
362
397
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
363
398
 
399
+ ## Contributing
400
+ You can contribute in several ways: reporting bugs, suggesting features, or contributing code. See [our contributing guidelines](CONTRIBUTING.md)
401
+
402
+ Make sure you adhere to [our code of conduct](CODE_OF_CONDUCT.md). We aim to keep this project open and inclusive.
403
+
404
+ ## Security
405
+
406
+ Please refer to our [security guidelines](SECURITY.md)
407
+
364
408
  ## Credits
365
409
  Barley is brought to you by the developer team from [StockPro](https://www.stock-pro.fr/).
366
410
 
367
- [![Barley is brought to you by StockPro](img/stockpro.png)](https://www.stock-pro.fr/)
411
+ [![Barley is brought to you by StockPro](https://i.imgur.com/5a0veEG.png)](https://www.stock-pro.fr/)
data/Rakefile CHANGED
@@ -1,3 +1,12 @@
1
1
  require "bundler/setup"
2
2
 
3
3
  require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ t.libs << "test"
9
+ end
10
+ desc "Run tests"
11
+
12
+ task default: :test
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Barley
4
+ class Error < StandardError
5
+ end
6
+ end
@@ -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,23 +20,55 @@ 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
- define_method(:serializer) do
28
- klass.new(self, cache: cache)
33
+ # We need to silence the warnings because we are defining a method with the same name as the parameter
34
+ # This avoids :
35
+ # - warning: method redefined; discarding old serializer
36
+ # - warning: previous definition of serializer was here
37
+ Kernel.silence_warnings do
38
+ define_method(:serializer) do
39
+ klass.new(self, cache: cache)
40
+ end
29
41
  end
30
42
  end
31
43
  end
32
44
 
33
45
  included do
34
- serializer "#{self}Serializer".constantize
46
+ begin
47
+ serializer "#{self}Serializer".constantize
48
+ rescue NameError
49
+ raise Barley::Error, "Could not find serializer for #{self}. Please define a #{self}Serializer class."
50
+ end
35
51
 
36
- def as_json(serializer: nil, cache: false, root: false)
37
- serializer ||= self.serializer.class
38
- serializer.new(self, cache: cache, root: root).serializable_hash
52
+ # Serializes the model
53
+ #
54
+ # @note this method does not provide default rails options like `only` or `except`.
55
+ # This is because the Barley serializer should be the only place where the attributes are defined.
56
+ #
57
+ # @option options [Class] :serializer the serializer to use
58
+ # @option options [Boolean, Hash<Symbol, ActiveSupport::Duration>] :cache whether to cache the result, or a hash with options for the cache
59
+ # @option options [Boolean] :root whether to include the root key
60
+ #
61
+ # @return [Hash] the serialized attributes
62
+ def as_json(options = nil)
63
+ options ||= {}
64
+ serializer = options[:serializer] || self.serializer.class
65
+ cache = options[:cache] || false
66
+ root = options[:root] || false
67
+ begin
68
+ serializer.new(self, cache: cache, root: root).serializable_hash
69
+ rescue NameError
70
+ raise Barley::Error, "Could not find serializer for #{self}. Please define a #{serializer} class."
71
+ end
39
72
  end
40
73
  end
41
74
  end
@@ -5,29 +5,116 @@ module Barley
5
5
  attr_accessor :object
6
6
 
7
7
  class << self
8
+ attr_accessor :defined_attributes
9
+
10
+ # Defines attributes for the serializer
11
+ #
12
+ # Accepts either a list of symbols or a hash of symbols and Dry::Types, or a mix of both
13
+ #
14
+ # @example only symbols
15
+ # attributes :id, :name, :email
16
+ # # => {id: 1234, name: "John Doe", email: "john.doe@example"}
17
+ #
18
+ # @example with types
19
+ # attributes id: Types::Strict::Integer, name: Types::Strict::String, email: Types::Strict::String
20
+ # # => {id: 1234, name: "John Doe", email: "john.doe@example"}
21
+ #
22
+ # @example with types and symbols
23
+ # attributes :id, name: Types::Strict::String, email: Types::Strict::String
24
+ # # => {id: 1234, name: "John Doe", email: "john.doe@example"}
25
+ #
26
+ # @see Serializer#attribute
27
+ #
28
+ # @param keys [Hash<Symbol, Dry::Types>, Array<Symbol>] mix of symbols and hashes of symbols and Dry::Types
8
29
  def attributes(*keys)
30
+ if keys.last.is_a?(Hash)
31
+ keys.pop.each do |key, type|
32
+ attribute(key, type: type)
33
+ end
34
+ end
9
35
  keys.each do |key|
10
- define_method(key) do
11
- object.send(key)
36
+ if key.is_a?(Hash)
37
+ attribute(key.keys.first, type: key.values.first)
38
+ else
39
+ attribute(key)
12
40
  end
13
- set_class_iv(:@defined_attributes, key)
14
41
  end
15
42
  end
16
43
 
17
- def attribute(key, key_name: nil, &block)
44
+ # Defines a single attribute for the serializer
45
+ #
46
+ # Type checking is done with Dry::Types. If a type is not provided, the value is returned as is.
47
+ # Dry::Types can be used to coerce the value to the desired type and to check constraints.
48
+ #
49
+ # @see https://dry-rb.org/gems/dry-types/main/
50
+ #
51
+ # @raise [Dry::Types::ConstraintError] if the type does not match
52
+ #
53
+ # @example simple attribute
54
+ # attribute :id
55
+ # # => {id: 1234}
56
+ #
57
+ # @example attribute with a different key name
58
+ # attribute :name, key_name: :full_name
59
+ # # => {full_name: "John Doe"}
60
+ #
61
+ # @example attribute with a type
62
+ # attribute :email, type: Types::Strict::String
63
+ # # => {email: "john.doe@example"}
64
+ #
65
+ # @example attribute with a type and a block
66
+ # attribute :email, type: Types::Strict::String do
67
+ # object.email.upcase
68
+ # end
69
+ # # => {email: "JOHN.DOE@EXAMPLE"}
70
+ #
71
+ # @param key [Symbol] the attribute name
72
+ # @param key_name [Symbol] the key name in the hash
73
+ # @param type [Dry::Types] the type to use, or coerce the value to
74
+ # @param block [Proc] a block to use to compute the value
75
+ def attribute(key, key_name: nil, type: nil, &block)
18
76
  key_name ||= key
19
- if block
20
- define_method(key_name) do
21
- instance_eval(&block)
22
- end
23
- else
24
- define_method(key_name) do
25
- object.send(key)
26
- end
77
+ define_method(key_name) do
78
+ value = block ? instance_eval(&block) : object.send(key)
79
+ type.nil? ? value : type[value]
27
80
  end
28
- set_class_iv(:@defined_attributes, key_name)
81
+
82
+ self.defined_attributes = (defined_attributes || []) << key_name
29
83
  end
30
84
 
85
+ # Defines a single association for the serializer
86
+ #
87
+ # @example using the default serializer of the associated model
88
+ # one :group
89
+ # # => {group: {id: 1234, name: "Group 1"}}
90
+ #
91
+ # @example using a custom serializer
92
+ # one :group, serializer: MyCustomGroupSerializer
93
+ # # => {group: {id: 1234, name: "Group 1"}}
94
+ #
95
+ # @example using a block with an inline serializer definition
96
+ # one :group do
97
+ # attributes :id, :name
98
+ # end
99
+ # # => {group: {id: 1234, name: "Group 1"}}
100
+ #
101
+ # @example using a different key name
102
+ # one :group, key_name: :my_group
103
+ # # => {my_group: {id: 1234, name: "Group 1"}}
104
+ #
105
+ # @example using cache
106
+ # one :group, cache: true
107
+ # # => {group: {id: 1234, name: "Group 1"}}
108
+ #
109
+ # @example using cache and expires_in
110
+ # one :group, cache: {expires_in: 1.hour}
111
+ # # => {group: {id: 1234, name: "Group 1"}}
112
+ #
113
+ # @param key [Symbol] the association name
114
+ # @param key_name [Symbol] the key name in the hash
115
+ # @param serializer [Class] the serializer to use
116
+ # @param cache [Boolean, Hash<Symbol, ActiveSupport::Duration>] whether to cache the result, or a hash with options for the cache
117
+ # @param block [Proc] a block to use to define the serializer inline
31
118
  def one(key, key_name: nil, serializer: nil, cache: false, &block)
32
119
  key_name ||= key
33
120
  if block
@@ -42,9 +129,42 @@ module Barley
42
129
  el_serializer = serializer || element.serializer.class
43
130
  el_serializer.new(element, cache: cache).serializable_hash
44
131
  end
45
- set_class_iv(:@defined_attributes, key_name)
132
+ self.defined_attributes = (defined_attributes || []) << key_name
46
133
  end
47
134
 
135
+ # Defines a collection association for the serializer
136
+ #
137
+ # @example using the default serializer of the associated model
138
+ # many :groups
139
+ # # => {groups: [{id: 1234, name: "Group 1"}, {id: 5678, name: "Group 2"}]}
140
+ #
141
+ # @example using a custom serializer
142
+ # many :groups, serializer: MyCustomGroupSerializer
143
+ # # => {groups: [{id: 1234, name: "Group 1"}, {id: 5678, name: "Group 2"}]}
144
+ #
145
+ # @example using a block with an inline serializer definition
146
+ # many :groups do
147
+ # attributes :id, :name
148
+ # end
149
+ # # => {groups: [{id: 1234, name: "Group 1"}, {id: 5678, name: "Group 2"}]}
150
+ #
151
+ # @example using a different key name
152
+ # many :groups, key_name: :my_groups
153
+ # # => {my_groups: [{id: 1234, name: "Group 1"}, {id: 5678, name: "Group 2"}]}
154
+ #
155
+ # @example using cache
156
+ # many :groups, cache: true
157
+ # # => {groups: [{id: 1234, name: "Group 1"}, {id: 5678, name: "Group 2"}]}
158
+ #
159
+ # @example using cache and expires_in
160
+ # many :groups, cache: {expires_in: 1.hour}
161
+ # # => {groups: [{id: 1234, name: "Group 1"}, {id: 5678, name: "Group 2"}]}
162
+ #
163
+ # @param key [Symbol] the association name
164
+ # @param key_name [Symbol] the key name in the hash
165
+ # @param serializer [Class] the serializer to use
166
+ # @param cache [Boolean, Hash<Symbol, ActiveSupport::Duration>] whether to cache the result, or a hash with options for the cache
167
+ # @param block [Proc] a block to use to define the serializer inline
48
168
  def many(key, key_name: nil, serializer: nil, cache: false, &block)
49
169
  key_name ||= key
50
170
  if block
@@ -59,18 +179,19 @@ module Barley
59
179
  el_serializer = serializer || elements.first.serializer.class
60
180
  elements.map { |element| el_serializer.new(element, cache: cache).serializable_hash }.reject(&:blank?)
61
181
  end
62
- set_class_iv(:@defined_attributes, key_name)
63
- end
64
-
65
- def set_class_iv(iv, key)
66
- instance_variable_defined?(iv) ? instance_variable_get(iv) << key : instance_variable_set(iv, [key])
182
+ self.defined_attributes = (defined_attributes || []) << key_name
67
183
  end
68
184
  end
69
185
 
70
186
  # @example with cache
71
187
  # Barley::Serializer.new(object, cache: true)
188
+ #
72
189
  # @example with cache and expires_in
73
190
  # Barley::Serializer.new(object, cache: {expires_in: 1.hour})
191
+ #
192
+ # @param object [Object] the object to serialize
193
+ # @param cache [Boolean, Hash<Symbol, ActiveSupport::Duration>] a boolean to cache the result, or a hash with options for the cache
194
+ # @param root [Boolean] whether to include the root key in the hash
74
195
  def initialize(object, cache: false, root: false)
75
196
  @object = object
76
197
  @root = root
@@ -81,6 +202,9 @@ module Barley
81
202
  end
82
203
  end
83
204
 
205
+ # Serializes the object
206
+ #
207
+ # @return [Hash] the serializable hash
84
208
  def serializable_hash
85
209
  if @cache
86
210
  Barley::Cache.fetch(cache_base_key, expires_in: @expires_in) do
@@ -91,12 +215,20 @@ module Barley
91
215
  end
92
216
  end
93
217
 
218
+ # Clears the cache for the object
219
+ #
220
+ # @param key [String] the cache key
221
+ #
222
+ # @return [Boolean] whether the cache was cleared
94
223
  def clear_cache(key: cache_base_key)
95
224
  Barley::Cache.delete(key)
96
225
  end
97
226
 
98
227
  private
99
228
 
229
+ # @api private
230
+ #
231
+ # @return [String] the cache key
100
232
  def cache_base_key
101
233
  if object.updated_at.present?
102
234
  "#{object.class.name&.underscore}/#{object.id}/#{object.updated_at&.to_i}/barley_cache/"
@@ -105,10 +237,18 @@ module Barley
105
237
  end
106
238
  end
107
239
 
240
+ # @api private
241
+ #
242
+ # @return [Array<Symbol>] the defined attributes
108
243
  def defined_attributes
109
- self.class.instance_variable_get(:@defined_attributes)
244
+ self.class.defined_attributes
110
245
  end
111
246
 
247
+ # Serializes the object
248
+ #
249
+ # @api private
250
+ #
251
+ # @return [Hash] the serializable hash
112
252
  def _serializable_hash
113
253
  hash = {}
114
254
 
@@ -119,6 +259,9 @@ module Barley
119
259
  @root ? {root_key => hash} : hash
120
260
  end
121
261
 
262
+ # @api private
263
+ #
264
+ # @return [Symbol] the root key, based on the class name
122
265
  def root_key
123
266
  object.class.name.underscore.to_sym
124
267
  end
@@ -1,3 +1,3 @@
1
1
  module Barley
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.1"
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.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cedric Delalande
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-18 00:00:00.000000000 Z
11
+ date: 2023-10-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -24,8 +24,23 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 6.1.0
27
- description: Cerealize your ActiveModel objects into flat JSON objects with a dead
28
- simple DSL. Our daily bread is to make your API faster.
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
41
+ description: Cerealize your ActiveModel objects into flat hashes with a dead simple,
42
+ yet versatile DSL, and caching and type-checking baked in. Our daily bread is to
43
+ make your API faster.
29
44
  email:
30
45
  - weengs@moskitohero.com
31
46
  executables: []
@@ -38,6 +53,7 @@ files:
38
53
  - lib/barley.rb
39
54
  - lib/barley/cache.rb
40
55
  - lib/barley/configuration.rb
56
+ - lib/barley/error.rb
41
57
  - lib/barley/railtie.rb
42
58
  - lib/barley/serializable.rb
43
59
  - lib/barley/serializer.rb
@@ -60,7 +76,8 @@ metadata:
60
76
  homepage_uri: https://github.com/moskitohero/barley
61
77
  source_code_uri: https://github.com/moskitohero/barley
62
78
  changelog_uri: https://github.com/moskitohero/barley/CHANGELOG.md
63
- post_install_message:
79
+ documentation_uri: https://rubydoc.info/github/MoskitoHero/barley/main
80
+ post_install_message:
64
81
  rdoc_options: []
65
82
  require_paths:
66
83
  - lib
@@ -75,8 +92,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
75
92
  - !ruby/object:Gem::Version
76
93
  version: '0'
77
94
  requirements: []
78
- rubygems_version: 3.2.33
79
- signing_key:
95
+ rubygems_version: 3.3.26
96
+ signing_key:
80
97
  specification_version: 4
81
- summary: Barley is a dead simple, fast, and efficient ActiveModel JSON serializer.
98
+ summary: Barley is a dead simple, fast, and efficient ActiveModel serializer.
82
99
  test_files: []