approval 0.1.0 → 0.2.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 (166) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +38 -16
  3. data/Rakefile +9 -3
  4. data/lib/approval/config.rb +19 -0
  5. data/lib/approval/engine.rb +12 -0
  6. data/lib/approval/mixins/resource.rb +24 -0
  7. data/lib/approval/mixins/user.rb +36 -0
  8. data/lib/approval/mixins.rb +19 -0
  9. data/lib/approval/models/approval/comment.rb +10 -0
  10. data/lib/approval/models/approval/item.rb +60 -0
  11. data/lib/approval/models/approval/request.rb +43 -0
  12. data/lib/approval/models/approval/request_form/base.rb +37 -0
  13. data/lib/approval/models/approval/request_form/create.rb +22 -0
  14. data/lib/approval/models/approval/request_form/destroy.rb +22 -0
  15. data/lib/approval/models/approval/request_form/update.rb +23 -0
  16. data/lib/approval/models/approval/request_form.rb +4 -0
  17. data/lib/approval/models/approval/respond_form/approve.rb +19 -0
  18. data/lib/approval/models/approval/respond_form/base.rb +42 -0
  19. data/lib/approval/models/approval/respond_form/cancel.rb +16 -0
  20. data/lib/approval/models/approval/respond_form/reject.rb +18 -0
  21. data/lib/approval/models/approval/respond_form.rb +4 -0
  22. data/lib/approval/version.rb +1 -1
  23. data/lib/approval.rb +11 -3
  24. data/lib/generators/approval/USAGE +0 -0
  25. data/lib/generators/approval/install_generator.rb +45 -0
  26. data/lib/generators/approval/templates/create_approval_comments.rb.tt +14 -0
  27. data/lib/generators/approval/templates/create_approval_items.rb.tt +17 -0
  28. data/lib/generators/approval/templates/create_approval_requests.rb.tt +19 -0
  29. data/lib/generators/approval/templates/initializer.rb +4 -0
  30. data/spec/lib/config_spec.rb +10 -0
  31. data/spec/lib/version_spec.rb +7 -0
  32. data/spec/models/book_spec.rb +27 -0
  33. data/spec/models/comment_spec.rb +9 -0
  34. data/spec/models/item_spec.rb +114 -0
  35. data/spec/models/request_form/base_spec.rb +54 -0
  36. data/spec/models/request_form/create_spec.rb +23 -0
  37. data/spec/models/request_form/destroy_spec.rb +23 -0
  38. data/spec/models/request_form/update_spec.rb +23 -0
  39. data/spec/models/request_form_spec.rb +4 -0
  40. data/spec/models/request_spec.rb +40 -0
  41. data/spec/models/respond_form/approve_spec.rb +4 -0
  42. data/spec/models/respond_form/base_spec.rb +84 -0
  43. data/spec/models/respond_form/cancel_spec.rb +4 -0
  44. data/spec/models/respond_form/reject_spec.rb +4 -0
  45. data/spec/models/respond_form_spec.rb +4 -0
  46. data/spec/models/user_spec.rb +48 -0
  47. data/spec/rails/rails-4.2.9/README.rdoc +28 -0
  48. data/spec/rails/rails-4.2.9/Rakefile +6 -0
  49. data/spec/rails/rails-4.2.9/app/assets/javascripts/application.js +16 -0
  50. data/spec/rails/rails-4.2.9/app/assets/stylesheets/application.css +15 -0
  51. data/spec/rails/rails-4.2.9/app/controllers/application_controller.rb +5 -0
  52. data/spec/rails/rails-4.2.9/app/helpers/application_helper.rb +2 -0
  53. data/spec/rails/rails-4.2.9/app/models/book.rb +3 -0
  54. data/spec/rails/rails-4.2.9/app/models/user.rb +3 -0
  55. data/spec/rails/rails-4.2.9/app/views/layouts/application.html.erb +14 -0
  56. data/spec/rails/rails-4.2.9/bin/bundle +3 -0
  57. data/spec/rails/rails-4.2.9/bin/rails +4 -0
  58. data/spec/rails/rails-4.2.9/bin/rake +4 -0
  59. data/spec/rails/rails-4.2.9/bin/setup +29 -0
  60. data/spec/rails/rails-4.2.9/config/application.rb +35 -0
  61. data/spec/rails/rails-4.2.9/config/boot.rb +3 -0
  62. data/spec/rails/rails-4.2.9/config/database.yml +25 -0
  63. data/spec/rails/rails-4.2.9/config/environment.rb +8 -0
  64. data/spec/rails/rails-4.2.9/config/environments/development.rb +28 -0
  65. data/spec/rails/rails-4.2.9/config/environments/production.rb +67 -0
  66. data/spec/rails/rails-4.2.9/config/environments/test.rb +42 -0
  67. data/spec/rails/rails-4.2.9/config/initializers/approval.rb +4 -0
  68. data/spec/rails/rails-4.2.9/config/initializers/backtrace_silencers.rb +7 -0
  69. data/spec/rails/rails-4.2.9/config/initializers/cookies_serializer.rb +3 -0
  70. data/spec/rails/rails-4.2.9/config/initializers/filter_parameter_logging.rb +4 -0
  71. data/spec/rails/rails-4.2.9/config/initializers/inflections.rb +16 -0
  72. data/spec/rails/rails-4.2.9/config/initializers/mime_types.rb +4 -0
  73. data/spec/rails/rails-4.2.9/config/initializers/session_store.rb +3 -0
  74. data/spec/rails/rails-4.2.9/config/initializers/to_time_preserves_timezone.rb +10 -0
  75. data/spec/rails/rails-4.2.9/config/initializers/wrap_parameters.rb +14 -0
  76. data/spec/rails/rails-4.2.9/config/locales/en.yml +23 -0
  77. data/spec/rails/rails-4.2.9/config/routes.rb +56 -0
  78. data/spec/rails/rails-4.2.9/config/secrets.yml +22 -0
  79. data/spec/rails/rails-4.2.9/config.ru +4 -0
  80. data/spec/rails/rails-4.2.9/db/migrate/20170731151146_create_approval_requests.rb +19 -0
  81. data/spec/rails/rails-4.2.9/db/migrate/20170731151147_create_approval_comments.rb +14 -0
  82. data/spec/rails/rails-4.2.9/db/migrate/20170731151148_create_approval_items.rb +17 -0
  83. data/spec/rails/rails-4.2.9/db/migrate/20170731151149_create_users.rb +9 -0
  84. data/spec/rails/rails-4.2.9/db/migrate/20170731151150_create_books.rb +9 -0
  85. data/spec/rails/rails-4.2.9/db/schema.rb +68 -0
  86. data/spec/rails/rails-4.2.9/db/seeds.rb +7 -0
  87. data/spec/rails/rails-4.2.9/db/test.sqlite3 +0 -0
  88. data/spec/rails/rails-4.2.9/log/test.log +133 -0
  89. data/spec/rails/rails-4.2.9/public/404.html +67 -0
  90. data/spec/rails/rails-4.2.9/public/422.html +67 -0
  91. data/spec/rails/rails-4.2.9/public/500.html +66 -0
  92. data/spec/rails/rails-4.2.9/public/favicon.ico +0 -0
  93. data/spec/rails/rails-4.2.9/public/robots.txt +5 -0
  94. data/spec/rails/rails-5.1.2/README.md +24 -0
  95. data/spec/rails/rails-5.1.2/Rakefile +6 -0
  96. data/spec/rails/rails-5.1.2/app/assets/config/manifest.js +3 -0
  97. data/spec/rails/rails-5.1.2/app/assets/javascripts/application.js +15 -0
  98. data/spec/rails/rails-5.1.2/app/assets/javascripts/cable.js +13 -0
  99. data/spec/rails/rails-5.1.2/app/assets/stylesheets/application.css +15 -0
  100. data/spec/rails/rails-5.1.2/app/channels/application_cable/channel.rb +4 -0
  101. data/spec/rails/rails-5.1.2/app/channels/application_cable/connection.rb +4 -0
  102. data/spec/rails/rails-5.1.2/app/controllers/application_controller.rb +3 -0
  103. data/spec/rails/rails-5.1.2/app/helpers/application_helper.rb +2 -0
  104. data/spec/rails/rails-5.1.2/app/jobs/application_job.rb +2 -0
  105. data/spec/rails/rails-5.1.2/app/mailers/application_mailer.rb +4 -0
  106. data/spec/rails/rails-5.1.2/app/models/application_record.rb +3 -0
  107. data/spec/rails/rails-5.1.2/app/models/book.rb +3 -0
  108. data/spec/rails/rails-5.1.2/app/models/user.rb +3 -0
  109. data/spec/rails/rails-5.1.2/app/views/layouts/application.html.erb +14 -0
  110. data/spec/rails/rails-5.1.2/app/views/layouts/mailer.html.erb +13 -0
  111. data/spec/rails/rails-5.1.2/app/views/layouts/mailer.text.erb +1 -0
  112. data/spec/rails/rails-5.1.2/bin/bundle +3 -0
  113. data/spec/rails/rails-5.1.2/bin/rails +4 -0
  114. data/spec/rails/rails-5.1.2/bin/rake +4 -0
  115. data/spec/rails/rails-5.1.2/bin/setup +38 -0
  116. data/spec/rails/rails-5.1.2/bin/update +29 -0
  117. data/spec/rails/rails-5.1.2/bin/yarn +11 -0
  118. data/spec/rails/rails-5.1.2/config/application.rb +31 -0
  119. data/spec/rails/rails-5.1.2/config/boot.rb +3 -0
  120. data/spec/rails/rails-5.1.2/config/cable.yml +10 -0
  121. data/spec/rails/rails-5.1.2/config/database.yml +25 -0
  122. data/spec/rails/rails-5.1.2/config/environment.rb +8 -0
  123. data/spec/rails/rails-5.1.2/config/environments/development.rb +47 -0
  124. data/spec/rails/rails-5.1.2/config/environments/production.rb +83 -0
  125. data/spec/rails/rails-5.1.2/config/environments/test.rb +42 -0
  126. data/spec/rails/rails-5.1.2/config/initializers/application_controller_renderer.rb +6 -0
  127. data/spec/rails/rails-5.1.2/config/initializers/approval.rb +4 -0
  128. data/spec/rails/rails-5.1.2/config/initializers/backtrace_silencers.rb +7 -0
  129. data/spec/rails/rails-5.1.2/config/initializers/cookies_serializer.rb +5 -0
  130. data/spec/rails/rails-5.1.2/config/initializers/filter_parameter_logging.rb +4 -0
  131. data/spec/rails/rails-5.1.2/config/initializers/inflections.rb +16 -0
  132. data/spec/rails/rails-5.1.2/config/initializers/mime_types.rb +4 -0
  133. data/spec/rails/rails-5.1.2/config/initializers/wrap_parameters.rb +14 -0
  134. data/spec/rails/rails-5.1.2/config/locales/en.yml +33 -0
  135. data/spec/rails/rails-5.1.2/config/puma.rb +56 -0
  136. data/spec/rails/rails-5.1.2/config/routes.rb +3 -0
  137. data/spec/rails/rails-5.1.2/config/secrets.yml +32 -0
  138. data/spec/rails/rails-5.1.2/config.ru +5 -0
  139. data/spec/rails/rails-5.1.2/db/migrate/20170731101409_create_approval_requests.rb +19 -0
  140. data/spec/rails/rails-5.1.2/db/migrate/20170731101410_create_approval_comments.rb +14 -0
  141. data/spec/rails/rails-5.1.2/db/migrate/20170731101411_create_approval_items.rb +17 -0
  142. data/spec/rails/rails-5.1.2/db/migrate/20170731101412_create_users.rb +9 -0
  143. data/spec/rails/rails-5.1.2/db/migrate/20170731101413_create_books.rb +9 -0
  144. data/spec/rails/rails-5.1.2/db/schema.rb +64 -0
  145. data/spec/rails/rails-5.1.2/db/seeds.rb +7 -0
  146. data/spec/rails/rails-5.1.2/db/test.sqlite3 +0 -0
  147. data/spec/rails/rails-5.1.2/log/test.log +3485 -0
  148. data/spec/rails/rails-5.1.2/package.json +5 -0
  149. data/spec/rails/rails-5.1.2/public/404.html +67 -0
  150. data/spec/rails/rails-5.1.2/public/422.html +67 -0
  151. data/spec/rails/rails-5.1.2/public/500.html +66 -0
  152. data/spec/rails/rails-5.1.2/public/apple-touch-icon-precomposed.png +0 -0
  153. data/spec/rails/rails-5.1.2/public/apple-touch-icon.png +0 -0
  154. data/spec/rails/rails-5.1.2/public/favicon.ico +0 -0
  155. data/spec/rails/rails-5.1.2/public/robots.txt +1 -0
  156. data/spec/spec_helper.rb +117 -0
  157. data/spec/support/rails_template.rb +24 -0
  158. metadata +287 -43
  159. data/.gitignore +0 -12
  160. data/.rspec +0 -2
  161. data/.travis.yml +0 -5
  162. data/Gemfile +0 -4
  163. data/LICENSE.txt +0 -21
  164. data/approval.gemspec +0 -26
  165. data/bin/console +0 -14
  166. data/bin/setup +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: '09dc3ed589e3cbfc30969832927f5f3e08560e2f'
