protected_record 0.0.0 → 0.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5936b33d95c7b170f1c1f0a90dbb94ba3d5cddc5
4
+ data.tar.gz: 4d6bbe694afa07f22a4b04e93fc6dc13fc9819a6
5
+ SHA512:
6
+ metadata.gz: fd08a85e169ce3ebc9e47f0217fd031d90ef01b1330640113e6b39c1d57a071cb0de107863b5269e2b3904716976131e19b4c974a64773eede60efa33b6d30e4
7
+ data.tar.gz: f5829512aba3077dc008b4fa47145ecda4c3f54713b50f679ef4f2d7f42da09c3043df93f1db0e1c3864e5a1b7fa6ba44444b05a40d2a771dd7c15147dde9f64
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ protected_record
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.0.0-p0
data/README.md CHANGED
@@ -1,7 +1,23 @@
1
- Just include in your tracked models:
1
+ # protected_record
2
+ ## Description
3
+ protected_record will prevent changes to attributes you specify as protected. Any attempted change will be logged as a ProtectedRecord::ChangeRequest::Record.
4
+ If any changes are allowed through the filter, protected_record will create a ProtectedRecord::ChangeLog::Record to log who changed what, and for which record.
5
+ ProtectedRecord is opt-in only. In order to update with protection, use the following:
6
+
7
+ result = ProtectedRecord::UseCase::Update.new({
8
+ params: visit_params,
9
+ protected_record: @patient_visit,
10
+ user: current_user,
11
+ protected_keys: %w{ visit_date do_not_resuscitate }
12
+ }).execute!
13
+
14
+
15
+ ## Usage
16
+
17
+ To get started, just include in your tracked models:
2
18
 
3
19
  include ProtectedRecord::ChangeRequest::Changeling
4
- include ProtectedRecord::ChangeLog::Changeling
20
+ include ProtectedRecord::ChangeLog::Changeling
5
21
 
6
22
  and include in your user model:
7
23
 
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.pattern = 'test/**/*_test.rb'
6
+ t.libs.push 'test'
7
+ end
8
+
9
+ task :default => :test
@@ -1,7 +1,8 @@
1
+ require 'active_record'
1
2
  module ProtectedRecord
2
3
  module ChangeLog
3
4
  class Record < ActiveRecord::Base
4
- self.table_name = "change_log_records"
5
+ self.table_name = "protected_record_change_log_records"
5
6
  attr_accessible :recordable, :user
6
7
  belongs_to :recordable, polymorphic: true
7
8
  belongs_to :user, class_name: "User", foreign_key: :user_id
@@ -1,7 +1,8 @@
1
+ require 'active_record'
1
2
  module ProtectedRecord
2
3
  module ChangeRequest
3
4
  class Record < ActiveRecord::Base
4
- self.table_name = 'change_request_records'
5
+ self.table_name = "protected_record_change_request_records"
5
6
  attr_accessible :recordable, :user
6
7
 
7
8
  belongs_to :recordable, polymorphic: true
