rapils 0.1.0.pre.alpha1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e6e7a6c654a9af37aaafbaf197e42d863184864481a581999651e44e8b96ce40
4
+ data.tar.gz: c944aa177efe1e7741d201b7a2ae5d445068cac8dbc11da14f000b3e18b4c100
5
+ SHA512:
6
+ metadata.gz: d8de9a618c19f8600c46ad2aecf7b4dca1924fe6998ecff4e0865001821e449fb6e26e1349327c725cf364021113ddbcdc3b6aa18424ecb086ca61bc9ea06a78
7
+ data.tar.gz: d73f40e4e350f28fca740e1af948b4cd2cf49993dc9da59c67e6f2bbe26573b725be477aa07fb2dcdb214a3aea33e545ef685f9dc456f0e7e433a2659c695ee5
@@ -0,0 +1,20 @@
1
+ Copyright 2020 Marc Qualie
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,39 @@
1
+ # Rapils
2
+
3
+ Opinionated out-of-the-box API + UI framework.
4
+
5
+
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'rapils'
13
+ ```
14
+
15
+ And then execute:
16
+ ```bash
17
+ $ bundle
18
+ ```
19
+
20
+ ### Models
21
+
22
+ In order to use the models, you need to extend the base models inside your application:
23
+
24
+ ```ruby
25
+ class User < Rapils::Models::User
26
+ end
27
+ ```
28
+
29
+
30
+
31
+ ## Contributing
32
+
33
+ Contribution directions go here.
34
+
35
+
36
+
37
+ ## License
38
+
39
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require 'bundler/setup'
2
+
3
+ APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
4
+ load 'rails/tasks/engine.rake'
5
+
6
+ require 'bundler/gem_tasks'
@@ -0,0 +1,17 @@
1
+ class CreateUsers < ActiveRecord::Migration[6.0]
2
+ def change
3
+ enable_extension 'pgcrypto'
4
+
5
+ create_table :users, id: :uuid do |t|
6
+ t.string :name
7
+ t.string :email, null: false
8
+ t.string :admin_roles, array: true, default: []
9
+ t.string :auth0_uid, null: false
10
+ t.datetime :access_granted_at
11
+ t.timestamps null: false
12
+
13
+ t.index %i[auth0_uid], name: 'index_users_on_auth0_uid', unique: true
14
+ t.index %i[email], name: 'index_users_on_email', unique: true
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ class CreateAccessTokens < ActiveRecord::Migration[6.0]
2
+ def change
3
+ create_table :access_tokens, id: :uuid do |t|
4
+ t.references :user, type: :uuid
5
+ # t.string :type, default: 'AccessToken', null: false
6
+ t.string :name, null: false
7
+ t.string :token, null: false
8
+ t.string :actions, default: [], null: false, array: true
9
+ t.datetime :last_used_at
10
+ t.datetime :expires_at
11
+ t.datetime :invalidated_at
12
+ t.timestamps null: false
13
+
14
+ t.index %i[token], name: 'index_access_tokens_on_token', unique: true
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ class CreatePermissions < ActiveRecord::Migration[6.0]
2
+ def change
3
+ create_table :permissions, id: :uuid do |t|
4
+ t.references :owner, type: :uuid, polymorphic: true
5
+ t.references :subject, type: :uuid, polymorphic: true
6
+ t.string :actions, array: true, null: false, default: []
7
+ t.boolean :recursive, default: false, null: false
8
+ t.datetime :expires_at
9
+ t.timestamps null: false
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,4 @@
1
+ require 'rapils/version'
2
+
3
+ module Rapils
4
+ end
@@ -0,0 +1,46 @@
1
+ require 'diffcrypt/rails/encrypted_configuration'
2
+
3
+ module Rapils
4
+ class Application < ::Rails::Application
5
+ config.load_defaults 6.0
6
+
7
+ # Configure generator defaults
8
+ config.generators do |g|
9
+ g.orm :active_record, primary_key_type: :uuid
10
+
11
+ # Disable things we never use
12
+ g.assets false
13
+ g.helper false
14
+ g.resource_route false
15
+ g.system_tests = nil
16
+ end
17
+
18
+ # Email
19
+ config.action_mailer.raise_delivery_errors = true
20
+ config.action_mailer.perform_deliveries = true
21
+ config.action_mailer.delivery_method = :smtp
22
+ config.action_mailer.smtp_settings = {
23
+ address: ENV['SMTP_HOST'] || 'localhost',
24
+ port: ENV['SMTP_PORT'] || 1025,
25
+ domain: ENV['SMTP_DOMAIN'] || 'localhozt',
26
+ user_name: ENV['SMTP_USERNAME'],
27
+ password: ENV['SMTP_PASSWORD'],
28
+ authentication: ENV['SMTP_AUTHENTICATION'] || 'plain',
29
+ enable_starttls_auto: true,
30
+ }
31
+ config.action_mailer.default_url_options = {
32
+ scheme: 'https',
33
+ host: ENV['WWW_HOST'] || 'localhost',
34
+ }
35
+
36
+ # Override encryption with diffcrypt
37
+ def encrypted(path, key_path: 'config/master.key', env_key: 'RAILS_MASTER_KEY')
38
+ Diffcrypt::Rails::EncryptedConfiguration.new(
39
+ config_path: Rails.root.join(path),
40
+ key_path: Rails.root.join(key_path),
41
+ env_key: env_key,
42
+ raise_if_missing_key: config.require_master_key,
43
+ )
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,47 @@
1
+ require 'pundit'
2
+
3
+ module Rapils
4
+ module Concerns
5
+ module JsonErrors
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ rescue_from ActiveRecord::RecordNotFound, with: :render_json_notfound_errors
10
+ rescue_from ActiveRecord::RecordInvalid, with: :render_json_validation_errors
11
+ rescue_from Pundit::NotAuthorizedError, with: :render_json_notfound_errors
12
+ end
13
+
14
+ def json_error(title:, details: nil, status: 422)
15
+ {
16
+ status: status,
17
+ details: details,
18
+ title: title,
19
+ }.compact
20
+ end
21
+
22
+ def render_json_error(title:, details: nil, status: 422)
23
+ errors = [
24
+ json_error(title: title, details: details, status: status),
25
+ ]
26
+
27
+ render json: { errors: errors }, status: status
28
+ end
29
+
30
+ def render_json_resource_error(resource, status: 422)
31
+ render_json_error(title: resource.errors.full_messages.join(', '), status: status)
32
+ end
33
+
34
+ def render_json_validation_errors(error)
35
+ if @resource
36
+ render_json_error(title: @resource.errors.full_messages.join(', '), status: 422)
37
+ else
38
+ render_json_error(title: error.message, status: 422)
39
+ end
40
+ end
41
+
42
+ def render_json_notfound_errors(error)
43
+ render_json_error(title: error.message, status: 404)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,58 @@
1
+ module Rapils
2
+ module Concerns
3
+ module TokenAuth
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ before_action :authenticate_api_user!
8
+ before_action :update_access_token_used_at!
9
+ end
10
+
11
+ def access_token
12
+ @access_token ||= begin
13
+ token = session[:access_token] || request.headers['Authorization']&.gsub(/bearer\s*/i, '')
14
+ return unless token
15
+
16
+ access_token ||= ::AccessToken.eager_load(:user).find_by(token: token)
17
+ return if access_token.nil? || access_token.expired? || access_token.invalidated?
18
+
19
+ access_token
20
+ end
21
+ end
22
+
23
+ def current_user
24
+ return unless access_token
25
+
26
+ @current_user ||= access_token.user
27
+ end
28
+
29
+ def authenticate_api_user!
30
+ return true if current_user
31
+
32
+ errors = [
33
+ {
34
+ 'title' => 'Not Authorized',
35
+ },
36
+ ]
37
+ render json: { errors: errors }, status: 401
38
+ end
39
+
40
+ def ensure_current_user_is_admin!
41
+ return true if current_user&.admin_roles == ['*']
42
+
43
+ errors = [
44
+ {
45
+ 'title' => 'Not Authorized',
46
+ },
47
+ ]
48
+ render json: { errors: errors }, status: 401
49
+ end
50
+
51
+ def update_access_token_used_at!
52
+ return unless access_token
53
+
54
+ access_token.update_columns(last_used_at: DateTime.now)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,12 @@
1
+ require 'rapils/concerns/json_errors'
2
+ require 'rapils/concerns/token_auth'
3
+
4
+ module Rapils
5
+ module Controllers
6
+ class BaseController < ActionController::API
7
+ include Concerns::JsonErrors
8
+ include Concerns::TokenAuth
9
+ include Pundit
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,50 @@
1
+ require 'rapils/policies/permission_policy'
2
+
3
+ # Handles management of permissions via REST interface
4
+ module Rapils
5
+ module Controllers
6
+ class PermissionsController < BaseController
7
+ def create
8
+ @resource = Permission.new(
9
+ subject_type: params[:subject_type],
10
+ subject_id: params[:subject_id],
11
+ )
12
+
13
+ manual_authorize @resource
14
+
15
+ user = User.find_by!(email: params[:email])
16
+ @resource.update!(
17
+ owner: user,
18
+ actions: params[:actions],
19
+ )
20
+
21
+ render json: ::PermissionSerializer.new(@resource).serialized_json, status: 201
22
+ end
23
+
24
+ def index
25
+ @resources = Permission.where(subject_type: params[:subject_type], subject_id: params[:subject_id])
26
+
27
+ render json: ::PermissionSerializer.new(@resources).serialized_json
28
+ end
29
+
30
+ def destroy
31
+ @resource = Permission.find(params[:id])
32
+ authorize @resource, policy_class: Rapils::Policies::PermissionPolicy
33
+ @resource.destroy!
34
+
35
+ head 204
36
+ end
37
+
38
+ protected
39
+
40
+ def manual_authorize(resource)
41
+ subject = resource.subject
42
+ permission = current_user.permissions.find_by(subject: subject)
43
+
44
+ message = "Not allowed to manage #{subject.class.name.downcase.pluralize}"
45
+ raise(Pundit::NotAuthorizedError, message) unless permission
46
+ raise(Pundit::NotAuthorizedError, message) unless permission.actions.include?('all') || permission.actions.include?('manage')
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,27 @@
1
+ # Load all controllers
2
+ require 'rapils/controllers/base_controller'
3
+ Dir["#{__dir__}/controllers/**/*.rb"].sort.each { |f| require f }
4
+
5
+ # Load all models
6
+ require_relative './models/base_record'
7
+ Dir["#{__dir__}/models/**/*.rb"].sort.each { |f| require f }
8
+
9
+ # Load all serializers
10
+ require_relative './serializers/base_serializer'
11
+ Dir["#{__dir__}/serializers/**/*.rb"].sort.each { |f| require f }
12
+
13
+ module Rapils
14
+ class Engine < Rails::Engine
15
+ isolate_namespace Rapils
16
+
17
+ # Generators for use within the engine itself, not the consuming rails app
18
+ config.generators do |g|
19
+ g.orm :active_record, primary_key_type: :uuid
20
+
21
+ # Disable things we never use
22
+ g.assets false
23
+ g.helper false
24
+ g.resource_route false
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,35 @@
1
+ module Rapils
2
+ module Models
3
+ class AccessToken < BaseRecord
4
+ self.abstract_class = true
5
+
6
+ belongs_to :user, class_name: '::User'
7
+
8
+ before_validation :generate_token
9
+
10
+ validates :name, presence: true, length: { minimum: 4, maximum: 100 }
11
+
12
+ scope :active, -> { where('(expires_at > ? OR expires_at IS NULL) AND invalidated_at IS NULL', DateTime.now) }
13
+
14
+ def invalidate!
15
+ update!(invalidated_at: DateTime.now)
16
+ end
17
+
18
+ def expired?
19
+ return false if expires_at.nil?
20
+
21
+ expires_at <= DateTime.now
22
+ end
23
+
24
+ def invalidated?
25
+ !invalidated_at.nil?
26
+ end
27
+
28
+ protected
29
+
30
+ def generate_token
31
+ self.token ||= SecureRandom.hex(16)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,8 @@
1
+ module Rapils
2
+ module Models
3
+ class BaseRecord < ActiveRecord::Base
4
+ self.abstract_class = true
5
+ self.implicit_order_column = 'created_at'
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,28 @@
1
+ module Rapils
2
+ module Models
3
+ class Permission < BaseRecord
4
+ self.abstract_class = true
5
+
6
+ ACTIONS = %w[
7
+ all
8
+ manage
9
+ view
10
+ edit
11
+ delete
12
+ ].freeze
13
+
14
+ belongs_to :owner, polymorphic: true
15
+ belongs_to :subject, polymorphic: true
16
+
17
+ validates :owner_id, uniqueness: { scope: %i[owner_type subject_type subject_id], message: '+ Subject combination already assigned' }
18
+
19
+ def owner_email
20
+ owner&.email
21
+ end
22
+
23
+ def owner_avatar_url
24
+ owner&.avatar_url
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,31 @@
1
+ module Rapils
2
+ module Models
3
+ class User < BaseRecord
4
+ self.abstract_class = true
5
+
6
+ has_many :access_tokens, class_name: '::AccessToken'
7
+ has_many :permissions, class_name: '::Permission', as: :owner
8
+
9
+ def admin?
10
+ admin_roles == ['*']
11
+ end
12
+
13
+ def access_granted?
14
+ access_granted_at && access_granted_at < DateTime.now ? true : false
15
+ end
16
+
17
+ def grant_access!
18
+ self.access_granted_at ||= DateTime.now
19
+ save! if changed?
20
+ end
21
+
22
+ def avatar_url
23
+ "https://www.gravatar.com/avatar/#{gravatar_hash}?s=160&d=identicon"
24
+ end
25
+
26
+ def gravatar_hash
27
+ Digest::MD5.hexdigest email.strip.downcase
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,49 @@
1
+ class ApplicationPolicy
2
+ attr_reader :user, :record
3
+
4
+ def initialize(user, record)
5
+ @user = user
6
+ @record = record
7
+ end
8
+
9
+ def index?
10
+ false
11
+ end
12
+
13
+ def show?
14
+ false
15
+ end
16
+
17
+ def create?
18
+ false
19
+ end
20
+
21
+ def new?
22
+ create?
23
+ end
24
+
25
+ def update?
26
+ false
27
+ end
28
+
29
+ def edit?
30
+ update?
31
+ end
32
+
33
+ def destroy?
34
+ false
35
+ end
36
+
37
+ class Scope
38
+ attr_reader :user, :scope
39
+
40
+ def initialize(user, scope)
41
+ @user = user
42
+ @scope = scope
43
+ end
44
+
45
+ def resolve
46
+ scope.all
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,22 @@
1
+ require_relative './application_policy'
2
+
3
+ module Rapils
4
+ module Policies
5
+ class PermissionPolicy < ApplicationPolicy
6
+ def create?
7
+ false
8
+ end
9
+
10
+ def destroy?
11
+ return true if record.owner == user
12
+
13
+ # If user does not own this permission, we need to see if thay
14
+ # can manage permissions on the parent subject
15
+ permission = Permission.find_by(owner: user, subject: record.subject)
16
+ return false unless permission
17
+
18
+ permission.actions.include?('all') || permission.actions.include?('manage')
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,13 @@
1
+ require 'fast_jsonapi'
2
+
3
+ module Rapils
4
+ module Serializers
5
+ class BaseSerializer
6
+ include FastJsonapi::ObjectSerializer
7
+
8
+ set_key_transform :camel_lower
9
+
10
+ attribute :id
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module Rapils
2
+ module Serializers
3
+ class PermissionSerializer < BaseSerializer
4
+ attribute :subject_id
5
+ attribute :subject_type
6
+ attribute :owner_id
7
+ attribute :owner_type
8
+ attribute :owner_email
9
+ attribute :owner_avatar_url
10
+ attribute :actions
11
+ attribute :created_at
12
+ attribute :updated_at
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,34 @@
1
+ module Rapils
2
+ DEFAULT_DEPENDENCIES = [
3
+ 'active_model/railtie',
4
+ # 'active_job/railtie',
5
+ 'active_record/railtie',
6
+ # 'active_storage/engine',
7
+ 'action_controller/railtie',
8
+ 'action_mailer/railtie',
9
+ # 'action_mailbox/engine',
10
+ # 'action_text/engine',
11
+ 'action_view/railtie',
12
+ # 'action_cable/engine',
13
+ # 'sprockets/railtie',
14
+ # 'rails/test_unit/railtie',
15
+ ].freeze
16
+
17
+ def self.setup(dependencies = DEFAULT_DEPENDENCIES)
18
+ # Require core libraries that would usually be in app Gemfile
19
+ require 'diffcrypt'
20
+ require 'pg'
21
+
22
+ # Load core rails dependencies
23
+ require 'rails'
24
+ dependencies.each { |dependency| require dependency }
25
+
26
+ # Require the gems listed in Gemfile, including any gems
27
+ # you've limited to :test, :development, or :production.
28
+ Bundler.require(*Rails.groups)
29
+
30
+ # Now we load the whole Rapils framework to hook into rails environment
31
+ require 'rapils/engine'
32
+ require 'rapils/application'
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ module Rapils
2
+ VERSION = '0.1.0-alpha1'.freeze
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :rapils do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,220 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rapils
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.pre.alpha1
5
+ platform: ruby
6
+ authors:
7
+ - Marc Qualie
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-07-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: diffcrypt
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: fast_jsonapi
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.5'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pg
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0.18'
48
+ - - "<"
49
+ - !ruby/object:Gem::Version
50
+ version: '2.0'
51
+ type: :runtime
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0.18'
58
+ - - "<"
59
+ - !ruby/object:Gem::Version
60
+ version: '2.0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: pundit
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '2.1'
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '2.1'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rails
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: 6.0.3
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: 6.0.3.1
85
+ type: :runtime
86
+ prerelease: false
87
+ version_requirements: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - "~>"
90
+ - !ruby/object:Gem::Version
91
+ version: 6.0.3
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: 6.0.3.1
95
+ - !ruby/object:Gem::Dependency
96
+ name: factory_bot_rails
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: 6.1.0
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: 6.1.0
109
+ - !ruby/object:Gem::Dependency
110
+ name: rspec-rails
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: 4.0.1
116
+ type: :development
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - "~>"
121
+ - !ruby/object:Gem::Version
122
+ version: 4.0.1
123
+ - !ruby/object:Gem::Dependency
124
+ name: rubocop
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: '0.88'
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - "~>"
135
+ - !ruby/object:Gem::Version
136
+ version: '0.88'
137
+ - !ruby/object:Gem::Dependency
138
+ name: simplecov
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - "~>"
142
+ - !ruby/object:Gem::Version
143
+ version: 0.17.0
144
+ type: :development
145
+ prerelease: false
146
+ version_requirements: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - "~>"
149
+ - !ruby/object:Gem::Version
150
+ version: 0.17.0
151
+ - !ruby/object:Gem::Dependency
152
+ name: simplecov-lcov
153
+ requirement: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - "<"
156
+ - !ruby/object:Gem::Version
157
+ version: '0.8'
158
+ type: :development
159
+ prerelease: false
160
+ version_requirements: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - "<"
163
+ - !ruby/object:Gem::Version
164
+ version: '0.8'
165
+ description: Opinionated out-of-the-box Rails API framework designed to simplify Rails
166
+ configuration
167
+ email:
168
+ - marc@marcqualie.com
169
+ executables: []
170
+ extensions: []
171
+ extra_rdoc_files: []
172
+ files:
173
+ - MIT-LICENSE
174
+ - README.md
175
+ - Rakefile
176
+ - db/migrate/20200724222614_create_users.rb
177
+ - db/migrate/20200724223120_create_access_tokens.rb
178
+ - db/migrate/20200725132221_create_permissions.rb
179
+ - lib/rapils.rb
180
+ - lib/rapils/application.rb
181
+ - lib/rapils/concerns/json_errors.rb
182
+ - lib/rapils/concerns/token_auth.rb
183
+ - lib/rapils/controllers/base_controller.rb
184
+ - lib/rapils/controllers/permissions_controller.rb
185
+ - lib/rapils/engine.rb
186
+ - lib/rapils/models/access_token.rb
187
+ - lib/rapils/models/base_record.rb
188
+ - lib/rapils/models/permission.rb
189
+ - lib/rapils/models/user.rb
190
+ - lib/rapils/policies/application_policy.rb
191
+ - lib/rapils/policies/permission_policy.rb
192
+ - lib/rapils/serializers/base_serializer.rb
193
+ - lib/rapils/serializers/permission_serializer.rb
194
+ - lib/rapils/setup.rb
195
+ - lib/rapils/version.rb
196
+ - lib/tasks/rapils_tasks.rake
197
+ homepage: https://github.com/marcqualie/rapils
198
+ licenses:
199
+ - MIT
200
+ metadata: {}
201
+ post_install_message:
202
+ rdoc_options: []
203
+ require_paths:
204
+ - lib
205
+ required_ruby_version: !ruby/object:Gem::Requirement
206
+ requirements:
207
+ - - ">="
208
+ - !ruby/object:Gem::Version
209
+ version: '0'
210
+ required_rubygems_version: !ruby/object:Gem::Requirement
211
+ requirements:
212
+ - - ">"
213
+ - !ruby/object:Gem::Version
214
+ version: 1.3.1
215
+ requirements: []
216
+ rubygems_version: 3.1.4
217
+ signing_key:
218
+ specification_version: 4
219
+ summary: Opinionated out-of-the-box Rails API framework
220
+ test_files: []