approval 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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