snfoil-rails 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/CODE_OF_CONDUCT.md +74 -0
  3. data/Rakefile +1 -1
  4. data/lib/generators/sn_foil/sn_foil_generator.rb +47 -0
  5. data/lib/sn_foil/controller/api.rb +77 -0
  6. data/lib/sn_foil/controller/base.rb +19 -0
  7. data/lib/sn_foil/controller/concerns/change_controller_concern.rb +21 -0
  8. data/lib/sn_foil/controller/concerns/create_controller_concern.rb +38 -0
  9. data/lib/sn_foil/controller/concerns/destroy_controller_concern.rb +40 -0
  10. data/lib/sn_foil/controller/concerns/index_controller_concern.rb +66 -0
  11. data/lib/sn_foil/controller/concerns/setup_controller_concern.rb +84 -0
  12. data/lib/sn_foil/controller/concerns/show_controller_concern.rb +36 -0
  13. data/lib/sn_foil/controller/concerns/update_controller_concern.rb +38 -0
  14. data/lib/sn_foil/jsonapi_deserializer.rb +151 -0
  15. data/lib/sn_foil/jsonapi_serializer.rb +16 -0
  16. data/lib/sn_foil/rails.rb +18 -7
  17. data/lib/sn_foil/rails/engine.rb +24 -0
  18. data/lib/sn_foil/rails/version.rb +1 -1
  19. data/lib/sn_foil/searcher.rb +123 -0
  20. metadata +47 -13
  21. data/lib/sn_foil/rails/controller/api.rb +0 -22
  22. data/lib/sn_foil/rails/controller/concerns/change_controller_concern.rb +0 -28
  23. data/lib/sn_foil/rails/controller/concerns/create_controller_concern.rb +0 -40
  24. data/lib/sn_foil/rails/controller/concerns/destroy_controller_concern.rb +0 -42
  25. data/lib/sn_foil/rails/controller/concerns/index_controller_concern.rb +0 -72
  26. data/lib/sn_foil/rails/controller/concerns/setup_controller_concern.rb +0 -110
  27. data/lib/sn_foil/rails/controller/concerns/show_controller_concern.rb +0 -38
  28. data/lib/sn_foil/rails/controller/concerns/update_controller_concern.rb +0 -40
  29. data/lib/sn_foil/rails/railtie.rb +0 -10
  30. data/lib/sn_foil/rails/searcher.rb +0 -127
  31. data/lib/tasks/sn_foil/rails_tasks.rake +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 95946ad8c9da4c8cf3498f8394145465131694074e3aef1b7f3849ae334b9358
4
- data.tar.gz: 817e82e8f00557815290a724aa27f69a2c7d01e2c3f28ac5b66e0bd9834b51ec
3
+ metadata.gz: e837f5987125bbffbb68708d10913c6357ae424d35fe9029daac63fb0d8b2dfc
4
+ data.tar.gz: 8818603e24b76a442575dfadd726f4dcae6c82756c7edb6ffeb157b691a54943
5
5
  SHA512:
6
- metadata.gz: 53d257a20b1cc16a9a8d7c54ec62f6c15cfc0b9bf377edc2d86c206af79331078746e9ea0e266103aff15407eaecdad56887c9c72dd790db4142d339f2effccb
7
- data.tar.gz: 5863066aa0bb468aef6796d8336e69fda0b207b5b161999ebd86fa3ff2d2ece2500a4a868b0d1fbb83a2509ad486f1b958e29861dfaf99441c23bb7b95922596
6
+ metadata.gz: 47c77c2ddaba01d200cf3dbca4383ef2dd0d20a47fe43d1030ba32b3025678b75e0ff721896f0e3849aef951b86ee53cd37a0c786f0937fd844caf4f68696522
7
+ data.tar.gz: 508627b26457a293fc67b64dedb00adaeabd9c0027ed63d079e6a9067f77693051181294c7bf05b51e8ce206b4b67e2b3ee0d6cba9788c2a20729b5566d2a493
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at howeszy@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Rakefile CHANGED
@@ -10,7 +10,7 @@ require 'rdoc/task'
10
10
 
11
11
  RDoc::Task.new(:rdoc) do |rdoc|
12
12
  rdoc.rdoc_dir = 'rdoc'
13
- rdoc.title = 'Sn::Foil::Rails'
13
+ rdoc.title = 'SnFoil::Rails'
14
14
  rdoc.options << '--line-numbers'
15
15
  rdoc.rdoc_files.include('README.md')
