snfoil-rails 0.8.3 → 1.0.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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. metadata +146 -65
  3. data/CODE_OF_CONDUCT.md +0 -74
  4. data/MIT-LICENSE +0 -20
  5. data/README.md +0 -28
  6. data/Rakefile +0 -29
  7. data/lib/generators/sn_foil/all/all_generator.rb +0 -50
  8. data/lib/generators/sn_foil/context/context_generator.rb +0 -41
  9. data/lib/generators/sn_foil/context/templates/context.erb +0 -9
  10. data/lib/generators/sn_foil/controller/controller_generator.rb +0 -52
  11. data/lib/generators/sn_foil/controller/templates/api_controller.erb +0 -14
  12. data/lib/generators/sn_foil/controller/templates/controller.erb +0 -14
  13. data/lib/generators/sn_foil/jsonapi_deserializer/jsonapi_deserializer_generator.rb +0 -41
  14. data/lib/generators/sn_foil/jsonapi_deserializer/templates/jsonapi_deserializer.erb +0 -12
  15. data/lib/generators/sn_foil/jsonapi_serializer/jsonapi_serializer_generator.rb +0 -41
  16. data/lib/generators/sn_foil/jsonapi_serializer/templates/jsonapi_serializer.erb +0 -20
  17. data/lib/generators/sn_foil/policy/policy_generator.rb +0 -41
  18. data/lib/generators/sn_foil/policy/templates/policy.erb +0 -22
  19. data/lib/generators/sn_foil/searcher/searcher_generator.rb +0 -41
  20. data/lib/generators/sn_foil/searcher/templates/searcher.erb +0 -7
  21. data/lib/sn_foil/configuration/lazy_jsonapi_serializer.rb +0 -87
  22. data/lib/sn_foil/controller/api.rb +0 -90
  23. data/lib/sn_foil/controller/base.rb +0 -19
  24. data/lib/sn_foil/controller/concerns/change_controller_concern.rb +0 -21
  25. data/lib/sn_foil/controller/concerns/create_controller_concern.rb +0 -38
  26. data/lib/sn_foil/controller/concerns/destroy_controller_concern.rb +0 -40
  27. data/lib/sn_foil/controller/concerns/index_controller_concern.rb +0 -67
  28. data/lib/sn_foil/controller/concerns/setup_controller_concern.rb +0 -85
  29. data/lib/sn_foil/controller/concerns/show_controller_concern.rb +0 -36
  30. data/lib/sn_foil/controller/concerns/update_controller_concern.rb +0 -38
  31. data/lib/sn_foil/jsonapi_deserializer.rb +0 -159
  32. data/lib/sn_foil/jsonapi_serializer.rb +0 -16
  33. data/lib/sn_foil/rails/engine.rb +0 -23
  34. data/lib/sn_foil/rails/version.rb +0 -7
  35. data/lib/sn_foil/rails.rb +0 -25
  36. data/lib/sn_foil/searcher.rb +0 -122
  37. data/lib/snfoil-rails.rb +0 -5
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class <%= class_modules %><%= class_name %>Controller < SnFoil::Controller::Base
4
- context <%= name.singularize.camelcase %>Context
5
- serializer <%= name.singularize.camelcase %>JsonapiSerializer
6
- deserializer <%= name.singularize.camelcase %>JsonapiDeserializer
7
-
8
- # SnFoil::Controller requires current_entity or current_user defined to properly scope
9
- # all queries, otherwise data leaks could occur
10
- #
11
- # def current_entity
12
- # # logic to find authenticated user
13
- # end
14
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SnFoil
4
- class JsonapiDeserializerGenerator < Rails::Generators::Base
5
- source_root File.expand_path('templates', __dir__)
6
-
7
- argument :model, type: :string
8
-
9
- class_option :path, desc: 'Base path for file', type: :string, default: 'app/jsonapi_deserializers'
10
-
11
- def add_app_file
12
- file_name = if modules.length.zero?
13
- name
14
- else
15
- "#{modules.join('/')}/#{name}"
16
- end
17
-
18
- template('jsonapi_deserializer.erb', "#{options[:path]}/#{file_name}_jsonapi_deserializer.rb")
19
- end
20
-
21
- private
22
-
23
- def name
24
- @name ||= model.split('/').last.underscore.singularize
25
- end
26
-
27
- def class_name
28
- @class_name ||= name.camelize
29
- end
30
-
31
- def modules
32
- @modules ||= model.split('/')[0..-2].map(&:underscore)
33
- end
34
-
35
- def class_modules
36
- return if modules.length.zero?
37
-
38
- @class_modules ||= "#{modules.map(&:camelize).join('::')}::"
39
- end
40
- end
41
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class <%= class_modules %><%= class_name %>JsonapiDeserializer
4
- include SnFoil::JsonapiDeserializer
5
-
6
- # Add attributes of the model you want to serializer with the following syntax
7
- attributes :id
8
-
9
- # Add relationships with the following syntax
10
- # belongs_to :store, serializer: Jsonapi::StoreJsonapiSerializer
11
- # has_many :locations, serializer: Jsonapi::LocationJsonapiSerializer
12
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SnFoil
4
- class JsonapiSerializerGenerator < Rails::Generators::Base
5
- source_root File.expand_path('templates', __dir__)
6
-
7
- argument :model, type: :string
8
-
9
- class_option :path, desc: 'Base path for file', type: :string, default: 'app/jsonapi_serializers'
10
-
11
- def add_app_file
12
- file_name = if modules.length.zero?
13
- name
14
- else
15
- "#{modules.join('/')}/#{name}"
16
- end
17
-
18
- template('jsonapi_serializer.erb', "#{options[:path]}/#{file_name}_jsonapi_serializer.rb")
19
- end
20
-
21
- private
22
-
23
- def name
24
- @name ||= model.split('/').last.underscore.singularize
25
- end
26
-
27
- def class_name
28
- @class_name ||= name.camelize
29
- end
30
-
31
- def modules
32
- @modules ||= model.split('/')[0..-2].map(&:underscore)
33
- end
34
-
35
- def class_modules
36
- return if modules.length.zero?
37
-
38
- @class_modules ||= "#{modules.map(&:camelize).join('::')}::"
39
- end
40
- end
41
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class <%= class_modules %><%= class_name %>JsonapiSerializer
4
- include SnFoil::JsonapiSerializer
5
-
6
- set_id :id
7
- set_type :<%= name.pluralize.dasherize %>
8
-
9
- # SnFoil::JsonapiSerializer is just a wrapper for jsonapi-serializer (https://github.com/jsonapi-serializer/jsonapi-serializer)
10
- # with some defaults added in
11
-
12
- # Add attributes of the model you want to serializer with the following syntax
13
- # attributes :name
14
- # :description
15
- # :logo_url
16
-
17
- # Add relationships with the following syntax
18
- # belongs_to :store, serializer: Jsonapi::StoreJsonapiSerializer
19
- # has_many :locations, serializer: Jsonapi::LocationJsonapiSerializer
20
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SnFoil
4
- class PolicyGenerator < Rails::Generators::Base
5
- source_root File.expand_path('templates', __dir__)
6
-
7
- argument :model, type: :string
8
-
9
- class_option :path, desc: 'Base path for file', type: :string, default: 'app/policies'
10
-
11
- def add_app_file
12
- file_name = if modules.length.zero?
13
- name
14
- else
15
- "#{modules.join('/')}/#{name}"
16
- end
17
-
18
- template('policy.erb', "#{options[:path]}/#{file_name}_policy.rb")
19
- end
20
-
21
- private
22
-
23
- def name
24
- @name ||= model.split('/').last.underscore.singularize
25
- end
26
-
27
- def class_name
28
- @class_name ||= name.camelize
29
- end
30
-
31
- def modules
32
- @modules ||= model.split('/')[0..-2].map(&:underscore)
33
- end
34
-
35
- def class_modules
36
- return if modules.length.zero?
37
-
38
- @class_modules ||= "#{modules.map(&:camelize).join('::')}::"
39
- end
40
- end
41
- end
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class <%= class_modules %><%= class_name %>Policy
4
- include SnFoil::Policy
5
- # A SnFoil::Policy is just a Pundit policy (https://github.com/varvet/pundit)
6
- # with some defaults
7
- # Available methods: show?, create?, update?, destroy?, index?, associate?
8
-
9
- # def show?
10
- # true
11
- # end
12
-
13
- # class Scope
14
- # # available read-only attributes
15
- # # - scope: the default scope passed into the policy (ex: an ActiveRecord::Relation)
16
- # # - entity: the object that the scope is build around (ex: a User)
17
-
18
- # def resolve
19
- # # the new default scope
20
- # end
21
- # end
22
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SnFoil
4
- class SearcherGenerator < Rails::Generators::Base
5
- source_root File.expand_path('templates', __dir__)
6
-
7
- argument :model, type: :string
8
-
9
- class_option :path, desc: 'Base path for file', type: :string, default: 'app/searchers'
10
-
11
- def add_app_file
12
- file_name = if modules.length.zero?
13
- name
14
- else
15
- "#{modules.join('/')}/#{name}"
16
- end
17
-
18
- template('searcher.erb', "#{options[:path]}/#{file_name}_searcher.rb")
19
- end
20
-
21
- private
22
-
23
- def name
24
- @name ||= model.split('/').last.underscore.pluralize
25
- end
26
-
27
- def class_name
28
- @class_name ||= name.camelize
29
- end
30
-
31
- def modules
32
- @modules ||= model.split('/')[0..-2].map(&:underscore)
33
- end
34
-
35
- def class_modules
36
- return if modules.length.zero?
37
-
38
- @class_modules ||= "#{modules.map(&:camelize).join('::')}::"
39
- end
40
- end
41
- end
@@ -1,7 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class <%= class_modules %><%= class_name %>Searcher
4
- include SnFoil::Searcher
5
-
6
- model <%= class_name.singularize %>
7
- end
@@ -1,87 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SnFoil
4
- module Configuration
5
- module LazyJsonapiSerializer
6
- extend ActiveSupport::Concern
7
-
8
- class << self
9
- def belongs_to(relationship_name, options = {}, &block)
10
- block = belongs_to_optimized_block(relationship_name, options, &block)
11
- super(relationship_name, options, &block)
12
- end
13
-
14
- def has_one(relationship_name, options = {}, &block) # rubocop:disable Naming/PredicateName reason: method override
15
- super(relationship_name, has_relation_optimized_options(relationship_name, options), &block)
16
- end
17
-
18
- def has_many(relationship_name, options = {}, &block) # rubocop:disable Naming/PredicateName reason: method override
19
- super(relationship_name, has_relation_optimized_options(relationship_name, options), &block)
20
- end
21
-
22
- private
23
-
24
- # We need to parse the include block because FastJsonAPI does not allow access to the pre-parsed includes
25
- def parse_include(params)
26
- return [] unless params[:include]
27
-
28
- if params[:include].is_a?(String)
29
- params[:include].split(',')
30
- else
31
- params[:include]
32
- end.map { |r| r.to_s.dasherize }.join(',')
33
- end
34
-
35
- def lookup_full_object_for_belongs_to(record, relationship_name, options, params)
36
- return unless parse_include(params).include?(relationship_name.to_s.dasherize)
37
-
38
- record.send(options[:object_method_name] || relationship_name)
39
- end
40
-
41
- def create_substitute_object_for_belongs_to(record, relationship_name, options)
42
- relationship_id = options[:id_method_name] || "#{relationship_name}_id".to_sym
43
- OpenStruct.new(id: record.send(relationship_id))
44
- end
45
-
46
- def belongs_to_optimized_block(relationship_name, options = {}, &block)
47
- return block if options[:skip_optimization] == true || block
48
-
49
- proc do |record, params|
50
- if params && params[:include]
51
- lookup_full_object(record_for_belongs_to, relationship_name, options, params)
52
- else
53
- create_substitute_object_for_belongs_to(record, relationship_name, options)
54
- end
55
- end
56
- end
57
-
58
- def create_included_proc_for_has_relation(relationship_name)
59
- proc do |_record, params|
60
- if params && params[:include]
61
- parse_include(params).include?(relationship_name.to_s.dasherize)
62
- else
63
- false
64
- end
65
- end
66
- end
67
-
68
- def create_if_proc_for_has_relation(if_proc, included_proc)
69
- if if_proc.present?
70
- proc do |record, params|
71
- FastJsonapi.call_proc(included_proc, record, params) && FastJsonapi.call_proc(if_proc, record, params)
72
- end
73
- else
74
- included_proc
75
- end
76
- end
77
-
78
- def has_relation_optimized_options(relationship_name, options) # rubocop:disable Naming/PredicateName reason: method override
79
- return options if options[:skip_optimization] == true
80
-
81
- options[:if] = create_if_proc_for_has_relation(options[:if], create_included_proc_for_has_relation(relationship_name))
82
- options
83
- end
84
- end
85
- end
86
- end
87
- end
@@ -1,90 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'action_controller/api'
4
- require_relative 'base'
5
-
6
- module SnFoil
7
- module Controller
8
- class API < SnFoil::Controller::Base
9
- class << self
10
- attr_reader :i_serializer, :i_deserializer
11
-
12
- def serializer(klass = nil)
13
- @i_serializer = klass
14
- end
15
-
16
- def deserializer(klass = nil)
17
- @i_deserializer = klass
18
- end
19
- end
20
-
21
- def serializer(**options)
22
- options[:serializer] || self.class.i_serializer
23
- end
24
-
25
- def deserializer(**options)
26
- options[:deserializer] || self.class.i_deserializer
27
- end
28
-
29
- def setup_options(**options)
30
- inject_deserialized_params(super)
31
- end
32
-
33
- def setup_create(**options)
34
- options[:deserialize] = options[:deserialize].nil? ? true : options[:deserialize]
35
- super(**options)
36
- end
37
-
38
- def setup_update(**options)
39
- options[:deserialize] = options[:deserialize].nil? ? true : options[:deserialize]
40
- super(**options)
41
- end
42
-
43
- def render_change(model, **options)
44
- if model.errors.empty?
45
- params
46
- render json: serializer(**options).new(model,
47
- **options,
48
- params: (options[:controller_params] || options[:params] || {})
49
- .merge(current_entity: context_entity)).serializable_hash
50
- else
51
- render json: model.errors, status: :unprocessable_entity
52
- end
53
- end
54
-
55
- def render_destroy(model, **_options)
56
- if model.errors.empty?
57
- render json: {}, status: :no_content
58
- else
59
- render json: model.errors, status: :unprocessable_entity
60
- end
61
- end
62
-
63
- def render_index(results, **options)
64
- render json: serializer(**options).new(paginate(results, **options),
65
- **options,
66
- params: (options[:controller_params] || options[:params] || {})
67
- .merge(current_entity: context_entity),
68
- meta: meta(results, **options))
69
- .serializable_hash
70
- end
71
-
72
- def render_show(model, **options)
73
- render json: serializer(**options).new(model,
74
- **options,
75
- params: (options[:controller_params] || options[:params] || {})
76
- .merge(current_entity: context_entity)).serializable_hash
77
- end
78
-
79
- private
80
-
81
- def inject_deserialized_params(**options)
82
- return options unless options[:params].present? && options[:deserialize] == true
83
- return options unless deserializer(**options)
84
-
85
- options[:params] = deserializer(**options).new(options[:params], **options).to_h
86
- options
87
- end
88
- end
89
- end
90
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'concerns/create_controller_concern'
4
- require_relative 'concerns/destroy_controller_concern'
5
- require_relative 'concerns/index_controller_concern'
6
- require_relative 'concerns/show_controller_concern'
7
- require_relative 'concerns/update_controller_concern'
8
-
9
- module SnFoil
10
- module Controller
11
- class Base < ActionController::Base # rubocop:disable Rails/ApplicationController
12
- include Concerns::CreateControllerConcern
13
- include Concerns::DestroyControllerConcern
14
- include Concerns::IndexControllerConcern
15
- include Concerns::ShowControllerConcern
16
- include Concerns::UpdateControllerConcern
17
- end
18
- end
19
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support/concern'
4
-
5
- module SnFoil
6
- module Controller
7
- module Concerns
8
- module ChangeControllerConcern
9
- extend ActiveSupport::Concern
10
-
11
- def render_change(model, **_options)
12
- if model.errors.empty?
13
- render model
14
- else
15
- render model.errors, status: :unprocessable_entity
16
- end
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support/concern'
4
- require_relative 'setup_controller_concern'
5
- require_relative 'change_controller_concern'
6
-
7
- module SnFoil
8
- module Controller
9
- module Concerns
10
- module CreateControllerConcern
11
- extend ActiveSupport::Concern
12
-
13
- included do
14
- include SetupControllerConcern
15
- include ChangeControllerConcern
16
- end
17
-
18
- def create(**options)
19
- options = setup_create(**options)
20
- model = process_create(**options)
21
- render_create(model, **options)
22
- end
23
-
24
- def setup_create(**options)
25
- setup_options(**options)
26
- end
27
-
28
- def process_create(**options)
29
- current_context(**options).create(**options)
30
- end
31
-
32
- def render_create(model, **options)
33
- render_change(model, **options)
34
- end
35
- end
36
- end
37
- end
38
- end
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support/concern'
4
- require_relative 'setup_controller_concern'
5
-
6
- module SnFoil
7
- module Controller
8
- module Concerns
9
- module DestroyControllerConcern
10
- extend ActiveSupport::Concern
11
-
12
- included do
13
- include SetupControllerConcern
14
- end
15
-
16
- def destroy(**options)
17
- options = setup_destroy(**options)
18
- model = process_destroy(**options)
19
- render_destroy(model, **options)
20
- end
21
-
22
- def setup_destroy(**options)
23
- setup_options(**options)
24
- end
25
-
26
- def process_destroy(**options)
27
- current_context(**options).destroy(**options)
28
- end
29
-
30
- def render_destroy(model, **_options)
31
- if model.errors.empty?
32
- render nil
33
- else
34
- render model.errors, status: :unprocessable_entity
35
- end
36
- end
37
- end
38
- end
39
- end
40
- end
@@ -1,67 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support/concern'
4
- require_relative 'setup_controller_concern'
5
-
6
- module SnFoil
7
- module Controller
8
- module Concerns
9
- module IndexControllerConcern
10
- extend ActiveSupport::Concern
11
-
12
- included do
13
- include SetupControllerConcern
14
- end
15
-
16
- def index(**options)
17
- options = setup_index(**options)
18
- results = process_index(**options)
19
- render_index(results, **options)
20
- end
21
-
22
- def setup_index(**options)
23
- setup_options(**options)
24
- end
25
-
26
- def process_index(**options)
27
- current_context(**options).index(options)
28
- end
29
-
30
- def render_index(results, **options)
31
- render paginate(results, **options), meta: meta(results, options)
32
- end
33
-
34
- def paginate(results, **options)
35
- return results unless results.respond_to?(:page)
36
-
37
- results.page(page(**options))
38
- .per(per_page(**options))
39
- end
40
-
41
- def page(**options)
42
- (options.dig(:params, :page) || 1).to_i
43
- end
44
-
45
- def per_page(**options)
46
- per_page_param = (options.dig(:params, :per_page) || 10).to_i
47
- return 1000 if per_page_param.zero? || per_page_param > 1000
48
-
49
- per_page_param
50
- end
51
-
52
- def meta(results, **options)
53
- results = paginate(results, **options)
54
- total_pages = results.respond_to?(:total_pages) ? results.total_pages : nil
55
- total_count = results.respond_to?(:total_count) ? results.total_count : nil
56
-
57
- {
58
- page: page(**options),
59
- pages: total_pages,
60
- total: total_count,
61
- per: per_page(**options)
62
- }
63
- end
64
- end
65
- end
66
- end
67
- end