auxiliary_rails 0.1.6 → 0.3.1

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab-ci.yml +26 -0
  3. data/.rubocop.yml +27 -2
  4. data/.rubocop_todo.yml +5 -15
  5. data/.yardopts +5 -0
  6. data/CHANGELOG.md +39 -3
  7. data/CONTRIBUTING.md +0 -6
  8. data/Gemfile.lock +116 -92
  9. data/README.md +213 -6
  10. data/auxiliary_rails.gemspec +13 -12
  11. data/bin/rubocop +29 -0
  12. data/bitbucket-pipelines.yml +35 -0
  13. data/lib/auxiliary_rails.rb +5 -3
  14. data/lib/auxiliary_rails/application/command.rb +56 -0
  15. data/lib/auxiliary_rails/application/error.rb +10 -0
  16. data/lib/auxiliary_rails/application/form.rb +30 -0
  17. data/lib/auxiliary_rails/application/query.rb +71 -0
  18. data/lib/auxiliary_rails/cli.rb +18 -5
  19. data/lib/auxiliary_rails/concerns/errorable.rb +22 -0
  20. data/lib/auxiliary_rails/concerns/performable.rb +128 -0
  21. data/lib/auxiliary_rails/version.rb +1 -1
  22. data/lib/generators/auxiliary_rails/api_resource_generator.rb +10 -1
  23. data/lib/generators/auxiliary_rails/command_generator.rb +44 -0
  24. data/lib/generators/auxiliary_rails/install_commands_generator.rb +6 -1
  25. data/lib/generators/auxiliary_rails/install_generator.rb +0 -1
  26. data/lib/generators/auxiliary_rails/templates/apis/api_entity_template.rb.erb +2 -1
  27. data/lib/generators/auxiliary_rails/templates/apis/api_resource_spec_template.rb.erb +36 -9
  28. data/lib/generators/auxiliary_rails/templates/apis/api_resource_template.rb.erb +12 -6
  29. data/lib/generators/auxiliary_rails/templates/application_error_template.rb +1 -1
  30. data/lib/generators/auxiliary_rails/templates/commands/application_command_template.rb +2 -0
  31. data/lib/generators/auxiliary_rails/templates/commands/command_spec_template.rb +11 -0
  32. data/lib/generators/auxiliary_rails/templates/commands/command_template.rb +6 -0
  33. data/lib/generators/auxiliary_rails/templates/commands/commands.en_template.yml +5 -0
  34. data/templates/rails/elementary.rb +40 -10
  35. metadata +81 -24
  36. data/lib/auxiliary_rails/abstract_command.rb +0 -96
  37. data/lib/auxiliary_rails/abstract_error.rb +0 -7
  38. data/lib/generators/auxiliary_rails/install_rubocop_generator.rb +0 -29
  39. data/lib/generators/auxiliary_rails/templates/application_command_template.rb +0 -2
  40. data/lib/generators/auxiliary_rails/templates/rubocop/rubocop_auxiliary_rails_template.yml +0 -51
  41. data/lib/generators/auxiliary_rails/templates/rubocop/rubocop_template.yml +0 -7
@@ -0,0 +1,71 @@
1
+ require 'auxiliary_rails/concerns/errorable'
2
+ require 'dry/core/class_attributes'
3
+ require 'dry-initializer'
4
+
5
+ module AuxiliaryRails
6
+ module Application
7
+ class Query
8
+ extend Dry::Core::ClassAttributes
9
+ extend Dry::Initializer
10
+ include AuxiliaryRails::Concerns::Errorable
11
+
12
+ defines :default_relation
13
+
14
+ option :relation, default: proc { nil }
15
+
16
+ def self.call(*args)
17
+ new(*args).call
18
+ end
19
+
20
+ def call
21
+ ensure_proper_relation_types!
22
+
23
+ perform
24
+
25
+ query
26
+ end
27
+
28
+ def perform
29
+ raise NotImplementedError
30
+ end
31
+
32
+ def method_missing(method_name, *args, &block)
33
+ super unless query.respond_to?(method_name)
34
+
35
+ query.send(method_name, *args, &block)
36
+ end
37
+
38
+ def respond_to_missing?(method_name, include_private = false)
39
+ query.respond_to?(method_name) || super
40
+ end
41
+
42
+ private
43
+
44
+ # rubocop:disable Metrics/AbcSize, Style/GuardClause
45
+ def ensure_proper_relation_types!
46
+ if self.class.default_relation.nil?
47
+ error!('Undefined `default_relation`')
48
+ end
49
+ if !queriable_object?(self.class.default_relation)
50
+ error!('Invalid class of `default_relation`')
51
+ end
52
+ if !relation.nil? && !queriable_object?(relation)
53
+ error!('Invalid class of `relation` option')
54
+ end
55
+ end
56
+ # rubocop:enable Metrics/AbcSize, Style/GuardClause
57
+
58
+ def queriable_object?(object)
59
+ object.is_a?(ActiveRecord::Relation)
60
+ end
61
+
62
+ def query(scoped_query = nil)
63
+ @query ||= (relation || self.class.default_relation)
64
+
65
+ @query = scoped_query unless scoped_query.nil?
66
+
67
+ @query
68
+ end
69
+ end
70
+ end
71
+ end
@@ -6,6 +6,10 @@ module AuxiliaryRails
6
6
 