16
16
  rdoc.rdoc_files.include('lib/**/*.rb')
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SnFoilGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('templates', __dir__)
5
+ argument :model, type: :string
6
+
7
+ def generate_sn_foil
8
+ generate_model
9
+ generate_controller
10
+ generate_searcher
11
+ generate_serializer
12
+ generate_deserializer
13
+ generate_policy
14
+ generate_context
15
+ puts 'In order to expose your model, it must be added to the config/routes.rb file'
16
+ end
17
+
18
+ private
19
+
20
+ def generate_context
21
+ template 'context.erb', "app/contexts/#{model.singularize.underscore}_context.rb"
22
+ end
23
+
24
+ def generate_controller
25
+ template 'controller.erb', "app/controllers/#{model.pluralize.underscore}_controller.rb"
26
+ end
27
+
28
+ def generate_deserializer
29
+ template 'jsonapi_deserializer.erb', "app/deserializers/#{model.singularize.underscore}_deserializer.rb"
30
+ end
31
+
32
+ def generate_serializer
33
+ template 'jsonapi_serializer.erb', "app/serializers/#{model.singularize.underscore}_jsonapi_serializer.rb"
34
+ end
35
+
36
+ def generate_model
37
+ generate('model', model.underscore) unless File.file? "app/models/#{model.singularize.underscore}.rb"
38
+ end
39
+
40
+ def generate_searcher
41
+ template 'searcher.erb', "app/searchers/#{model.pluralize.underscore}_searcher.rb"
42
+ end
43
+
44
+ def generate_policy
45
+ template 'policy.erb', "app/policies/#{model.singularize.underscore}_policy.rb"
46
+ end
47
+ end
@@ -0,0 +1,77 @@
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
+ super(**options.merge(deserialize: true))
35
+ end
36
+
37
+ def setup_update(**options)
38
+ super(**options.merge(deserialize: true))
39
+ end
40
+
41
+ def render_change(model, **options)
42
+ if model.errors.empty?
43
+ render json: serializer(**options).new(model, **options).serializable_hash
44
+ else
45
+ render model.errors, status: :unprocessable_entity
46
+ end
47
+ end
48
+
49
+ def render_destroy(model, **_options)
50
+ if model.errors.empty?
51
+ render status: :no_content
52
+ else
53
+ render model.errors, status: :unprocessable_entity
54
+ end
55
+ end
56
+
57
+ def render_index(results, **options)
58
+ render json: serializer(**options).new(paginate(results, **options), **options, meta: meta(results, **options))
59
+ .serializable_hash
60
+ end
61
+
62
+ def render_show(model, **options)
63
+ render json: serializer(**options).new(model, **options).serializable_hash
64
+ end
65
+
66
+ private
67
+
68
+ def inject_deserialized_params(**options)
69
+ return options unless options[:params].present? && options[:deserialize] == true
70
+ return options unless deserializer(**options)
71
+
72
+ options[:params] = deserializer(**options).new(options[:params], **options).to_h
73
+ options
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,19 @@
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
@@ -0,0 +1,21 @@
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
@@ -0,0 +1,38 @@
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
@@ -0,0 +1,40 @@
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
@@ -0,0 +1,66 @@
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
+ total_pages = results.respond_to?(:total_pages) ? results.total_pages : nil
54
+ total_count = results.respond_to?(:total_count) ? results.total_count : nil
55
+
56
+ {
57
+ page: page(**options),
58
+ pages: total_pages,
59
+ total: total_count,
60
+ per: per_page(**options)
61
+ }
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+
5
+ module SnFoil
6
+ module Controller
7
+ module Concerns
8
+ module SetupControllerConcern
9
+ extend ActiveSupport::Concern
10
+
11
+ class_methods do
12
+ attr_reader :i_context
13
+
14
+ def context(klass = nil)
15
+ @i_context = klass
16
+ end
17
+ end
18
+
19
+ def context(**options)
20
+ options[:context] || self.class.i_context
21
+ end
22
+
23
+ def setup_options(**options)
24
+ options = inject_params(**options)
25
+ options = inject_id(**options)
26
+ options = inject_includes(**options)
27
+ inject_controller_action(**options)
28
+ end
29
+
30
+ def current_context(**options)
31
+ @current_context ||= context(**options).new(context_user)
32
+ end
33
+
34
+ protected
35
+
36
+ def pundit_not_authorized
37
+ head :forbidden
38
+ end
39
+
40
+ private
41
+
42
+ # Grab the rails params and inject them into the options
43
+ def inject_params(**options)
44
+ return options if options[:params]
45
+ return options unless params
46
+
47
+ options[:params] = params.to_unsafe_h.deep_symbolize_keys
48
+ options[:controller_params] = options[:params]
49
+ options
50
+ end
51
+
52
+ def inject_id(**options)
53
+ return options if options[:id]
54
+
55
+ options[:id] = id if defined? id
56
+ options[:id] ||= options[:params][:id]
57
+ options
58
+ end
59
+
60
+ def inject_includes(**options)
61
+ return options if options[:include]
62
+ return options unless options.dig(:params, :include)
63
+
64
+ options[:include] = options.dig(:params, :include)
65
+ .split(',')
66
+ .map { |item| item.underscore.to_sym }
67
+ options
68
+ end
69
+
70
+ def inject_controller_action(**options)
71
+ return options if options[:controller_action]
72
+ return options unless options.dig(:params, :action)
73
+
74
+ options[:controller_action] = options[:params][:action]
75
+ options
76
+ end
77
+
78
+ def context_user
79
+ return current_user if defined? current_user
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end