railsful 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 77c58909e21f08d21955de91651f80db0b6e149351e2819e0980c8215f938fbb
4
+ data.tar.gz: ad03136f8c07f8c9d8b7db6000f1aec732c91b2c3f450ec6446ed882b0f00884
5
+ SHA512:
6
+ metadata.gz: 1a8aab06a687afe8073b03541aa514e8e709983ea96b0f0e0802b08f93cabe4f3b71e70a465252b0b50e61288c1a300edda2433b9c2a7a2abae92d126a023696
7
+ data.tar.gz: 99ac9e457415aee433f83f20e48e483dc8176f2b8a73a6fabcd52ce8d1232e069a65deb8b30a89f2fb3d12526409d5d3a32d036a3e84092cfa45cb84780bc90f
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ Gemfile.lock
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.reek.yml ADDED
@@ -0,0 +1,2 @@
1
+ exclude_paths:
2
+ - spec/
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,18 @@
1
+ sudo: false
2
+ env:
3
+ global:
4
+ - CC_TEST_REPORTER_ID=2002e6cb033fc09cb3a764c5d1622f756e0c2d2b61f0a62e51b8e38070cdff00
5
+ language: ruby
6
+ rvm:
7
+ - 2.6
8
+ - 2.5
9
+ - 2.4
10
+ - 2.3
11
+ before_script:
12
+ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
13
+ - chmod +x ./cc-test-reporter
14
+ - ./cc-test-reporter before-build
15
+ script:
16
+ - bundle exec rspec
17
+ after_script:
18
+ - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in railsful.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Henning Vogt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,116 @@
1
+ # Railsful
2
+ [![Maintainability](https://api.codeclimate.com/v1/badges/d1e81476a4c63779815e/maintainability)](https://codeclimate.com/github/hausgold/railsful/maintainability)
3
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/d1e81476a4c63779815e/test_coverage)](https://codeclimate.com/github/hausgold/railsful/test_coverage)
4
+
5
+ A small but helpful collection of concerns and tools to create
6
+ a restful JSON API compliant Rails application.
7
+
8
+ ## Installation
9
+
10
+ Add these lines to your application's Gemfile:
11
+
12
+ ```ruby
13
+ # fast_jsonapi is used to serialize objects.
14
+ gem 'fast_jsonapi'
15
+ # kaminari needed for pagination.
16
+ gem 'kaminari'
17
+
18
+ gem 'railsful'
19
+ ```
20
+
21
+ And then execute:
22
+
23
+ $ bundle
24
+
25
+ Or install it yourself as:
26
+
27
+ $ gem install railsful
28
+
29
+ ## Usage
30
+
31
+ ### Serialization
32
+
33
+ In order to serialize your objects it is necessary that their serializer follow
34
+ the same naming convention and modules as the object to be serialized.
35
+
36
+ ``` ruby
37
+ # app/models/some_module/user.rb
38
+ module SomeModule
39
+ class User
40
+ ...
41
+ end
42
+ end
43
+
44
+ # app/serializers/some_module/user_serializer.rb
45
+ module SomeModule
46
+ class UserSerializer
47
+ ...
48
+ end
49
+ end
50
+ ```
51
+
52
+ After that you can use `render json: ...` without specifying the serializer:
53
+
54
+ ``` ruby
55
+ module SomeModule
56
+ class UserController
57
+ def index
58
+ render json: User.all
59
+ end
60
+ end
61
+ end
62
+ ```
63
+
64
+ Will result in:
65
+
66
+ ``` json
67
+ GET /some_module/users
68
+ {
69
+ "data": [
70
+ { "type": "user", "id": 1, "attributes": { ... } },
71
+ { "type": "user", "id": 2, "attributes": { ... } }
72
+ ]
73
+ }
74
+ ```
75
+
76
+ ### Deserialization
77
+ For deserialization of jsonapi compliant request all controllers that
78
+ inherit from `ActionController` can use the `#deserialized_params` method.
79
+
80
+ ``` ruby
81
+ class UsersController < ApplicationController
82
+ def create
83
+ user = User.new(user_params)
84
+
85
+ # Return success/fail ...
86
+ end
87
+
88
+ private
89
+
90
+ def user_params
91
+ deserialized_params.permit(:first_name, :last_name, ...)
92
+ end
93
+ end
94
+ ```
95
+
96
+ ## Development
97
+
98
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
99
+ `rake spec` to run the tests. You can also run `bin/console` for an interactive
100
+ prompt that will allow you to experiment.
101
+
102
+ To install this gem onto your local machine, run `bundle exec rake install`. To
103
+ release a new version, update the version number in `version.rb`, and then run
104
+ `bundle exec rake release`, which will create a git tag for the version, push
105
+ git commits and tags, and push the `.gem` file to
106
+ [rubygems.org](https://rubygems.org).
107
+
108
+ ## Contributing
109
+
110
+ Bug reports and pull requests are welcome on GitHub at
111
+ https://github.com/hausgold/railsful.
112
+
113
+ ## License
114
+
115
+ The gem is available as open source under the terms of the [MIT
116
+ License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "railsful"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Railsful
4
+ module Deserializable
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ def deserialized_params
9
+ Deserializer.new(params.to_unsafe_hash).deserialize
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'deep_merge/rails_compat'
4
+
5
+ module Railsful
6
+ class Deserializer
7
+ attr_reader :params
8
+
9
+ def initialize(params)
10
+ @params = params
11
+ end
12
+
13
+ # Deserializes the given params.
14
+ #
15
+ # :reek:FeatureEnvy
16
+ def deserialize
17
+ deserialized = {}
18
+
19
+ data = params.fetch(:data, {})
20
+
21
+ # Merge the resources attributes
22
+ deserialized.merge!(data.fetch(:attributes, {}))
23
+
24
+ # Get the already existing relationships
25
+ data.fetch(:relationships, {}).each do |type, payload|
26
+ deserialized.merge!(relationship(type, payload))
27
+ end
28
+
29
+ # Get the included elements.
30
+ deserialized.deeper_merge!(included_hash(params))
31
+
32
+ # Return the deserialized params.
33
+ ActionController::Parameters.new(deserialized)
34
+ end
35
+
36
+ # Fetches all included associations/relationships from the
37
+ # included hash.
38
+ def included_hash(params)
39
+ included_hash = {}
40
+
41
+ params.fetch(:included, []).each do |inc|
42
+ type = inc[:type].to_sym
43
+ attrs = inc[:attributes]
44
+
45
+ if params.dig(:data, :relationships, type, :data).is_a?(Array)
46
+ # TODO: Pluralize the key here.
47
+ included_hash["#{type}_attributes"] ||= []
48
+ included_hash["#{type}_attributes"] << attrs
49
+ else
50
+ included_hash["#{type}_attributes"] = attrs
51
+ end
52
+ end
53
+
54
+ included_hash
55
+ end
56
+
57
+ def relationship(type, payload)
58
+ data = payload[:data]
59
+
60
+ return has_many_relationship(type, data) if data.is_a?(Array)
61
+
62
+ belongs_to_relationship(type, data)
63
+ end
64
+
65
+ # rubocop:disable Naming/PredicateName
66
+ def has_many_relationship(type, data)
67
+ return {} unless data.is_a?(Array)
68
+
69
+ ids = data.map { |relation| relation[:id] }.compact
70
+
71
+ return {} if ids.empty?
72
+
73
+ { :"#{type}_ids" => ids }
74
+ end
75
+ # rubocop:enable Naming/PredicateName
76
+
77
+ def belongs_to_relationship(type, data)
78
+ # Fetch a possible id from the data.
79
+ relation_id = data[:id]
80
+
81
+ # If no ID is provided skip it.
82
+ return {} unless relation_id
83
+
84
+ # Build the relationship hash.
85
+ { :"#{type}_id" => relation_id }
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Railsful
4
+ # The base error for this gem.
5
+ class Error < StandardError
6
+ attr_reader :detail, :status
7
+
8
+ # Initializer.
9
+ #
10
+ # @param detail [String] The detailed message.
11
+ # @param status [Integer] The status code.
12
+ def initialize(detail = nil, status = 400)
13
+ @detail = detail
14
+ @status = status
15
+ end
16
+
17
+ # Format the error as jsonapi wants it to.
18
+ #
19
+ # @return [Hash]
20
+ def as_json(_options = nil)
21
+ {
22
+ errors: [
23
+ {
24
+ status: status,
25
+ title: self.class.name.demodulize.underscore,
26
+ detail: detail
27
+ }
28
+ ]
29
+ }
30
+ end
31
+ end
32
+
33
+ # The error that is raised when pagination fails.
34
+ class PaginationError < Error; end
35
+ # The error that is raised when sorting fails.
36
+ class SortingError < Error; end
37
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_model/errors'
4
+
5
+ module Railsful
6
+ module Interceptors
7
+ # This interceptor checks the given json object for an 'errors' array
8
+ # and checks if any errors are available.
9
+ module Errors
10
+ def render(options)
11
+ super(errors_options(options))
12
+ end
13
+
14
+ def errors_options(options)
15
+ return options unless errors?(options)
16
+
17
+ # Fetch all the errors from the passed json value.
18
+ errors = errors(options.fetch(:json))
19
+
20
+ # Overwrite the json value and set the errors array.
21
+ options.merge(json: { errors: errors })
22
+ end
23
+
24
+ # Transform error output format into more "jsonapi" like.
25
+ def errors(raw_errors)
26
+ errors = []
27
+
28
+ raw_errors.details.each do |field, array|
29
+ errors += field_errors(field, array)
30
+ end
31
+
32
+ errors
33
+ end
34
+
35
+ def field_errors(field, array)
36
+ array.map do |hash|
37
+ formatted_error(hash, field)
38
+ end
39
+ end
40
+
41
+ # Format the error by adding additional status and field information.
42
+ #
43
+ # :reek:UtilityFunction
44
+ def formatted_error(hash, field)
45
+ {
46
+ status: '422',
47
+ field: field
48
+ }.merge(hash)
49
+ end
50
+
51
+ # Checks if given renderable is an ActiveModel::Error
52
+ #
53
+ # :reek:UtilityFunction
54
+ def errors?(options)
55
+ return false unless options
56
+
57
+ options.fetch(:json, nil).is_a?(ActiveModel::Errors)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Railsful
4
+ module Interceptors
5
+ # This interceptors implements the "include" functionality for
6
+ # a given record or a relation.
7
+ module Include
8
+ def render(options)
9
+ super(include_options(options))
10
+ end
11
+
12
+ def include_options(options)
13
+ # Check if include key should be merged into options hash.
14
+ return options unless should_include?
15
+
16
+ # Deep merge include options, so we do not override existing
17
+ # include options.
18
+ options.deeper_merge(include: includes)
19
+ end
20
+
21
+ # Check if options should contain includes.
22
+ #
23
+ # @return [Boolean] The answer.
24
+ def should_include?
25
+ # Only GET requests should have the "include" functionality,
26
+ # since it may be a parameter in a create or update action.
27
+ method == 'GET' && includes.any?
28
+ end
29
+
30
+ # Fetch the list of all includes.
31
+ #
32
+ # @return [Array] The list of all include options.
33
+ def includes
34
+ params.fetch(:include, nil).to_s.split(',')
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Railsful
4
+ module Interceptors
5
+ # Interceptor that paginates a given ActiveRecord::Relation
6
+ # with help of the Kaminari gem.
7
+ module Pagination
8
+ def render(options)
9
+ super(pagination_options(options))
10
+ end
11
+
12
+ def pagination_options(options)
13
+ # Check if json value should be paginated.
14
+ return options unless paginate?(options)
15
+
16
+ # Get the relation from options hash so we can paginate it and
17
+ # check the total count.
18
+ relation = options.fetch(:json)
19
+
20
+ # Paginate the relation and store new relation in temporary variable.
21
+ paginated = paginate(relation)
22
+
23
+ # Create a meta hash
24
+ meta = {
25
+ total_pages: paginated.try(:total_pages),
26
+ total_count: paginated.try(:total_count),
27
+ current_page: paginated.try(:current_page),
28
+ next_page: paginated.try(:next_page),
29
+ prev_page: paginated.try(:prev_page)
30
+ }
31
+
32
+ options.deeper_merge(
33
+ links: links(paginated), meta: meta
34
+ ).merge(json: paginated)
35
+ end
36
+
37
+ private
38
+
39
+ # Check if given entity is paginatable and request allows pagination.
40
+ #
41
+ # @param options [Hash] The global render options.
42
+ # @return [Boolean] The answer.
43
+ def paginate?(options)
44
+ method == 'GET' &&
45
+ params.fetch(:page, false) &&
46
+ relation?(options)
47
+ end
48
+
49
+ # Paginate given relation
50
+ #
51
+ # @param relation [ActiveRecord::Relation] The relation.
52
+ # @return [ActiveRecord::Relation] The paginated relation.
53
+ def paginate(relation)
54
+ # If page param is not a hash, raise an error.
55
+ unless params.to_unsafe_hash.fetch(:page, nil).is_a?(Hash)
56
+ raise PaginationError,
57
+ 'Wrong pagination format. Hash expected.'
58
+ end
59
+
60
+ # Get the per page size.
61
+ per_page = params.dig(:page, :size)
62
+
63
+ relation = relation.page(params.dig(:page, :number))
64
+ relation = relation.per(per_page) if per_page
65
+
66
+ relation
67
+ end
68
+
69
+ # Create the pagination links
70
+ #
71
+ # @param relation [ActiveRecord::Relation] The relation to be paginated.
72
+ # @return [Hash] The +links+ hash.
73
+ def links(relation)
74
+ per_page = params.dig(:page, :size)
75
+
76
+ {
77
+ self: collection_url(relation.try(:current_page), per_page),
78
+ next: collection_url(relation.try(:next_page), per_page),
79
+ prev: collection_url(relation.try(:prev_page), per_page)
80
+ }
81
+ end
82
+
83
+ # The assembled pagination url.
84
+ #
85
+ # @param per_page [Integer] The per page count.
86
+ # @param page [Integer] The page.
87
+ # @return [String] The URL.
88
+ def collection_url(page, per_page)
89
+ return nil unless page
90
+
91
+ # Set fallback pagination.
92
+ # TODO: Get it from the model.
93
+ per_page ||= 25
94
+
95
+ # The +#url_for+ method comes from the given controller.
96
+ controller.url_for \
97
+ controller: params[:controller],
98
+ action: params[:action],
99
+ params: {
100
+ page: { number: page, size: per_page }
101
+ }
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Railsful
4
+ module Interceptors
5
+ # Interceptor that sorts a given ActiveRecord::Relation
6
+ module Sorting
7
+ def render(options)
8
+ super(sorting_options(options))
9
+ end
10
+
11
+ def sorting_options(options)
12
+ # Check if json value should be sorted.
13
+ return options unless sort?(options)
14
+
15
+ # Get the relation from options hash so we can sort it
16
+ relation = options.fetch(:json)
17
+
18
+ options.merge(json: sort(relation))
19
+ end
20
+
21
+ private
22
+
23
+ # Check if given entity is sortable and request allows sorting.
24
+ #
25
+ # @param options [Hash] The global render options.
26
+ # @return [Boolean] The answer.
27
+ def sort?(options)
28
+ method == 'GET' && params.fetch(:sort, false) && relation?(options)
29
+ end
30
+
31
+ # Format a sort string to a database friendly order string
32
+ #
33
+ # @return [String] database order query e.g. 'name DESC'
34
+ #
35
+ # :reek:UtilityFunction
36
+ def order(string)
37
+ string.start_with?('-') ? "#{string[1..-1]} DESC" : "#{string} ASC"
38
+ end
39
+
40
+ # Map the sort params to a database friendly set of strings
41
+ #
42
+ # @return [Array] Array of string e.g. ['name DESC', 'age ASC']
43
+ def orders
44
+ params.fetch(:sort).split(',').map do |string|
45
+ next unless string =~ /\A-?\w+\z/ # allow only word chars
46
+
47
+ order(string)
48
+ end.compact
49
+ end
50
+
51
+ # Sort given relation
52
+ #
53
+ # @param relation [ActiveRecord::Relation] The relation.
54
+ # @return [ActiveRecord::Relation] The sorted relation.
55
+ def sort(relation)
56
+ order_string = orders.join(', ')
57
+ # support both #reorder and #order call on relation
58
+ return relation.reorder(order_string) if relation.respond_to?(:reorder)
59
+ return relation.order(order_string) if relation.respond_to?(:order)
60
+
61
+ raise SortingError, 'Relation does not respond to #reorder or #order.'
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Railsful
4
+ class Railtie < Rails::Railtie
5
+ initializer 'railsful.action_controller' do
6
+ ActiveSupport.on_load(:action_controller) do
7
+ prepend Railsful::Serializable
8
+ include Railsful::Deserializable
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Railsful
4
+ module Serializable
5
+ def render(options = nil, extra_options = {}, &block)
6
+ super(fast_jsonapi_options(options), extra_options, &block)
7
+ end
8
+
9
+ def fast_jsonapi_options(options)
10
+ Serializer.new(self).render(options)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'railsful/interceptors/errors'
4
+ require 'railsful/interceptors/include'
5
+ require 'railsful/interceptors/pagination'
6
+ require 'railsful/interceptors/sorting'
7
+
8
+ module Railsful
9
+ # This class allows to encapsulate the interceptor logic from the
10
+ # prepended controller, so the controller is not polluted with
11
+ # all needed (helper) methods.
12
+ class Serializer
13
+ # All interceptors that provide jsonapi logic.
14
+ prepend Interceptors::Include
15
+ prepend Interceptors::Pagination
16
+ prepend Interceptors::Sorting
17
+ prepend Interceptors::Errors
18
+
19
+ attr_reader :controller
20
+
21
+ # Keep a reference to the controller, so all helper methods
22
+ # like +url_for+ can be used.
23
+ def initialize(controller)
24
+ @controller = controller
25
+ end
26
+
27
+ # The render function every interceptor MUST implement in order to
28
+ # add certain functionality.
29
+ #
30
+ # @param options [Hash] The render options hash.
31
+ # @return [Hash] The (modified) render options hash.
32
+ #
33
+ # :reek:FeatureEnvy
34
+ def render(options)
35
+ # Get the renderable (Object that should be rendered) from options hash.
36
+ renderable = options[:json]
37
+
38
+ # Return if renderable is blank
39
+ return options unless renderable
40
+
41
+ # Try to fetch the right serializer for given renderable.
42
+ serializer = serializer_for(renderable)
43
+
44
+ # When no serializer is found just pass the original options hash.
45
+ return options unless serializer
46
+
47
+ # Replace json value with new serializer
48
+ options.merge(json: serializer.new(renderable, options))
49
+ end
50
+
51
+ # Find the right serializer for given object.
52
+ #
53
+ # @param [ApplicationRecord, ActiveRecord::Relation]
54
+ # @return [Class] The serializer class.
55
+ #
56
+ # :reek:UtilityFunction
57
+ def serializer_for(renderable)
58
+ # Get the class in order to find the right serializer.
59
+ klass = if renderable.is_a?(ActiveRecord::Relation)
60
+ renderable.model.name
61
+ else
62
+ renderable.class.name
63
+ end
64
+
65
+ "#{klass}Serializer".safe_constantize
66
+ end
67
+
68
+ # Fetch the params from controller.
69
+ #
70
+ # @return [] The params.
71
+ def params
72
+ controller.params
73
+ end
74
+
75
+ # Fetch the HTTP method from controllers request.
76
+ #
77
+ # @return [String] The method.
78
+ def method
79
+ controller.request.request_method
80
+ end
81
+
82
+ # Check if given options contain an ActiveRecord::Relation.
83
+ #
84
+ # @param options [Hash] The options.
85
+ # @return [Boolean] The answer.
86
+ #
87
+ # :reek:UtilityFunction
88
+ def relation?(options)
89
+ options[:json].is_a?(ActiveRecord::Relation)
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Railsful
4
+ VERSION = '0.1.0'.freeze
5
+ end
data/lib/railsful.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'active_support'
2
+
3
+ require 'railsful/version'
4
+ require 'railsful/exceptions'
5
+ require 'railsful/railtie' if defined?(Rails)
6
+ require 'railsful/serializable'
7
+ require 'railsful/serializer'
8
+ require 'railsful/deserializable'
9
+ require 'railsful/deserializer'
10
+
11
+ # A small but helpful collection of concerns and tools to create
12
+ # a restful JSON API compliant Rails application.
13
+ module Railsful
14
+ # Nothing to do here
15
+ end
data/railsful.gemspec ADDED
@@ -0,0 +1,42 @@
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'railsful/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'railsful'
7
+ spec.version = Railsful::VERSION
8
+ spec.authors = ['Henning Vogt', 'Marcus Geissler']
9
+ spec.email = ['henning.vogt@hausgold.de',
10
+ 'marcus.geissler@hausgold.de']
11
+
12
+ spec.summary = 'JSON API serializer and deserializer for Rails'
13
+ spec.description = 'This gem provides useful helper functions to interact' \
14
+ 'with JSON API.'
15
+ spec.homepage = 'https://github.com/hausgold/restful'
16
+ spec.license = 'MIT'
17
+
18
+ if spec.respond_to?(:metadata)
19
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
20
+ else
21
+ raise 'RubyGems 2.0 or newer is required to protect against public pushes.'
22
+ end
23
+
24
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
25
+ f.match(%r{^(test|spec|features)/})
26
+ end
27
+
28
+ spec.bindir = 'exe'
29
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ['lib']
31
+
32
+ spec.required_ruby_version = '>= 2.3'
33
+
34
+ spec.add_dependency 'deep_merge', '~> 1'
35
+ spec.add_dependency 'rails', ['>=4', '< 6']
36
+
37
+ spec.add_development_dependency 'bundler', '>= 1.16', '< 3'
38
+ spec.add_development_dependency 'pry'
39
+ spec.add_development_dependency 'rake', '~> 10.0'
40
+ spec.add_development_dependency 'rspec-rails', '~> 3.0'
41
+ spec.add_development_dependency 'simplecov', '~> 0.15'
42
+ end
metadata ADDED
@@ -0,0 +1,180 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: railsful
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Henning Vogt
8
+ - Marcus Geissler
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2019-01-14 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: deep_merge
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rails
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '4'
35
+ - - "<"
36
+ - !ruby/object:Gem::Version
37
+ version: '6'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: '4'
45
+ - - "<"
46
+ - !ruby/object:Gem::Version
47
+ version: '6'
48
+ - !ruby/object:Gem::Dependency
49
+ name: bundler
50
+ requirement: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '1.16'
55
+ - - "<"
56
+ - !ruby/object:Gem::Version
57
+ version: '3'
58
+ type: :development
59
+ prerelease: false
60
+ version_requirements: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: '1.16'
65
+ - - "<"
66
+ - !ruby/object:Gem::Version
67
+ version: '3'
68
+ - !ruby/object:Gem::Dependency
69
+ name: pry
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: rake
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '10.0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '10.0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: rspec-rails
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '3.0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '3.0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: simplecov
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '0.15'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '0.15'
124
+ description: This gem provides useful helper functions to interactwith JSON API.
125
+ email:
126
+ - henning.vogt@hausgold.de
127
+ - marcus.geissler@hausgold.de
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - ".gitignore"
133
+ - ".reek.yml"
134
+ - ".rspec"
135
+ - ".travis.yml"
136
+ - Gemfile
137
+ - LICENSE.txt
138
+ - README.md
139
+ - Rakefile
140
+ - bin/console
141
+ - bin/setup
142
+ - lib/railsful.rb
143
+ - lib/railsful/deserializable.rb
144
+ - lib/railsful/deserializer.rb
145
+ - lib/railsful/exceptions.rb
146
+ - lib/railsful/interceptors/errors.rb
147
+ - lib/railsful/interceptors/include.rb
148
+ - lib/railsful/interceptors/pagination.rb
149
+ - lib/railsful/interceptors/sorting.rb
150
+ - lib/railsful/railtie.rb
151
+ - lib/railsful/serializable.rb
152
+ - lib/railsful/serializer.rb
153
+ - lib/railsful/version.rb
154
+ - railsful.gemspec
155
+ homepage: https://github.com/hausgold/restful
156
+ licenses:
157
+ - MIT
158
+ metadata:
159
+ allowed_push_host: https://rubygems.org
160
+ post_install_message:
161
+ rdoc_options: []
162
+ require_paths:
163
+ - lib
164
+ required_ruby_version: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - ">="
167
+ - !ruby/object:Gem::Version
168
+ version: '2.3'
169
+ required_rubygems_version: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ requirements: []
175
+ rubyforge_project:
176
+ rubygems_version: 2.7.6
177
+ signing_key:
178
+ specification_version: 4
179
+ summary: JSON API serializer and deserializer for Rails
180
+ test_files: []