@@ -0,0 +1,34 @@
1
+ module ProtectedRecord
2
+ module UseCase
3
+ module ChangeFilter
4
+ class Create < PayDirt::Base
5
+ def initialize(options)
6
+ load_options(:protected_keys, :protected_record, options) and validate_state
7
+ end
8
+
9
+ def execute!
10
+ revert_protected_attrs
11
+
12
+ return PayDirt::Result.new(data: { change_request_record: @protected_record })
13
+ end
14
+
15
+ private
16
+ def revert_protected_attrs
17
+ @protected_keys.each do |key|
18
+ if @protected_record.send("#{key.to_s}_changed?")
19
+ @protected_record.send("#{key.to_s}=", @protected_record.send("#{key.to_s}_was"))
20
+ end
21
+ end
22
+ end
23
+
24
+ protected
25
+ def validate_state
26
+ # If there are no changes, there's no need to do any filtering
27
+ if !@protected_record.changes.present?
28
+ raise ActiveRecord::ActiveRecordError.new(':protected_record not dirty')
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,41 @@
1
+ module ProtectedRecord
2
+ module UseCase
3
+ module ChangeLog
4
+ class Create < PayDirt::Base
5
+ def initialize(options)
6
+ options = {
7
+ record_class: ::ProtectedRecord::ChangeLog::Record
8
+ }.merge!(options) if !options.has_key?(:record_class)
9
+
10
+ load_options(:record_class, :user, :changed_object, options) and validate_state
11
+ end
12
+
13
+ def execute!
14
+ initialize_change_log_record
15
+
16
+ if @record.save
17
+ return PayDirt::Result.new(data: { change_log_record: @record })
18
+ else
19
+ return PayDirt::Result.new(data: { change_log_record: @record }, success: false)
20
+ end
21
+ end
22
+
23
+ private
24
+ def initialize_change_log_record
25
+ @record = @record_class.new
26
+ @record.user = @user
27
+ @record.recordable = @changed_object
28
+ @record.observed_changes = ActiveSupport::JSON.encode(@changed_object.previous_changes)
29
+ end
30
+
31
+ protected
32
+ def validate_state
33
+ # What are we doing here, if not logging a change that has already happened?
34
+ if !@changed_object.previous_changes.present?
35
+ raise ActiveRecord::ActiveRecordError.new(':changed_object has no previous_changes')
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,60 @@
1
+ module ProtectedRecord
2
+ module UseCase
3
+ module ChangeRequest
4
+ class Create < PayDirt::Base
5
+ def initialize(options)
6
+ options = {
7
+ record_class: ::ProtectedRecord::ChangeRequest::Record
8
+ }.merge!(options) if !options.has_key?(:record_class)
9
+
10
+ load_options(:protected_keys, :record_class, :user, :protected_record, options) and validate_state
11
+ end
12
+
13
+ def execute!
14
+ return PayDirt::Result.new(data: {}, success: true) if !requested_changes.present?
15
+
16
+ initialize_change_request_record
17
+
18
+ if @record.save
19
+ return PayDirt::Result.new(data: { change_request_record: @record })
20
+ else
21
+ return PayDirt::Result.new(data: { change_request_record: @record }, success: false)
22
+ end
23
+ end
24
+
25
+ private
26
+ def initialize_change_request_record
27
+ @record = @record_class.new
28
+ @record.user = @user
29
+ @record.recordable = @protected_record
30
+ @record.requested_changes = ActiveSupport::JSON.encode(requested_changes)
31
+ end
32
+
33
+ def requested_changes
34
+ @dirty_object.changes.select do |key|
35
+ @protected_keys.map(&:to_s).include? key.to_s
36
+ end
37
+ end
38
+
39
+ def revert_protected_attrs
40
+ @protected_keys.each do |key|
41
+ if @protected_record.send("#{key.to_s}_changed?")
42
+ @protected_record.send("#{key.to_s}=", @protected_record.send("#{key.to_s}_was"))
43
+ end
44
+ end
45
+
46
+ raise if @protected_keys.any? { |key| @protected_record.send("#{key}_changed?") }
47
+ end
48
+
49
+ protected
50
+ def validate_state
51
+ # If there are no changes, there's no need to make a request
52
+ if !@dirty_object.changes.present?
53
+ raise ActiveRecord::ActiveRecordError.new(':dirty_object not dirty')
54
+ end
55
+ end
56
+
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,89 @@
1
+ module ProtectedRecord
2
+ module UseCase
3
+ class Update < PayDirt::Base
4
+ def initialize(options)
5
+ # Defaults
6
+ options = {
7
+ change_request: UseCase::ChangeRequest::Create,
8
+ change_filter: UseCase::ChangeFilter::Create,
9
+ change_log: UseCase::ChangeLog::Create
10
+ }.merge!(options)
11
+
12
+ load_options(:params, :protected_record, :change_request, :change_log, :change_filter, :protected_keys, :user, options)
13
+ validate_state
14
+ end
15
+
16
+ def execute!
17
+ form_change_request
18
+
19
+ # We are successful if all changes have been applied
20
+ if !@protected_record.changes.present?
21
+ return UseCase::Result.new(data: { updated: @protected_record })
22
+ else
23
+ return UseCase::Result.new({
24
+ data: { remaining_changes: @protected_record.changes },
25
+ success: false
26
+ })
27
+ end
28
+ end
29
+
30
+ private
31
+ def form_change_request
32
+ @protected_record.attributes = @params
33
+
34
+ # We allow any changes on creation
35
+ unless @protected_record.id_was.nil?
36
+ request_result = @change_request.new({
37
+ protected_keys: @protected_keys,
38
+ protected_record: @protected_record,
39
+ user: @user
40
+ }).execute!
41
+
42
+ if request_result.successful?
43
+ revert_protected_attributes ||
44
+ save_protected_record
45
+ end
46
+ end
47
+ end
48
+
49
+ def save_protected_record
50
+ @protected_record.save
51
+ log_changes
52
+ end
53
+
54
+ def revert_protected_attributes
55
+ revert_result = @change_filter.new({
56
+ protected_keys: @protected_keys,
57
+ protected_record: @protected_record
58
+ }).execute!
59
+
60
+ revert_result.successful? ? return : raise
61
+ end
62
+
63
+ def log_changes
64
+ log_result = @change_log.new(user: @user, changed_object: @protected_record).execute!
65
+
66
+ result.successful? ? return : raise
67
+ end
68
+
69
+ protected
70
+ def validate_state
71
+ # We expect some keys
72
+ if !@protected_keys.kind_of?(Array)
73
+ raise TypeError.new(':protected_keys not kind of Array')
74
+ end
75
+
76
+ # The keys should respond to to_s
77
+ if @protected_keys.any? {|el| !el.respond_to?("to_s") }
78
+ raise TypeError.new('All :protected_keys should respond to #to_s')
79
+ end
80
+
81
+ # The dirty_object should respond to all keys (as methods)
82
+ @protected_keys.each do |key|
83
+ error = ActiveRecord::ActiveRecordError.new(":dirty_object must respond to #{key.to_s}")
84
+ raise error unless @protected_record.respond_to?(key.to_s)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,5 @@
1
+ module ProtectedRecord
2
+ module UseCase
3
+ require "pay_dirt"
4
+ end
5
+ end
@@ -1,3 +1,3 @@
1
1
  module ProtectedRecord
