diff --git a/.travis.yml b/.travis.yml index 8bf1951..be72d86 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,14 @@ language: ruby -sudo: false env: - - 'RAILS_VERSION=5.1.2' + matrix: + - "RAILS_VERSION=5.2" + - "RAILS_VERSION=6.0" rvm: - - 2.3.3 -matrix: -before_install: gem install bundler -v 1.13.6 + - 2.4 + - 2.5 + - 2.6 + - 2.7 script: bundle exec rspec spec +before_install: + - gem install bundler diff --git a/README.md b/README.md index bc2a712..47494be 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ end Support: * Ruby 1.9+ with Rails 4 -* Ruby 2.3+ with Rails 5 +* Ruby 2.4+ with Rails 5 For Rails 4 add this to your application's Gemfile: @@ -62,10 +62,10 @@ For Rails 4 add this to your application's Gemfile: gem 'jsonapi-utils', '~> 0.4.9' ``` -For Rails 5: +For Rails 5+: ```ruby -gem 'jsonapi-utils', '~> 0.7.2' +gem 'jsonapi-utils', '~> 0.7.3' ``` And then execute: @@ -225,12 +225,12 @@ class CustomPaginator < JSONAPI::Paginator end ``` -And then it can be either set at the resource class level (e.g. UserResource.paginator :custom_paginator) or via config initializer: +And then it can be either set at the resource class level (e.g. UserResource.paginator :custom) or via config initializer: ```ruby # config/initializers/jsonapi_resources.rb JSONAPI.configure do |config| - config.default_paginator = :custom_paginator + config.default_paginator = :custom end ``` diff --git a/jsonapi-utils.gemspec b/jsonapi-utils.gemspec index a89bc18..a26093d 100644 --- a/jsonapi-utils.gemspec +++ b/jsonapi-utils.gemspec @@ -19,15 +19,15 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ['lib'] - spec.add_runtime_dependency 'jsonapi-resources', '0.9.8' + spec.add_runtime_dependency 'jsonapi-resources', '0.9.11' - spec.add_development_dependency 'bundler', '~> 1.10' - spec.add_development_dependency 'rake', '~> 10.0' - spec.add_development_dependency 'rails', ENV['RAILS_VERSION'] || '~> 5.1' - spec.add_development_dependency 'sqlite3', '~> 1.3.6' - spec.add_development_dependency 'rspec-rails', '~> 3.1' - spec.add_development_dependency 'factory_girl', '~> 4.8' + spec.add_development_dependency 'bundler', '~> 1.14', '>= 1.14' + spec.add_development_dependency 'rake', '~> 12.3', '>= 12.3.3' + spec.add_development_dependency 'rails', (ENV['RAILS_VERSION'] || '~> 5.2'), '>= 5.2.4' + spec.add_development_dependency 'sqlite3', '~> 1.4' + spec.add_development_dependency 'rspec-rails', '~> 3.9', '>= 3.9.0' + spec.add_development_dependency 'factory_bot', '~> 5.1' spec.add_development_dependency 'smart_rspec', '~> 0.1.6' - spec.add_development_dependency 'pry', '~> 0.10.3' - spec.add_development_dependency 'pry-byebug' + spec.add_development_dependency 'pry', '~> 0.12', '>= 0.12.2' + spec.add_development_dependency 'pry-byebug', '~> 3.7', '>= 3.7.0' end diff --git a/lib/jsonapi/utils/response/formatters.rb b/lib/jsonapi/utils/response/formatters.rb index 055b026..c821c36 100644 --- a/lib/jsonapi/utils/response/formatters.rb +++ b/lib/jsonapi/utils/response/formatters.rb @@ -15,7 +15,7 @@ module Formatters # @option options [JSONAPI::Resource] resource: it tells the formatter which resource # class to be used rather than use an infered one (default behaviour) # - # @option options [JSONAPI::Resource] source_resource: it tells the formatter that this response is from a related resource + # @option options [JSONAPI::Resource] source: it tells the formatter that this response is from a related resource # and the result should be interpreted as a related resources response # # @option options [String, Symbol] relationship_type: it tells that the formatter which relationship the data is from @@ -32,7 +32,7 @@ module Formatters # @api public def jsonapi_format(object, options = {}) if object.is_a?(Hash) - hash = object.with_indifferent_access + hash = object.with_indifferent_access object = hash_to_active_record(hash[:data], options[:model]) end fix_custom_request_options(object) @@ -120,8 +120,8 @@ def build_response_document(object, options) # @option options [JSONAPI::Resource] :resource which resource class to be used # rather than using the default one (inferred) # - # @option options [ActiveRecord::Base, JSONAPI::Resource] :source source of related resource, - # the result should be interpreted as a related resources response + # @option options [ActiveRecord::Base, JSONAPI::Resource] :source parent model/resource + # of the related resource # # @option options [String, Symbol] :relationship which relationship the data is from # @@ -133,13 +133,15 @@ def build_response_document(object, options) # # @api private def build_collection_result(object, options) - records = build_collection(object, options) + records = build_collection(object, options) result_options = result_options(object, options) - if options[:source].present? && related_resource_operation? - source_resource = turn_source_into_resource(options[:source]) + if related_resource_operation?(options) + source_resource = turn_source_into_resource(options[:source]) relationship_type = get_source_relationship(options) - JSONAPI::RelatedResourcesOperationResult.new(:ok, + + JSONAPI::RelatedResourcesOperationResult.new( + :ok, source_resource, relationship_type, records, @@ -152,11 +154,24 @@ def build_collection_result(object, options) # Is this a request for related resources? # + # In order to answer that it needs to check for some {options} + # controller params like {params[:source]} and {params[:relationship]}. + # + # @option options [Boolean] :related when true, jsonapi-utils infers the parent and + # related resources from controller's {params} values. + # + # @option options [ActiveRecord::Base, JSONAPI::Resource] :source parent model/resource + # of the related resource + # + # @option options [String, Symbol] :relationship which relationship the data is from + # # @return [Boolean] # # @api private - def related_resource_operation? - params[:source].present? && params[:relationship].present? + def related_resource_operation?(options) + (options[:related] || options[:source].present?) && + params[:source].present? && + params[:relationship].present? end # Apply a proper action setup for custom requests/actions. @@ -263,8 +278,9 @@ def get_source_relationship(options) # @api private def result_options(records, options) {}.tap do |data| - if JSONAPI.configuration.default_paginator != :none && - JSONAPI.configuration.top_level_links_include_pagination + data[:meta] = options.fetch(:meta, {}) + + if include_pagination_links? data[:pagination_params] = pagination_params(records, options) end @@ -272,7 +288,7 @@ def result_options(records, options) data[:record_count] = record_count_for(records, options) end - if JSONAPI.configuration.top_level_meta_include_page_count + if include_page_count? data[:page_count] = page_count_for(data[:record_count]) end end diff --git a/lib/jsonapi/utils/support/pagination.rb b/lib/jsonapi/utils/support/pagination.rb index c8f1eaf..5687fe1 100644 --- a/lib/jsonapi/utils/support/pagination.rb +++ b/lib/jsonapi/utils/support/pagination.rb @@ -4,20 +4,40 @@ module Support module Pagination RecordCountError = Class.new(ArgumentError) + # Check whether pagination links should be included. + # + # @api public + # @return [Boolean] + def include_pagination_links? + JSONAPI.configuration.default_paginator != :none && + JSONAPI.configuration.top_level_links_include_pagination + end + + # Check whether pagination's page count should be included + # on the "meta" key. + # + # @api public + # @return [Boolean] + def include_page_count? + JSONAPI.configuration.top_level_meta_include_page_count + end + # Apply proper pagination to the records. # # @param records [ActiveRecord::Relation, Array] collection of records # e.g.: User.all or [{ id: 1, name: 'Tiago' }, { id: 2, name: 'Doug' }] # - # @param options [Hash] JU's options + # @param options [Hash] JSONAPI::Utils' options # e.g.: { resource: V2::UserResource, count: 100 } # # @return [ActiveRecord::Relation, Array] # # @api public def apply_pagination(records, options = {}) - return records unless apply_pagination?(options) - records.is_a?(Array) ? records[paginate_with(:range)] : paginate_with(:paginator).apply(records, nil) + if !apply_pagination?(options) then records + elsif records.is_a?(Array) then records[paginate_with(:range)] + else paginate_with(:paginator).apply(records, nil) + end end # Mount pagination params for JSONAPI::ResourcesOperationResult. @@ -32,9 +52,10 @@ def apply_pagination(records, options = {}) # @return [Hash] # e.g.: {"first"=>{"number"=>1, "size"=>2}, "next"=>{"number"=>2, "size"=>2}, "last"=>{"number"=>2, "size"=>2}} # + # # @api public def pagination_params(records, options) - return {} unless JSONAPI.configuration.top_level_links_include_pagination + return {} unless include_pagination_links? paginator.links_page_params(record_count: record_count_for(records, options)) end diff --git a/lib/jsonapi/utils/version.rb b/lib/jsonapi/utils/version.rb index c8c1c81..1bddf34 100644 --- a/lib/jsonapi/utils/version.rb +++ b/lib/jsonapi/utils/version.rb @@ -1,5 +1,5 @@ module JSONAPI module Utils - VERSION = '0.7.2'.freeze + VERSION = '0.7.3'.freeze end end diff --git a/spec/controllers/posts_controller_spec.rb b/spec/controllers/posts_controller_spec.rb index b709d08..45b74f6 100644 --- a/spec/controllers/posts_controller_spec.rb +++ b/spec/controllers/posts_controller_spec.rb @@ -4,7 +4,7 @@ include_context 'JSON API headers' before(:all) do - @post = FactoryGirl.create_list(:post, 3).first + @post = FactoryBot.create_list(:post, 3).first end before(:each) do diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 56786ef..8556f94 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -4,7 +4,7 @@ include_context 'JSON API headers' before(:all) do - @user = FactoryGirl.create_list(:user, 3, :with_posts).first + @user = FactoryBot.create_list(:user, 3, :with_posts).first end before(:each) do @@ -28,7 +28,7 @@ expect(response).to have_http_status :ok expect(response).to have_primary_data('users') expect(response).to have_data_attributes(fields) - expect(response).to have_relationships(relationships) + expect(response).to have_relationships(relationships - ['profile']) end context 'with "include"' do diff --git a/spec/features/page_count_spec.rb b/spec/features/page_count_spec.rb index a85d761..9c3ec64 100644 --- a/spec/features/page_count_spec.rb +++ b/spec/features/page_count_spec.rb @@ -29,13 +29,12 @@ def TestApp.draw_page_count_test_routes # Feature Tests ## - describe PageCountTestController, type: :controller do include_context 'JSON API headers' before(:all) do TestApp.draw_page_count_test_routes - FactoryGirl.create_list(:user, 3, :with_posts) + FactoryBot.create_list(:user, 3, :with_posts) end describe 'page count with a paged paginator' do diff --git a/spec/features/record_count_spec.rb b/spec/features/record_count_spec.rb index b90a982..b8113bc 100644 --- a/spec/features/record_count_spec.rb +++ b/spec/features/record_count_spec.rb @@ -56,7 +56,7 @@ def TestApp.draw_record_count_test_routes before(:all) do TestApp.draw_record_count_test_routes - FactoryGirl.create_list(:user, 3, :with_posts) + FactoryBot.create_list(:user, 3, :with_posts) end describe 'explicit count' do diff --git a/spec/jsonapi/utils/support/pagination_spec.rb b/spec/jsonapi/utils/support/pagination_spec.rb index c1b2632..f032295 100644 --- a/spec/jsonapi/utils/support/pagination_spec.rb +++ b/spec/jsonapi/utils/support/pagination_spec.rb @@ -6,7 +6,7 @@ end before(:all) do - FactoryGirl.create_list(:user, 2) + FactoryBot.create_list(:user, 2) end let(:options) { {} } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d6c565f..17ef5b0 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,10 +1,10 @@ require 'smart_rspec' -require 'factory_girl' +require 'factory_bot' require 'support/helpers' RSpec.configure do |config| - config.include FactoryGirl::Syntax::Methods config.include Helpers::ResponseParser + config.include FactoryBot::Syntax::Methods config.define_derived_metadata do |meta| meta[:aggregate_failures] = true diff --git a/spec/support/controllers.rb b/spec/support/controllers.rb index d937320..96c57b7 100644 --- a/spec/support/controllers.rb +++ b/spec/support/controllers.rb @@ -14,7 +14,7 @@ def index jsonapi_render json: @user.posts, options: { count: 100 } end - # GET /users/:user_id//index_with_hash + # GET /users/:user_id/index_with_hash def index_with_hash @posts = { data: [ { id: 1, title: 'Lorem Ipsum', body: 'Body 4' }, diff --git a/spec/support/factories.rb b/spec/support/factories.rb index d99b29e..57da6b6 100644 --- a/spec/support/factories.rb +++ b/spec/support/factories.rb @@ -1,21 +1,9 @@ -require 'factory_girl' +require 'factory_bot' +require_relative './models' -FactoryGirl.define do - factory :category, class: Category do - sequence(:title) { |n| "Title for Category #{n}" } - end - - factory :post, class: Post do - association :author, factory: :user - category - - sequence(:id) { |n| n } - sequence(:title) { |n| "Title for Post #{n}" } - sequence(:body) { |n| "Body for Post #{n}" } - content_type :article - hidden_field 'It\'s a hidden field!' - end +# require 'byebug'; byebug +FactoryBot.define do factory :user, class: User do sequence(:id) { |n| n } sequence(:first_name) { |n| "User##{n}" } @@ -24,7 +12,7 @@ after(:create) { |user| create(:profile, user: user) } trait :with_posts do - transient { post_count 3 } + transient { post_count { 3 } } after(:create) do |user, e| create_list(:post, e.post_count, author: user) end @@ -37,4 +25,19 @@ sequence(:nickname) { |n| "Nickname##{n}" } sequence(:location) { |n| "Location##{n}" } end + + factory :post, class: Post do + association :author, factory: :user + category + + sequence(:id) { |n| n } + sequence(:title) { |n| "Title for Post #{n}" } + sequence(:body) { |n| "Body for Post #{n}" } + content_type { :article } + hidden_field { 'It\'s a hidden field!' } + end + + factory :category, class: Category do + sequence(:title) { |n| "Title for Category #{n}" } + end end