7
7
  TEMPLATES_DIR =
8
8
  File.expand_path("#{__dir__}/../../templates/")
9
+ REPOSITORY_URL =
10
+ 'https://raw.githubusercontent.com/ergoserv/' \
11
+ 'auxiliary_rails/develop/templates'
12
+ .freeze
9
13
 
10
14
  desc 'new APP_PATH', 'Create Rails application from template'
11
15
  long_desc <<-LONGDESC
@@ -17,20 +21,29 @@ module AuxiliaryRails
17
21
  option :database,
18
22
  default: 'postgresql',
19
23
  type: :string
20
- option :template_name,
24
+ option :template,
21
25
  default: 'elementary',
22
26
  type: :string
27
+ option :develop,
28
+ default: false,
29
+ type: :boolean
23
30
  def new(app_path)
24
31
  run "rails new #{app_path} " \
25
- '--skip-action-cable --skip-coffee --skip-test ' \
26
32
  "--database=#{options[:database]} " \
27
- "--template=#{rails_template_path(options[:template_name])}"
33
+ "--template=#{rails_template_path(options[:template])} " \
34
+ '--skip-action-cable ' \
35
+ '--skip-coffee ' \
36
+ '--skip-test '
28
37
  end
29
38
 
30
39
  private
31
40
 
32
- def rails_template_path(template_name)
33
- "#{TEMPLATES_DIR}/rails/#{template_name}.rb"
41
+ def rails_template_path(template)
42
+ if options[:develop] == true
43
+ "#{REPOSITORY_URL}/rails/#{template}.rb"
44
+ else
45
+ "#{TEMPLATES_DIR}/rails/#{template}.rb"
46
+ end
34
47
  end
35
48
  end
36
49
  end