4
- data.tar.gz: 5941a088abc4e8603a56112e4105a2e4a11f0d93
3
+ metadata.gz: ec9655c12fc1e790936193dfe16b35b3e2352dd7
4
+ data.tar.gz: 4ab7e5d9dd38039a91391cfdc6c0f1665938c44f
5
5
  SHA512:
6
- metadata.gz: 78f368c57bf25e07f8f4151a056802c530039cade42d93055606bbf5691230e8425bd53f856fa5ded3b204558a5a2e921192e573c1e7020771894180434ca022
7
- data.tar.gz: 3b2f2bf914d919d7c1c2e0509bddddeb927f7449ba7a5e9c30c7ef8b68764df255aa4975a4b9986119fae93aa54f41ab5c760b7c5f1a9e02625a0bf5f497677d
6
+ metadata.gz: ea6dea2cfe991bf86765c405926af2ae6d13d5422fb2f115c11907a29166edd729cb061d58a2bf40cf73c712927526032fa9369a2afaf8bc9a93b0a4a1a90328
7
+ data.tar.gz: c6b4fcdf006136b9288cceb8c2b67291873b2e9f41e378cc60ed46c33e0752fa5e5107d27801e706fff9f7d67ee0718feb312d942f45deb37946d62cc0a136bd
data/README.md CHANGED
@@ -1,38 +1,60 @@
1
1
  # Approval
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/approval`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
3
+ :ok_woman::no_good:Approval workflow for Rails
6
4
 
7
5
  ## Installation
8
6
 
9
- Add this line to your application's Gemfile:
10
-
11
7
  ```ruby