2
- VERSION = "0.0.0"
2
+ VERSION = "0.0.1"
3
3
  end
@@ -1,5 +1,3 @@
1
- require "protected_record/use_case/base"
2
-
3
1
  Dir.glob(File.join(File.dirname(__FILE__), '/**/*.rb')) do |c|
4
2
  require(c)
5
3
  end
@@ -19,8 +19,9 @@ Gem::Specification.new do |s|
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
20
  s.require_paths = ["lib"]
21
21
 
22
+ s.add_runtime_dependency "pay_dirt"
23
+ s.add_runtime_dependency "activerecord"
22
24
  s.add_development_dependency "minitest"
23
-
24
25
  s.add_development_dependency "rake"
25
26
  s.add_development_dependency "pry"
26
27
  end
@@ -0,0 +1,134 @@
1
+ require "minitest_helper"
2
+ require "active_record"
3
+ require "pry"
4
+
5
+ describe ProtectedRecord::UseCase::ChangeFilter::Create do
6
+ describe "new" do
7
+ before do
8
+ class TestCase
9
+ include ActiveModel::Dirty
10
+
11
+ define_attribute_methods [:knowledge, :power]
12
+
13
+ def knowledge
14
+ @knowledge
15
+ end
16
+
17
+ def power
18
+ @power
19
+ end
20
+
21
+ def knowledge=(val)
22
+ knowledge_will_change! unless val == @knowledge
23
+ @knowledge = val
24
+ end
25
+
26
+ def power=(val)
27
+ power_will_change! unless val == @power
28
+ @power = val
29
+ end
30
+
31
+ def save
32
+ @previously_changed = changes
33
+ @changed_attributes.clear
34
+ end
35
+
36
+ def initialize(attributes = {})
37
+ attributes.each do |name, value|
38
+ send("#{name}=", value)
39
+ end
40
+ end
41
+ #####
42
+ end
43
+
44
+ @subject = ProtectedRecord::UseCase::ChangeFilter::Create
45
+
46
+ @protected_record = TestCase.new(knowledge: "power", power: "money")
47
+ @protected_record.save
48
+ end
49
+
50
+ it "initializes with required options" do
51
+ @protected_record.knowledge="growth"
52
+ @protected_record.power="knowledge"
53
+
54
+ @protected_keys = %w{ power }
55
+ @subject.new(protected_record: @protected_record, protected_keys: @protected_keys)
56
+ end
57
+
58
+ it "fails to initialize without :protected_record" do
59
+ begin
60
+ @subject.new(protected_keys: @protected_keys)
61
+ rescue => e
62
+ e.must_be_kind_of RuntimeError
63
+ end
64
+ end
65
+
66
+ it "fails to initialize without :protected_keys" do
67
+ begin
68
+ @subject.new(protected_record: @protected_record)
69
+ rescue => e
70
+ e.must_be_kind_of RuntimeError
71
+ end
72
+ end
73
+
74
+ it "returns a result" do
75
+ @protected_record.knowledge="growth"
76
+ @protected_record.power="knowledge"
77
+
78
+ @protected_keys = %w{ power }
79
+ @subject.new({
80
+ protected_record: @protected_record,
81
+ protected_keys: @protected_keys
82
+ }).execute!.must_be_kind_of(PayDirt::Result)
83
+ end
84
+
85
+ it "allows unprotected changes" do
86
+ @protected_keys = %w{}
87
+
88
+ @protected_record.knowledge="growth"
89
+ @protected_record.power="knowledge"
90
+
91
+ result = @subject.new({
92
+ protected_record: @protected_record,
93
+ protected_keys: @protected_keys
94
+ }).execute!
95
+
96
+ ret_obj = result.data[:change_request_record]
97
+
98
+ ret_obj.knowledge.must_equal "growth"
99
+ ret_obj.power.must_equal "knowledge"
100
+ end
101
+
102
+ it "filters all protected_keys" do
103
+ @protected_record.knowledge="growth"
104
+ @protected_record.power="knowledge"
105
+
106
+ @protected_keys = %w{ power knowledge }
107
+ result = @subject.new({
108
+ protected_record: @protected_record,
109
+ protected_keys: @protected_keys
110
+ }).execute!
111
+
112
+ ret_obj = result.data[:change_request_record]
113
+
114
+ ret_obj.knowledge.must_equal "power"
115
+ ret_obj.power.must_equal "money"
116
+ end
117
+
118
+ it "will allow unprotected changes while reverting protected changes" do
119
+ @protected_record.knowledge="growth"
120
+ @protected_record.power="knowledge"
121
+
122
+ @protected_keys = %w{ power }
123
+ result = @subject.new({
124
+ protected_record: @protected_record,
125
+ protected_keys: @protected_keys
126
+ }).execute!
127
+
128
+ ret_obj = result.data[:change_request_record]
129
+
130
+ ret_obj.knowledge.must_equal "growth"
131
+ ret_obj.power.must_equal "money"
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,2 @@
1
+ require "minitest_helper"
2
+
@@ -0,0 +1,2 @@
1
+ require "minitest_helper"
2
+
@@ -0,0 +1 @@
1
+ require "minitest_helper"
metadata CHANGED
@@ -1,73 +1,88 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: protected_record
3
3
  version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: 0.0.0