@@ -0,0 +1,22 @@
1
+ module AuxiliaryRails
2
+ module Concerns
3
+ module Errorable
4
+ # extend ActiveSupport::Concern
5
+
6
+ def error!(message = nil)
7
+ message ||= "`#{self.class}` raised error."
8
+ raise error_class, message
9
+ end
10
+
11
+ if defined?(ApplicationError)
12
+ def error_class
13
+ ApplicationError
14
+ end
15
+ else
16
+ def error_class
17
+ AuxiliaryRails::Application::Error
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,128 @@
1
+ require 'active_support/concern'
2
+ require 'auxiliary_rails/concerns/errorable'
3
+
4
+ module AuxiliaryRails
5
+ module Concerns
6
+ module Performable
7
+ extend ActiveSupport::Concern
8
+ include ActiveModel::Validations
9
+ include AuxiliaryRails::Concerns::Errorable
10
+
11
+ class_methods do
12
+ def call(*args)
13
+ new(*args).call
14
+ end
15
+ end
16
+
17
+ def call(options = {})
18
+ ensure_empty_status!
19
+
20
+ if options[:validate!] == true
21
+ validate!
22
+ elsif options[:validate] != false && invalid?
23
+ return failure!
24
+ end
25
+
26
+ perform
27
+
28
+ ensure_execution!
29
+ self
30
+ end
31
+
32
+ # Describes business logic.
33
+ #
34
+ # Method <b>should always</b> return <tt>success!</tt>
35
+ # or <tt>failure!</tt> methods in order pro provide further
36
+ # correct method chaining.
37
+ #
38
+ # @abstract
39
+ # @return [self]
40
+ def perform
41
+ raise NotImplementedError
42
+ end
43
+
44
+ def failure?
45
+ status?(:failure)
46
+ end
47
+
48
+ def status?(value)
49
+ ensure_execution!
50
+
51
+ performable_status == value&.to_sym
52
+ end
53
+
54
+ def success?
55
+ status?(:success)
56
+ end
57
+
58
+ # Provides ability to execude block of the code depending on
59
+ # command execution status
60
+ #
61
+ # @param status [Symol] Desired command status
62
+ # @param &_block Code to be executed if status matches
63
+ # @return [self]
64
+ def on(status, &_block)
65
+ ensure_execution!
66
+
67
+ return self unless status?(status)
68
+
69
+ yield(self) if block_given?
70
+
71
+ self
72
+ end
73
+
74
+ # Shortcut for <tt>ActiveRecord::Base.transaction</tt>
75
+ def transaction(&block)
76
+ ActiveRecord::Base.transaction(&block) if block_given?
77
+ end
78
+
79
+ protected
80
+
81
+ attr_accessor :performable_status
82
+
83
+ # @raise [AuxiliaryRails::Application::Error]
84
+ # Error happens if command contains any errors.
85
+ # @return [nil]
86
+ def ensure_empty_errors!
87
+ return if errors.empty?
88
+
89
+ error!("`#{self.class}` contains errors.")
90
+ end
91
+
92
+ def ensure_empty_status!
93
+ return if performable_status.nil?
94
+
95
+ error!("`#{self.class}` was already executed.")
96
+ end
97
+
98
+ def ensure_execution!
99
+ return if performable_status.present?
100
+
101
+ error!("`#{self.class}` was not executed yet.")
102
+ end
103
+
104
+ # Sets command's status to <tt>failure</tt>.
105
+ #
106
+ # @return [self]
107
+ def failure!(message = nil, options = {})
108
+ ensure_empty_status!
109
+
110
+ errors.add(:base, message, options) if message.present?
111
+
112
+ self.performable_status = :failure
113
+ self
114
+ end
115
+
116
+ # Sets command's status to <tt>success</tt>.
117
+ #
118
+ # @return [self]
119
+ def success!
120
+ ensure_empty_errors!
121
+ ensure_empty_status!
122
+
123
+ self.performable_status = :success
124
+ self
125
+ end
126
+ end
127
+ end
128
+ end
@@ -1,3 +1,3 @@
1
1
  module AuxiliaryRails
2
- VERSION = '0.1.6'.freeze
2
+ VERSION = '0.3.1'.freeze
3
3
  end
@@ -37,7 +37,12 @@ module AuxiliaryRails
37
37
 
38
38
  def create_api_resource_spec_file
39
39
  template 'api_resource_spec_template.rb.erb',
40
- "spec/#{api_module_path}/#{plural_file_name}_resource_spec.rb"
40
+ "spec/#{api_module_path}/resources/#{plural_file_name}_resource_spec.rb"
41
+ end
42
+
43
+ def say_instructions
44
+ say "Mount resource in #{api_module_name}:"
45
+ say " mount #{resource_class_name}"
41
46
  end
42
47
 
43
48
  private
@@ -59,5 +64,9 @@ module AuxiliaryRails
59
64
  def entity_class_name
60
65
  "#{api_module_name}::Entities::#{class_name}Entity"
61
66
  end
67
+
68
+ def resource_class_name
69
+ "#{api_module_name}::Resources::#{plural_name.camelize}Resource"
70
+ end
62
71
  end
63
72
  end
@@ -0,0 +1,44 @@
1
+ require 'rails'
2
+
3
+ module AuxiliaryRails
4
+ class CommandGenerator < ::Rails::Generators::NamedBase
5
+ desc 'Stubs out a new Command and spec.'
6
+
7
+ source_root File.expand_path('templates/commands', __dir__)
8
+
9
+ class_option :path,
10
+ type: :string,
11
+ default: 'app/commands',
12
+ desc: 'Command location'
13
+
14
+ def create_command_file
15
+ FileUtils.mkdir_p(command_file_path)
16
+ template 'command_template.rb',
17
+ "#{command_file_path}/#{command_file_name}.rb"
18
+ end
19
+
20
+ def create_command_spec_file
21
+ FileUtils.mkdir_p(command_spec_path)
22
+ template 'command_spec_template.rb',
23
+ "#{command_spec_path}/#{command_file_name}_spec.rb"
24
+ end
25
+
26
+ private
27
+
28
+ def command_class_name
29
+ "#{class_name.gsub(/Command$/, '')}Command"
30
+ end
31
+
32
+ def command_file_name
33
+ command_class_name.underscore
34
+ end
35
+
36
+ def command_file_path
37
+ options[:path]
38
+ end
39
+
40
+ def command_spec_path
41
+ command_file_path.gsub(%r{^app/}, 'spec/')
42
+ end
43
+ end
44
+ end
@@ -2,11 +2,16 @@ require 'rails'
2
2
 