12
8
  gem 'approval'
13
9
  ```
14
10
 
15
- And then execute:
11
+ Optionally, you can run the generator, which will set up approval models with some useful defaults for you:
12
+
13
+ ```bash
14
+ $ bin/rails g approval:install
15
+ ```
16
16
 
17
- $ bundle
17
+ ## Usage
18
18
 
19
- Or install it yourself as:
19
+ ### Prepare
20
20
 
21
- $ gem install approval
21
+ Call `acts_as_approval_user` in your resource of user model (`User`, `AdminUser`, `Member` etc):
22
22
 
23
- ## Usage
23
+ ```ruby
24
+ class User
25
+ acts_as_approval_user
26
+ end
27
+ ```
24
28
 
25
- TODO: Write usage instructions here
29
+ Call `acts_as_approval_resource` in your resource:
26
30
 
27
- ## Development
31
+ ```ruby
32
+ class Book < ApplicationRecord
33
+ acts_as_approval_resource
34
+ end
35
+ ```
36
+
37
+ ### Request
38
+
39
+ ```ruby
40
+ staff = User.find_or_create_by(email: "staff@example.com")
41
+ book = 10.times.map { |n| Book.new(name: "my_book_#{n}") }
42
+ request = staff.request_for_create(records, reason: "something")
43
+ request.save!
44
+ ```
28
45
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
46
+ You send request, but resources aren't created.
30
47
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
48
+ ### Respond
32
49
 
33
- ## Contributing
50
+ ```ruby
51
+ admin = User.find_or_create_by(email: "admin@example.com")
52
+ request = Approval::Request.first
53
+ respond = admin.approve_request(request, reason: "something")
54
+ respond.save!
55
+ ```
34
56
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/yhirano55/approval.
57
+ Then resources are created, if respond user have approved the request.
36
58
 
37
59
  ## License
38
60
 
data/Rakefile CHANGED
@@ -1,6 +1,12 @@
1
1
  require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
2
 
4
- RSpec::Core::RakeTask.new(:spec)
3
+ desc "Creates a test rails app for the specs to run against"
4
+ task :setup do
5
+ require "rails/version"
6
+ system("mkdir spec/rails") unless File.exist?("spec/rails")
7
+ system "bundle exec rails new spec/rails/rails-#{Rails::VERSION::STRING} -m spec/support/rails_template.rb -T -B --skip-spring --skip-listen --skip-sprockets"
8
+ end
5
9
 
6
- task :default => :spec
10
+ require "rspec/core/rake_task"
11
+ RSpec::Core::RakeTask.new(:spec)
12
+ task default: :spec
@@ -0,0 +1,19 @@
1
+ module Approval
2
+ class Config
3
+ attr_accessor(
4
+ :comment_maximum,
5
+ :user_class_name,
6
+ :permit_to_respond_to_own_request,
7
+ )
8
+
9
+ def initialize
10
+ @comment_maximum = 2000
11
+ @user_class_name = "User"
12
+ @permit_to_respond_to_own_request = false
13
+ end
14
+
15
+ def permit_to_respond_to_own_request?
16
+ !!permit_to_respond_to_own_request
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,12 @@
1
+ module Approval
2
+ class Engine < ::Rails::Engine
3
+ paths["app/models"] << "lib/approval/models"
4
+
5
+ initializer "approval" do
6
+ ActiveSupport.on_load :active_record do
7
+ require "approval/mixins"
8
+ ActiveRecord::Base.send :include, ::Approval::Mixins
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,24 @@
1
+ module Approval
2
+ module Mixins
3
+ module Resource
4
+ extend ActiveSupport::Concern
5
+
6
+ DEFAULT_IGNORE_FIELDS = %w[id created_at updated_at].freeze
7
+
8
+ included do
9
+ class_attribute :approval_ignore_fields
10
+ has_many :approval_items, class_name: :"Approval::Item", as: :resource
11
+ end
12
+
13
+ class_methods do
14
+ def append_ignore_fields(ignore_fields = [])
15
+ self.approval_ignore_fields = DEFAULT_IGNORE_FIELDS.dup.concat(ignore_fields).map(&:to_s).uniq
16
+ end
17
+ end
18
+
19
+ def params_for_approval
20
+ attributes.except(*approval_ignore_fields).compact
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,36 @@
1
+ module Approval
2
+ module Mixins
3
+ module User
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ has_many :approval_requests, class_name: :"Approval::Request", foreign_key: :request_user_id
8
+ has_many :approval_comments, class_name: :"Approval::Comment", foreign_key: :user_id
9
+ end
10
+
11
+ def request_for_create(records, reason:)
12
+ Approval::RequestForm::Create.new(user: self, reason: reason, records: records)
13
+ end
14
+
15
+ def request_for_update(records, reason:)
16
+ Approval::RequestForm::Update.new(user: self, reason: reason, records: records)
17
+ end
18
+
19
+ def request_for_destroy(records, reason:)
20
+ Approval::RequestForm::Destroy.new(user: self, reason: reason, records: records)
21
+ end
22
+
23
+ def cancel_request(request, reason:)
24
+ Approval::RespondForm::Cancel.new(user: self, reason: reason, request: request)
25
+ end
26
+
27
+ def approve_request(request, reason:)
28
+ Approval::RespondForm::Approve.new(user: self, reason: reason, request: request)
29
+ end
30
+
31
+ def reject_request(request, reason:)
32
+ Approval::RespondForm::Reject.new(user: self, reason: reason, request: request)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,19 @@
1
+ module Approval
2
+ module Mixins
3
+ extend ActiveSupport::Concern
4
+
5
+ class_methods do
6
+ def acts_as_approval_resource(ignore_fields: [])
7
+ include ::Approval::Mixins::Resource
8
+ append_ignore_fields(ignore_fields)
9
+ end
10
+
11
+ def acts_as_approval_user
12
+ include ::Approval::Mixins::User
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ require "approval/mixins/resource"
19
+ require "approval/mixins/user"
@@ -0,0 +1,10 @@
1
+ module Approval
2
+ class Comment < ::ActiveRecord::Base
3
+ self.table_name_prefix = "approval_".freeze
4
+
5
+ belongs_to :request, class_name: :"Approval::Request", inverse_of: :comments
6
+ belongs_to :user, class_name: Approval.config.user_class_name
7
+
8
+ validates :content, presence: true, length: { maximum: Approval.config.comment_maximum }
9
+ end
10
+ end
@@ -0,0 +1,60 @@
1
+ module Approval
2
+ class Item < ::ActiveRecord::Base
3
+ class UnexistResource < StandardError; end
4
+
5
+ EVENTS = %w[create update destroy].freeze
6
+
7
+ self.table_name_prefix = "approval_".freeze
8
+
9
+ belongs_to :request, class_name: :"Approval::Request", inverse_of: :items
10
+ belongs_to :resource, polymorphic: true, optional: true
11
+
12
+ serialize :params, Hash
13
+
14
+ with_options presence: true do
15
+ validates :resource_id, unless: :create_event?
16
+ validates :resource_type
17
+ validates :event, inclusion: { in: EVENTS }
18
+ end
19
+
20
+ validate :ensure_resource_be_valid
21
+
22
+ EVENTS.each do |event_name|
23
+ define_method "#{event_name}_event?" do
24
+ event_name.to_s == event.to_s
25
+ end
26
+ end
27
+
28
+ def apply
29
+ case event
30
+ when "create"
31
+ resource_model.create!(params).tap do |created_resource|
32
+ update!(resource_id: created_resource.id)
33
+ end
34
+ when "update"
35
+ raise UnexistResource unless resource
36
+ resource.update!(params)
37
+ when "destroy"
38
+ raise UnexistResource unless resource
39
+ resource.destroy
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def resource_model
46
+ @_resource_model ||= resource_type.to_s.safe_constantize
47
+ end
48
+
49
+ def ensure_resource_be_valid
50
+ return if resource_model.nil? || destroy_event?
51
+ record = resource_model.new(params || {})
52
+
53
+ unless record.valid?
54
+ record.errors.full_messages.each do |_message|
55
+ errors.add(:base, "message")
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,43 @@
1
+ module Approval
2
+ class Request < ::ActiveRecord::Base
3
+ self.table_name_prefix = "approval_".freeze
4
+
5
+ with_options class_name: Approval.config.user_class_name do
6
+ belongs_to :request_user
7
+ belongs_to :respond_user, optional: true
8
+ end
9
+
10
+ with_options dependent: :destroy, inverse_of: :request do
11
+ has_many :comments, class_name: "::Approval::Comment"
12
+ has_many :items, class_name: "::Approval::Item"
13
+ end
14
+
15
+ enum state: { pending: 0, cancelled: 1, approved: 2, rejected: 3 }
16
+
17
+ scope :recently, -> { order(id: :desc) }
18
+
19
+ with_options presence: true do
20
+ validates :state
21
+ validates :request_user
22
+ validates :respond_user, unless: :pending?
23
+ validates :comments
24
+ validates :items
25
+ end
26
+
27
+ validates_associated :comments
28
+ validates_associated :items
29
+
30
+ validate :ensure_state_was_pending
31
+
32
+ before_create do
33
+ self.requested_at = Time.current
34
+ end
35
+
36
+ private
37
+
38
+ def ensure_state_was_pending
39
+ return unless persisted?
40
+ errors.add(:base, :already_performed) if state_was != "pending"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,37 @@
1
+ module Approval
2
+ module RequestForm
3
+ class Base
4
+ include ::ActiveModel::Model
5
+
6
+ attr_accessor :user, :reason, :records
7
+
8
+ def initialize(user:, reason:, records:)
9
+ @user = user
10
+ @reason = reason
11
+ @records = records
12
+ end
13
+
14
+ with_options presence: true do
15
+ validates :user
16
+ validates :reason, length: { maximum: Approval.config.comment_maximum }
17
+ validates :records
18
+ end
19
+
20
+ def save
21
+ return false unless valid?
22
+ prepare(&:save)
23
+ end
24
+
25
+ def save!
26
+ raise ::ActiveRecord::RecordInvalid unless valid?
27
+ prepare(&:save!)
28
+ end
29
+
30
+ private
31
+
32
+ def prepare
33
+ raise NotImplementedError, "you must implement #{self.class}##{__method__}"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,22 @@
1
+ module Approval
2
+ module RequestForm
3
+ class Create < Base
4
+ private
5
+
6
+ def prepare
7
+ ::ActiveRecord::Base.transaction do
8
+ request = user.approval_requests.new
9
+ request.comments.new(user: user, content: reason)
10
+ Array(records).each do |record|
11
+ request.items.new(
12
+ event: "create",
13
+ resource_type: record.class.to_s,
14
+ params: record.params_for_approval,
15
+ )
16
+ end
17
+ yield(request)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ module Approval
2
+ module RequestForm
3
+ class Destroy < Base
4
+ private
5
+
6
+ def prepare
7
+ ::ActiveRecord::Base.transaction do
8
+ request = user.approval_requests.new
9
+ request.comments.new(user: user, content: reason)
10
+ Array(records).each do |record|
11
+ request.items.new(
12
+ event: "destroy",
13
+ resource_type: record.class.to_s,
14
+ resource_id: record.id,
15
+ )
16
+ end
17
+ yield(request)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ module Approval
2
+ module RequestForm
3
+ class Update < Base
4
+ private
5
+
6
+ def prepare
7
+ ::ActiveRecord::Base.transaction do
8
+ request = user.approval_requests.new
9
+ request.comments.new(user: user, content: reason)
10
+ Array(records).each do |record|
11
+ request.items.new(
12
+ event: "update",
13
+ resource_type: record.class.to_s,
14
+ resource_id: record.id,
15
+ params: record.params_for_approval,
16
+ )
17
+ end
18
+ yield(request)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,4 @@
1
+ module Approval
2
+ module RequestForm
3
+ end
4
+ end
@@ -0,0 +1,19 @@
1
+ module Approval
2
+ module RespondForm
3
+ class Approve < Base
4
+ validate :ensure_user_cannot_respond_to_my_request
5
+
6
+ private
7
+
8
+ def prepare
9
+ ::ActiveRecord::Base.transaction do
10
+ request.lock!
11
+ request.assign_attributes(state: :approved, approved_at: Time.current, respond_user: user)
12
+ request.comments.new(user: user, content: reason)
13
+ request.items.each(&:apply)
14
+ yield(request)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,42 @@
1
+ module Approval
2
+ module RespondForm
3
+ class Base
4
+ include ::ActiveModel::Model
5
+
6
+ attr_accessor :user, :reason, :request
7
+
8
+ def initialize(user:, reason:, request:)
9
+ @user = user
10
+ @reason = reason
11
+ @request = request
12
+ end
13
+
14
+ with_options presence: true do
15
+ validates :user
16
+ validates :reason, length: { maximum: Approval.config.comment_maximum }
17
+ validates :request
18
+ end
19
+
20
+ def save
21
+ return false unless valid?
22
+ prepare(&:save)
23
+ end
24
+
25
+ def save!
26
+ raise ::ActiveRecord::RecordInvalid unless valid?
27
+ prepare(&:save!)
28
+ end
29
+
30
+ private
31
+
32
+ def prepare
33
+ raise NotImplementedError, "you must implement #{self.class}##{__method__}"
34
+ end
35
+
36
+ def ensure_user_cannot_respond_to_my_request
37
+ return if Approval.config.permit_to_respond_to_own_request?
38
+ errors.add(:user, :invalid) if user == request.request_user
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,16 @@
1
+ module Approval
2
+ module RespondForm
3
+ class Cancel < Base
4
+ private
5
+
6
+ def prepare
7
+ ::ActiveRecord::Base.transaction do
8
+ request.lock!
9
+ request.assign_attributes(state: :cancelled, cancelled_at: Time.current, respond_user: user)
10
+ request.comments.new(user: user, content: reason)
11
+ yield(request)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ module Approval
2
+ module RespondForm
3
+ class Reject < Base
4
+ validate :ensure_user_cannot_respond_to_my_request
5
+
6
+ private
7
+
8
+ def prepare
9
+ ::ActiveRecord::Base.transaction do
10
+ request.lock!
11
+ request.assign_attributes(state: :rejected, rejected_at: Time.current, respond_user: user)
12
+ request.comments.new(user: user, content: reason)
13
+ yield(request)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,4 @@
1
+ module Approval
2
+ module RespondForm
3
+ end
4
+ end
@@ -1,3 +1,3 @@
1
1
  module Approval
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0".freeze
3
3
  end
data/lib/approval.rb CHANGED
@@ -1,5 +1,13 @@
1
- require "approval/version"
2
-
3
1
  module Approval
4
- # Your code goes here...
2
+ def self.config
3
+ @_config ||= Config.new
4
+ end
5
+
6
+ def self.configure
7
+ yield config
8
+ end
5
9
  end
10
+
11
+ require "approval/config"
12
+ require "approval/engine" if defined?(::Rails)
13
+ require "approval/version"
File without changes
@@ -0,0 +1,45 @@
1
+ require "rails/generators"
2
+ require "rails/generators/active_record"
3
+
4
+ module Approval
5
+ class InstallGenerator < ::Rails::Generators::Base
6
+ include ::Rails::Generators::Migration
7
+ source_root File.expand_path("templates", __dir__)
8
+
9
+ class << self
10
+ def next_migration_number(dirname)
11
+ ::ActiveRecord::Generators::Base.next_migration_number(dirname)
12
+ end
13
+ end
14
+
15
+ def create_migration_file
16
+ add_migration_file("create_approval_requests")
17
+ add_migration_file("create_approval_comments")
18
+ add_migration_file("create_approval_items")
19
+ end
20
+
21
+ def create_initializer
22
+ copy_file("initializer.rb", "config/initializers/approval.rb")
23
+ end
24
+
25
+ private
26
+
27
+ def add_migration_file(template)
28
+ migration_dir = File.expand_path("db/migrate")
29
+
30
+ if self.class.migration_exists?(migration_dir, template)
31
+ ::Kernel.warn "Migration already exists: #{template}"
32
+ else
33
+ migration_template("#{template}.rb.tt", "db/migrate/#{template}.rb", migration_version: migration_version)
34
+ end
35
+ end
36
+
37
+ def migration_version
38
+ major = ActiveRecord::VERSION::MAJOR
39
+
40
+ if major >= 5
41
+ "[#{major}.#{ActiveRecord::VERSION::MINOR}]"
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,14 @@
1
+ class CreateApprovalComments < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table :approval_comments do |t|
4
+ t.integer :request_id, null: false
5
+ t.integer :user_id, null: false
6
+ t.text :content, null: false
7
+
8
+ t.timestamps
9
+
10
+ t.index :request_id
11
+ t.index :user_id
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ class CreateApprovalItems < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ create_table :approval_items do |t|
4
+ t.integer :request_id, null: false
5
+ t.integer :resource_id
6
+ t.string :resource_type, null: false
7
+ t.string :event, null: false
8
+ t.text :params
9
+
10
+ t.timestamps
11
+
12
+ t.index :request_id
13
+ end
14
+
15
+ add_index :approval_items, [:resource_id, :resource_type]
16
+ end
17
+ end