4
+ version: 0.0.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Tad Hosford
9
- autorequire:
8
+ autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-04-19 00:00:00.000000000 Z
11
+ date: 2013-04-23 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- name: minitest
14
+ name: pay_dirt
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
16
22
  version_requirements: !ruby/object:Gem::Requirement
17
23
  requirements:
18
- - - ! '>='
24
+ - - '>='
19
25
  - !ruby/object:Gem::Version
20
- version: !binary |-
21
- MA==
22
- none: false
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
23
29
  requirement: !ruby/object:Gem::Requirement
24
30
  requirements:
25
- - - ! '>='
31
+ - - '>='
26
32
  - !ruby/object:Gem::Version
27
- version: !binary |-
28
- MA==
29
- none: false
33
+ version: '0'
34
+ type: :runtime
30
35
  prerelease: false
31
- type: :development
32
- - !ruby/object:Gem::Dependency
33
- name: rake
34
36
  version_requirements: !ruby/object:Gem::Requirement
35
37
  requirements:
36
- - - ! '>='
38
+ - - '>='
37
39
  - !ruby/object:Gem::Version
38
- version: !binary |-
39
- MA==
40
- none: false
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
41
43
  requirement: !ruby/object:Gem::Requirement
42
44
  requirements:
43
- - - ! '>='
45
+ - - '>='
44
46
  - !ruby/object:Gem::Version