3
3
  module AuxiliaryRails
4
4
  class InstallCommandsGenerator < ::Rails::Generators::Base
5
- source_root File.expand_path('templates', __dir__)
5
+ source_root File.expand_path('templates/commands', __dir__)
6
6
 
7
7
  def copy_application_command_file
8
8
  copy_file 'application_command_template.rb',
9
9
  'app/commands/application_command.rb'
10
10
  end
11
+
12
+ def copy_locale_command_file
13
+ copy_file 'commands.en_template.yml',
14
+ 'config/locales/commands.en.yml'
15
+ end
11
16
  end
12
17
  end
@@ -5,7 +5,6 @@ module AuxiliaryRails
5
5
  def install
6
6
  generate 'auxiliary_rails:install_commands'
7
7
  generate 'auxiliary_rails:install_errors'
8
- generate 'auxiliary_rails:install_rubocop --no-specify-gems'
9
8
  end
10
9
  end
11
10
  end
@@ -1,6 +1,7 @@
1
1
  module <%= api_module_name %>::Entities
2
2
  class <%= class_name %>Entity < Grape::Entity
3
- expose :id
3
+ expose :id,
4
+ documentation: { type: 'Integer' }
4
5
  # TODO: define `<%= class_name %>Entity` exposes
5
6
  expose :created_at, :updated_at
6
7
  end
@@ -1,11 +1,18 @@
1
1
  require 'rails_helper'
2
2
 
3
- RSpec.describe <%= api_module_name %>::Resources::<%= plural_name.camelize %>Resource, type: :request do
3
+ RSpec.describe <%= resource_class_name %>, type: :request do
4
4
  describe 'GET <%= api_url_path %>' do
5
5
  subject { get '<%= api_url_path %>' }
6
6
 
7
- # TODO: write some tests
8
- skip
7
+ it 'returns a list of <%= plural_name %>' do
8
+ create_list(:<%= singular_name %>, 2)
9
+
10
+ subject
11
+
12
+ expect_status :ok
13
+ expect_json_sizes 2
14
+ expect_json '0', id: <%= class_name %> %>.first.id
15
+ end
9
16
  end
10
17
 
11
18
  describe 'POST <%= api_url_path %>' do
@@ -16,17 +23,37 @@ RSpec.describe <%= api_module_name %>::Resources::<%= plural_name.camelize %>Res
16
23
  end
17
24
 
18
25
  describe 'GET <%= api_url_path %>/:id' do
19
- # TODO: write some tests
20
- skip
26
+ subject { get "<%= api_url_path %>/#{<%= singular_name %>.id}" }
27
+
28
+ let(:<%= singular_name %>) { create(:<%= singular_name %>) }
29
+
30
+ it 'returns a <%= singular_name %> by ID' do
31
+ subject
32
+
33
+ expect_status :ok
34
+ expect_json id: <%= singular_name %>.id
35
+ end
21
36
  end
22
37
 
23
38
  describe 'PUT <%= api_url_path %>/:id' do
24
- # TODO: write some tests
25
- skip
39
+ subject { put "<%= api_url_path %>/#{<%= singular_name %>.id}" }
40
+
41
+ let(:<%= singular_name %>) { create(:<%= singular_name %>) }
42
+
43
+ it 'updates a <%= singular_name %> by ID' do
44
+ skip
45
+ end
26
46
  end
27
47
 
28
48
  describe 'DELETE <%= api_url_path %>/:id' do
29
- # TODO: write some tests
30
- skip
49
+ subject { delete "<%= api_url_path %>/#{<%= singular_name %>.id}" }
50
+
51
+ let!(:<%= singular_name %>) { create(:<%= singular_name %>) }
52
+
53
+ it 'deletes a <%= singular_name %> by ID' do
54
+ expect { subject }.to change(<%= class_name %>, :count).by -1
55
+ expect_status 204
56
+ expect(response.body).to be_blank
57
+ end
31
58
  end
32
59
  end