Compare commits

..

17 Commits

Author SHA1 Message Date
Clemens Kofler
7db80f673d Clarify README wording related to contributions 2022-04-04 18:52:58 +01:00
Peter Goldstein
bcee2b597b Add Ruby 3.1 to CI
Also fixes the unquoted 3.0, which needs to be quoted to avoid truncation
Addresses a few Rubocop issues and an active_support require issue on Rails 7 w/Zeitwerk
2022-02-02 21:40:34 +00:00
Igor Victor
44cf8495ea Update ci.yml 2021-04-12 11:50:08 +01:00
Stas SUȘCOV
37235057df
Version bump. 2021-03-11 18:35:11 +00:00
Stas SUȘCOV
c21f9def8f Updated the changelog. 2021-03-11 18:33:35 +00:00
Stas SUȘCOV
8b74954478 Added ruby v3 to CI matrix. 2021-03-11 18:33:35 +00:00
Stas SUȘCOV
ef93f7f358 Make rubocop happy. 2021-03-11 18:33:35 +00:00
HubertVonHerbert
c5eb1ce27c
Fix Ruby3 compatibility issue with Procs (#160)
* Fix Ruby3 compatibility issue with Procs

* Fix rubocop complaints

* Remove ruby 3 from CI actions

* Simplify check for &:foo procs
2021-03-11 18:29:59 +00:00
Ryan Romanchuk
a25d415b4d Fix most likely copy/paste error
this lambda yielding a bool for `belongs_to` might confuse readers or conflate the :if block

which would probably look something like this 

```ruby
belongs_to :primary_agent, if: proc { |movie, params| params[:current_user].present? } do |movie, params|
    # in here, params is a hash containing the `:current_user` key
    params[:current_user]
  end
```
2021-02-14 22:17:08 +00:00
Stas SUȘCOV
c3376037e7 Let everyone know there's a WIP branch for v3. 2021-01-11 11:11:24 +00:00
Yaroslav Kasperovych
963cd77900 Fix require clause in fastjson migration guide
Seems like the require clause should be different as it produces an error if used as it is right now.
2020-11-06 10:15:26 +00:00
Tony Dehnke
98dd59884c Fix namespace name per comment in #139 2020-10-26 12:01:52 +00:00
Guillaume Briday
b7e8a30833 Fix typo in readme 2020-10-25 18:13:08 +00:00
Guillaume Briday
f4ed4f0440 Adding migration section 2020-10-25 17:23:20 +00:00
Jorge Garcia
88061bcfac Add deserialization options on Readme 2020-10-08 10:21:20 +01:00
nattfodd
f8255771dc Raise FastJsonapi scoped error in case of unsupported include 2020-09-18 13:12:41 +03:00
Donatas Povilaitis
1bcf8d2cb5
Do not add empty relationships key (#116) 2020-09-01 20:44:08 +01:00
20 changed files with 181 additions and 26 deletions

View File

@ -4,16 +4,16 @@ on: [push, pull_request]
jobs: jobs:
tests: tests:
runs-on: ubuntu-18.04 runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
ruby: [2.4, 2.5, 2.6, 2.7] ruby: [2.4, 2.7, '3.0', 3.1, truffleruby-head]
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
- name: Sets up the Ruby version - name: Sets up the Ruby version
uses: actions/setup-ruby@v1 uses: ruby/setup-ruby@v1
with: with:
ruby-version: ${{ matrix.ruby }} ruby-version: ${{ matrix.ruby }}

View File

@ -2,6 +2,10 @@ require:
- rubocop-performance - rubocop-performance
- rubocop-rspec - rubocop-rspec
AllCops:
NewCops: enable
SuggestExtensions: false
Style/FrozenStringLiteralComment: Style/FrozenStringLiteralComment:
Enabled: false Enabled: false
@ -38,6 +42,9 @@ Performance/TimesMap:
Exclude: Exclude:
- 'spec/**/**.rb' - 'spec/**/**.rb'
Gemspec/RequiredRubyVersion:
Enabled: false
# TODO: Fix these... # TODO: Fix these...
Style/Documentation: Style/Documentation:
Enabled: false Enabled: false
@ -76,3 +83,20 @@ Naming/PredicateName:
Naming/AccessorMethodName: Naming/AccessorMethodName:
Exclude: Exclude:
- 'lib/**/**.rb' - 'lib/**/**.rb'
Style/CaseLikeIf:
Exclude:
- 'lib/fast_jsonapi/object_serializer.rb'
Style/OptionalBooleanParameter:
Exclude:
- 'lib/fast_jsonapi/serialization_core.rb'
- 'lib/fast_jsonapi/relationship.rb'
Lint/DuplicateBranch:
Exclude:
- 'lib/fast_jsonapi/relationship.rb'
Style/DocumentDynamicEvalDefinition:
Exclude:
- 'lib/extensions/has_one.rb'

View File

@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
- ... - ...
## [2.2.0] - 2021-03-11
### Added
- Proper error is raised on unsupported includes (#125)
### Changed
- Documentation updates (#137 #139 #143 #146)
### Fixed
- Empty relationships are no longer added to serialized doc (#116)
- Ruby v3 compatibility (#160)
## [2.1.0] - 2020-08-30 ## [2.1.0] - 2020-08-30
### Added ### Added

View File

@ -1,5 +1,11 @@
# JSON:API Serialization Library # JSON:API Serialization Library
## :warning: :construction: v2 (the `master` branch) is in maintenance mode! :construction: :warning:
We'll gladly accept bugfixes and security-related fixes for v2 (the `master` branch), but at this stage, contributions for new features/improvements are welcome only for v3. Please feel free to leave comments in the [v3 Pull Request](https://github.com/jsonapi-serializer/jsonapi-serializer/pull/141).
---
A fast [JSON:API](https://jsonapi.org/) serializer for Ruby Objects. A fast [JSON:API](https://jsonapi.org/) serializer for Ruby Objects.
Previously this project was called **fast_jsonapi**, we forked the project Previously this project was called **fast_jsonapi**, we forked the project
@ -39,6 +45,8 @@ article in the `docs` folder for any questions related to methodology.
* [Sparse Fieldsets](#sparse-fieldsets) * [Sparse Fieldsets](#sparse-fieldsets)
* [Using helper methods](#using-helper-methods) * [Using helper methods](#using-helper-methods)
* [Performance Instrumentation](#performance-instrumentation) * [Performance Instrumentation](#performance-instrumentation)
* [Deserialization](#deserialization)
* [Migrating from Netflix/fast_jsonapi](#migrating-from-netflixfast_jsonapi)
* [Contributing](#contributing) * [Contributing](#contributing)
@ -458,7 +466,7 @@ class MovieSerializer
belongs_to :primary_agent do |movie, params| belongs_to :primary_agent do |movie, params|
# in here, params is a hash containing the `:current_user` key # in here, params is a hash containing the `:current_user` key
params[:current_user].is_employee? ? true : false params[:current_user]
end end
end end
@ -693,6 +701,61 @@ tests. To run tests use the following command:
rspec rspec
``` ```
## Deserialization
We currently do not support deserialization, but we recommend to use any of the next gems:
### [JSONAPI.rb](https://github.com/stas/jsonapi.rb)
This gem provides the next features alongside deserialization:
- Collection meta
- Error handling
- Includes and sparse fields
- Filtering and sorting
- Pagination
## Migrating from Netflix/fast_jsonapi
If you come from [Netflix/fast_jsonapi](https://github.com/Netflix/fast_jsonapi), here is the instructions to switch.
### Modify your Gemfile
```diff
- gem 'fast_jsonapi'
+ gem 'jsonapi-serializer'
```
### Replace all constant references
```diff
class MovieSerializer
- include FastJsonapi::ObjectSerializer
+ include JSONAPI::Serializer
end
```
### Replace removed methods
```diff
- json_string = MovieSerializer.new(movie).serialized_json
+ json_string = MovieSerializer.new(movie).serializable_hash.to_json
```
### Replace require references
```diff
- require 'fast_jsonapi'
+ require 'jsonapi/serializer'
```
### Update your cache options
See [docs](https://github.com/jsonapi-serializer/jsonapi-serializer#caching).
```diff
- cache_options enabled: true, cache_length: 12.hours
+ cache_options store: Rails.cache, namespace: 'jsonapi-serializer', expires_in: 1.hour
```
## Contributing ## Contributing
Please follow the instructions we provide as part of the issue and Please follow the instructions we provide as part of the issue and

View File

@ -12,7 +12,7 @@ Gem::Specification.new do |gem|
gem.summary = 'Fast JSON:API serialization library' gem.summary = 'Fast JSON:API serialization library'
gem.description = 'Fast, simple and easy to use '\ gem.description = 'Fast, simple and easy to use '\
'JSON:API serialization library (also known as fast_jsonapi).' 'JSON:API serialization library (also known as fast_jsonapi).'
gem.homepage = 'https://github.com/jsonapi-serializer/jsonapi-serializer' gem.homepage = 'https://github.com/jsonapi-serializer/jsonapi-serializer'
gem.licenses = ['Apache-2.0'] gem.licenses = ['Apache-2.0']
gem.files = Dir['lib/**/*'] gem.files = Dir['lib/**/*']
@ -33,4 +33,5 @@ Gem::Specification.new do |gem|
gem.add_development_dependency('rubocop-rspec') gem.add_development_dependency('rubocop-rspec')
gem.add_development_dependency('simplecov') gem.add_development_dependency('simplecov')
gem.add_development_dependency('sqlite3') gem.add_development_dependency('sqlite3')
gem.metadata['rubygems_mfa_required'] = 'true'
end end

View File

@ -1,5 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'jsonapi/serializer/errors'
module FastJsonapi module FastJsonapi
require 'fast_jsonapi/object_serializer' require 'fast_jsonapi/object_serializer'
if defined?(::Rails) if defined?(::Rails)

View File

@ -6,7 +6,16 @@ module FastJsonapi
# @param [Array<Object>] *params any number of parameters to be passed to the Proc # @param [Array<Object>] *params any number of parameters to be passed to the Proc
# @return [Object] the result of the Proc call with the supplied parameters # @return [Object] the result of the Proc call with the supplied parameters
def call_proc(proc, *params) def call_proc(proc, *params)
proc.call(*params.take(proc.parameters.length)) # The parameters array for a lambda created from a symbol (&:foo) differs
# from explictly defined procs/lambdas, so we can't deduce the number of
# parameters from the array length (and differs between Ruby 2.x and 3).
# In the case of negative arity -- unlimited/unknown argument count --
# just send the object to act as the method receiver.
if proc.arity.negative?
proc.call(params.first)
else
proc.call(*params.take(proc.parameters.length))
end
end end
end end
end end

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'active_support'
require 'active_support/time' require 'active_support/time'
require 'active_support/concern' require 'active_support/concern'
require 'active_support/inflector' require 'active_support/inflector'
@ -133,9 +134,7 @@ module FastJsonapi
def reflected_record_type def reflected_record_type
return @reflected_record_type if defined?(@reflected_record_type) return @reflected_record_type if defined?(@reflected_record_type)
@reflected_record_type ||= begin @reflected_record_type ||= (name.split('::').last.chomp('Serializer').underscore.to_sym if name&.end_with?('Serializer'))
name.split('::').last.chomp('Serializer').underscore.to_sym if name&.end_with?('Serializer')
end
end end
def set_key_transform(transform_name) def set_key_transform(transform_name)
@ -228,10 +227,10 @@ module FastJsonapi
# TODO: Remove this undocumented option. # TODO: Remove this undocumented option.
# Delegate the caching to the serializer exclusively. # Delegate the caching to the serializer exclusively.
if !relationship.cached if relationship.cached
uncachable_relationships_to_serialize[relationship.name] = relationship
else
cachable_relationships_to_serialize[relationship.name] = relationship cachable_relationships_to_serialize[relationship.name] = relationship
else
uncachable_relationships_to_serialize[relationship.name] = relationship
end end
relationships_to_serialize[relationship.name] = relationship relationships_to_serialize[relationship.name] = relationship
end end
@ -302,7 +301,7 @@ module FastJsonapi
def serializer_for(name) def serializer_for(name)
namespace = self.name.gsub(/()?\w+Serializer$/, '') namespace = self.name.gsub(/()?\w+Serializer$/, '')
serializer_name = name.to_s.demodulize.classify + 'Serializer' serializer_name = "#{name.to_s.demodulize.classify}Serializer"
serializer_class_name = namespace + serializer_name serializer_class_name = namespace + serializer_name
begin begin
serializer_class_name.constantize serializer_class_name.constantize
@ -340,9 +339,9 @@ module FastJsonapi
def validate_includes!(includes) def validate_includes!(includes)
return if includes.blank? return if includes.blank?
parse_includes_list(includes).keys.each do |include_item| parse_includes_list(includes).each_key do |include_item|
relationship_to_include = relationships_to_serialize[include_item] relationship_to_include = relationships_to_serialize[include_item]
raise ArgumentError, "#{include_item} is not specified as a relationship on #{name}" unless relationship_to_include raise(JSONAPI::Serializer::UnsupportedIncludeError.new(include_item, name)) unless relationship_to_include
relationship_to_include.static_serializer # called for a side-effect to check for a known serializer class. relationship_to_include.static_serializer # called for a side-effect to check for a known serializer class.
end end

View File

@ -12,12 +12,12 @@ module FastJsonapi
object_block:, object_block:,
serializer:, serializer:,
relationship_type:, relationship_type:,
cached: false,
polymorphic:, polymorphic:,
conditional_proc:, conditional_proc:,
transform_method:, transform_method:,
links:, links:,
meta:, meta:,
cached: false,
lazy_load_data: false lazy_load_data: false
) )
@owner = owner @owner = owner

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'active_support'
require 'active_support/concern' require 'active_support/concern'
require 'digest/sha1' require 'digest/sha1'
@ -71,12 +72,11 @@ module FastJsonapi
record_hash = cache_store_instance.fetch(record, **cache_opts) do record_hash = cache_store_instance.fetch(record, **cache_opts) do
temp_hash = id_hash(id_from_record(record, params), record_type, true) temp_hash = id_hash(id_from_record(record, params), record_type, true)
temp_hash[:attributes] = attributes_hash(record, fieldset, params) if attributes_to_serialize.present? temp_hash[:attributes] = attributes_hash(record, fieldset, params) if attributes_to_serialize.present?
temp_hash[:relationships] = {}
temp_hash[:relationships] = relationships_hash(record, cachable_relationships_to_serialize, fieldset, includes_list, params) if cachable_relationships_to_serialize.present? temp_hash[:relationships] = relationships_hash(record, cachable_relationships_to_serialize, fieldset, includes_list, params) if cachable_relationships_to_serialize.present?
temp_hash[:links] = links_hash(record, params) if data_links.present? temp_hash[:links] = links_hash(record, params) if data_links.present?
temp_hash temp_hash
end end
record_hash[:relationships] = record_hash[:relationships].merge(relationships_hash(record, uncachable_relationships_to_serialize, fieldset, includes_list, params)) if uncachable_relationships_to_serialize.present? record_hash[:relationships] = (record_hash[:relationships] || {}).merge(relationships_hash(record, uncachable_relationships_to_serialize, fieldset, includes_list, params)) if uncachable_relationships_to_serialize.present?
else else
record_hash = id_hash(id_from_record(record, params), record_type, true) record_hash = id_hash(id_from_record(record, params), record_type, true)
record_hash[:attributes] = attributes_hash(record, fieldset, params) if attributes_to_serialize.present? record_hash[:attributes] = attributes_hash(record, fieldset, params) if attributes_to_serialize.present?

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
module JSONAPI
module Serializer
class Error < StandardError; end
class UnsupportedIncludeError < Error
attr_reader :include_item, :klass
def initialize(include_item, klass)
super()
@include_item = include_item
@klass = klass
end
def message
"#{include_item} is not specified as a relationship on #{klass}"
end
end
end
end

View File

@ -1,3 +1,4 @@
require 'active_support'
require 'active_support/notifications' require 'active_support/notifications'
module JSONAPI module JSONAPI

View File

@ -1,5 +1,5 @@
module JSONAPI module JSONAPI
module Serializer module Serializer
VERSION = '2.1.0'.freeze VERSION = '2.2.0'.freeze
end end
end end

View File

@ -1,3 +1,6 @@
require 'active_support'
require 'active_support/cache'
class User class User
attr_accessor :uid, :first_name, :last_name, :email attr_accessor :uid, :first_name, :last_name, :email
@ -26,3 +29,12 @@ class UserSerializer
} }
end end
end end
module Cached
class UserSerializer < ::UserSerializer
cache_options(
store: ActiveSupport::Cache::MemoryStore.new,
namespace: 'test'
)
end
end

View File

@ -1,3 +1,4 @@
require 'active_support'
require 'active_support/cache' require 'active_support/cache'
require 'jsonapi/serializer/instrumentation' require 'jsonapi/serializer/instrumentation'

View File

@ -26,7 +26,7 @@ class Movie
@url ||= FFaker::Internet.http_url @url ||= FFaker::Internet.http_url
return @url if obj.nil? return @url if obj.nil?
@url + '?' + obj.hash.to_s "#{@url}?#{obj.hash}"
end end
def owner=(ownr) def owner=(ownr)
@ -48,8 +48,9 @@ class MovieSerializer
set_type :movie set_type :movie
attribute :released_in_year, &:year
attributes :name attributes :name
attribute :release_year do |object| attribute :release_year do |object, _params|
object.year object.year
end end

View File

@ -25,6 +25,16 @@ RSpec.describe JSONAPI::Serializer do
cache_store.delete(actor.movies[0].owner, namespace: 'test') cache_store.delete(actor.movies[0].owner, namespace: 'test')
).to be(false) ).to be(false)
end end
context 'without relationships' do
let(:user) { User.fake }
let(:serialized) { Cached::UserSerializer.new(user).serializable_hash.as_json }
it do
expect(serialized['data']).not_to have_key('relationships')
end
end
end end
describe 'with caching and different fieldsets' do describe 'with caching and different fieldsets' do

View File

@ -18,7 +18,7 @@ RSpec.describe JSONAPI::Serializer do
it do it do
expect { ActorSerializer.new(actor, include: ['bad_include']) } expect { ActorSerializer.new(actor, include: ['bad_include']) }
.to raise_error( .to raise_error(
ArgumentError, /bad_include is not specified as a relationship/ JSONAPI::Serializer::UnsupportedIncludeError, /bad_include is not specified as a relationship/
) )
end end
end end

View File

@ -10,10 +10,8 @@ RSpec.describe JSONAPI::Serializer do
it do it do
payload = event_name = nil payload = event_name = nil
notification_name = ( notification_name =
::JSONAPI::Serializer::Instrumentation::NOTIFICATION_NAMESPACE + "#{::JSONAPI::Serializer::Instrumentation::NOTIFICATION_NAMESPACE}serializable_hash"
'serializable_hash'
)
ActiveSupport::Notifications.subscribe( ActiveSupport::Notifications.subscribe(
notification_name notification_name

View File

@ -6,6 +6,7 @@ SimpleCov.start do
end end
SimpleCov.minimum_coverage 90 SimpleCov.minimum_coverage 90
require 'active_support'
require 'active_support/core_ext/object/json' require 'active_support/core_ext/object/json'
require 'jsonapi/serializer' require 'jsonapi/serializer'
require 'ffaker' require 'ffaker'