45
- version: !binary |-
46
- MA==
47
- none: false
48
- prerelease: false
47
+ version: '0'
49
48
  type: :development
50
- - !ruby/object:Gem::Dependency
51
- name: pry
49
+ prerelease: false
52
50
  version_requirements: !ruby/object:Gem::Requirement
53
51
  requirements:
54
- - - ! '>='
52
+ - - '>='
55
53
  - !ruby/object:Gem::Version
56
- version: !binary |-
57
- MA==
58
- none: false
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
59
57
  requirement: !ruby/object:Gem::Requirement
60
58
  requirements:
61
- - - ! '>='
59
+ - - '>='
62
60
  - !ruby/object:Gem::Version
63
- version: !binary |-
64
- MA==
65
- none: false
61
+ version: '0'
62
+ type: :development
66
63
  prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
67
76
  type: :development
68
- description: ! "\n Filters changes & logs changes to protected\
69
- \ records.\n Creates change requests when changes are attempted\
70
- \ on protected attrs\n "
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: "\n Filters changes & logs changes to protected
84
+ records.\n Creates change requests when changes are attempted
85
+ on protected attrs\n "
71
86
  email:
72
87
  - tad.hosford@gmail.com
73
88
  executables: []
@@ -75,49 +90,47 @@ extensions: []
75
90
  extra_rdoc_files: []
76
91
  files:
77
92
  - .gitignore
93
+ - .ruby-gemset
94
+ - .ruby-version
78
95
  - Gemfile
79
96
  - README.md
97
+ - Rakefile
80
98
  - lib/protected_record.rb
81
99
  - lib/protected_record/change_log.rb
82
100
  - lib/protected_record/change_request.rb
83
- - lib/protected_record/use_case/base.rb
84
- - lib/protected_record/use_case/change/change_log/create.rb
85
- - lib/protected_record/use_case/change/change_request/create.rb
86
- - lib/protected_record/use_case/change/update.rb
87
- - lib/protected_record/use_case/result.rb
101
+ - lib/protected_record/use_case.rb
102
+ - lib/protected_record/use_case/change_filter/create.rb
103
+ - lib/protected_record/use_case/change_log/create.rb
104
+ - lib/protected_record/use_case/change_request/create.rb
105
+ - lib/protected_record/use_case/update.rb
88
106
  - lib/protected_record/version.rb
89
107
  - protected_record.gemspec
90
108
  - test/minitest_helper.rb
109
+ - test/protected_record/change_filter/create_test.rb
110
+ - test/protected_record/change_log/create_test.rb
111
+ - test/protected_record/change_request/create_test.rb
112
+ - test/protected_record/update_test.rb
91
113
  homepage: http://github.com/rthbound/protected_record
92
114
  licenses: []
93
- post_install_message:
115
+ metadata: {}
116
+ post_install_message:
94
117
  rdoc_options: []
95
118
  require_paths:
96
119
  - lib
97
120
  required_ruby_version: !ruby/object:Gem::Requirement
98
121
  requirements:
99
- - - ! '>='
122
+ - - '>='
100
123
  - !ruby/object:Gem::Version
101
- segments:
102
- - 0
103
- hash: 2
104
- version: !binary |-
105
- MA==
106
- none: false
124
+ version: '0'
107
125
  required_rubygems_version: !ruby/object:Gem::Requirement
108
126
  requirements:
109
- - - ! '>='
127
+ - - '>='
110
128
  - !ruby/object:Gem::Version
111
- segments:
112
- - 0
113
- hash: 2
114
- version: !binary |-
115
- MA==
116
- none: false
129
+ version: '0'
117
130
  requirements: []
118
- rubyforge_project:
119
- rubygems_version: 1.8.24
120
- signing_key:
121
- specification_version: 3
131
+ rubyforge_project:
132
+ rubygems_version: 2.0.0.rc.2
133
+ signing_key:
134
+ specification_version: 4
122
135
  summary: ''
123
136
  test_files: []
@@ -1,21 +0,0 @@
1
- module ProtectedRecord
2
- module UseCase
3
- class Base
4
- def load_option(option, options)
5
- instance_variable_set("@#{option}", options.fetch(option.to_sym) { raise "Missing required option: #{option}" } )
6
- end
7
-
8
- def load_options(*option_names, options)
9
- option_names.each{|o| load_option(o, options) }
10
- @errors ||= []
11
- end
12
-
13
- def errors
14
- @errors
15
- end
16
- end
17
- end
18
- end
19
-
20
-
21
-
@@ -1,43 +0,0 @@
1
- module ProtectedRecord
2
- module UseCase
3
- module Change
4
- module ChangeLog
5
- class Create < ::ProtectedRecord::UseCase::Base
6
- def initialize(options)
7
- options = {
8
- record_class: ::ProtectedRecord::ChangeLog::Record
9
- }.merge!(options) if !options.has_key?(:record_class)
10
-
11
- load_options(:record_class, :user, :changed_object, options) and validate_state
12
- end
13
-
14
- def execute!
15
- create_change_log_record if @errors.empty?
16
-
17
- if @errors.empty? && @record.save
18
- return UseCase::Result.new(data: { change_log_record: @record })
19
- else
20
- return UseCase::Result.new(data: { change_log_record: @record }, errors: @errors)
21
- end
22
- end
23
-
24
- private
25
- def create_change_log_record
26
- @record = @record_class.new
27
- @record.user = @user
28
- @record.recordable = @changed_object
29
- @record.observed_changes = ActiveSupport::JSON.encode(@changed_object.previous_changes)
30
- end
31
-
32
- protected
33
- def validate_state
34
- # What are we doing here, if not logging a change that has already happened?
35
- if !@changed_object.previous_changes.present?
36
- @errors << ActiveRecord::ActiveRecordError.new(':changed_object has no previous_changes')
37
- end
38
- end
39
- end
40
- end
41
- end
42
- end
43
- end
@@ -1,51 +0,0 @@
1
- module ProtectedRecord
2
- module UseCase
3
- module Change
4
- module ChangeRequest
5
- class Create < ::ProtectedRecord::UseCase::Base
6
- def initialize(options)
7
- options = {
8
- record_class: ::ProtectedRecord::ChangeRequest::Record
9
- }.merge!(options) if !options.has_key?(:record_class)
10
-
11
- load_options(:protected_keys, :record_class, :user, :dirty_object, options) and validate_state
12
- end
13
-
14
- def execute!
15
- return UseCase::Result.new(data: nil, success: true) if !requested_changes.present?
16
-
17
- create_change_request_record if @errors.empty?
18
-
19
- if @errors.empty? && @record.save
20
- return UseCase::Result.new(data: { change_request_record: @record })
21
- else
22
- return UseCase::Result.new(data: { change_request_record: @record }, errors: @errors)
23
- end
24
- end
25
-
26
- private
27
- def create_change_request_record
28
- @record = @record_class.new
29
- @record.user = @user
30
- @record.recordable = @dirty_object
31
- @record.requested_changes = ActiveSupport::JSON.encode(requested_changes)
32
- end
33
-
34
- def requested_changes
35
- @dirty_object.changes.select do |key|
36
- @protected_keys.map(&:to_s).include? key.to_s
37
- end
38
- end
39
-
40
- protected
41
- def validate_state
42
- # If there are no changes, there's no need to make a request
43
- if !@dirty_object.changes.present?
44
- @errors << ActiveRecord::ActiveRecordError.new(':dirty_object not dirty')
45
- end
46
- end
47
- end
48
- end
49
- end
50
- end
51
- end
@@ -1,80 +0,0 @@
1
- module ProtectedRecord
2
- module UseCase
3
- module Change
4
- class Update < ::ProtectedRecord::UseCase::Base
5
- def initialize(options)
6
- # Defaults
7
- options = {
8
- change_request_use_case: UseCase::Change::ChangeRequest::Create,
9
- change_log_use_case: UseCase::Change::ChangeLog::Create
10
- }.merge!(options) if !options.has_key?(:record_class)
11
-
12
- load_options(:params, :protected_record, :change_request_use_case, :change_log_use_case, :protected_keys, :user, options)
13
- validate_state
14
- end
15
-
16
- def execute!
17
- form_change_request
18
-
19
- if @errors.empty? && !@protected_record.changes.present?
20
- return UseCase::Result.new(data: { params: @protected_record })
21
- else
22
- return UseCase::Result.new(data: { params: @protected_record }, errors: @errors)
23
- end
24
- end
25
-
26
- private
27
- def form_change_request
28
- @protected_record.attributes = @params
29
-
30
- unless @protected_record.id_was.nil?
31
- request_result = @change_request_use_case.new(protected_keys: @protected_keys, dirty_object: @protected_record, user: @user).execute!
32
- if request_result.successful?
33
- revert_protected_attrs
34
- save_protected_record
35
- end
36
- end
37
- end
38
-
39
- def revert_protected_attrs
40
- @protected_keys.each do |key|
41
- if @protected_record.send("#{key.to_s}_changed?")
42
- @protected_record.send("#{key.to_s}=", @protected_record.send("#{key.to_s}_was"))
43
- end
44
- end
45
-
46
- raise if @protected_keys.any? { |key| @protected_record.send("#{key}_changed?") }
47
- end
48
-
49
- def save_protected_record
50
- @protected_record.save
51
- log_changes
52
- end
53
-
54
- def log_changes
55
- log_result = @change_log_use_case.new(user: @user, changed_object: @protected_record).execute!
56
- return true if log_result.successful?
57
- end
58
-
59
- protected
60
- def validate_state
61
- # We expect some keys
62
- if !@protected_keys.kind_of?(Array)
63
- @errors << TypeError.new(':protected_keys not kind of Array')
64
- end
65
-
66
- # The keys should respond to to_s
67
- if @protected_keys.any? {|el| !el.respond_to?("to_s") }
68
- @errors << TypeError.new('All :protected_keys should respond to #to_s')
69
- end
70
-
71
- # The dirty_object should respond to all keys (as methods)
72
- @protected_keys.each do |key|
73
- error = ActiveRecord::ActiveRecordError.new(":dirty_object must respond to #{key.to_s}")
74
- @errors << error unless @protected_record.respond_to?(key.to_s)
75
- end
76
- end
77
- end
78
- end
79
- end
80
- end
@@ -1,40 +0,0 @@
1
- module ProtectedRecord
2
- module UseCase
3
- class Result
4
- # The response from a use case execution
5
- #
6
- # Every use case should return a Result after it runs.
7
- #
8
- # @param [options] options_hash
9
- # A hash specifying the appropriate options
10
- #
11
- # @return [UseCase::Result]
12
- # the Result instance
13
- #
14
- # @example
15
- # UseCase::Result.new(success: true, data: {})
16
- # # => <UseCase::Result>
17
- #
18
- # @api public
19
- def initialize(options)
20
- @success = options.fetch(:success) { !options[:errors].present? }
21
- @errors = options[:errors]
22
- @data = options[:data]
23
- end
24
-
25
- # @api public
26
- def successful?
27
- !!@success
28
- end
29
-
30
- def errors
31
- @errors
32
- end
33
-
34
- # @api public
35
- def data
36
- @data
37
- end
38
- end
39
- end
40
- end