loggable_activity 0.1.39 → 0.1.40

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 (96) hide show
  1. checksums.yaml +4 -4
  2. data/.document +1 -0
  3. data/.nojekyll +1 -0
  4. data/.rubocop.yml +1 -1
  5. data/doc/.nojekyll +1 -0
  6. data/doc/CHANGELOG_md.html +125 -0
  7. data/doc/CODE_OF_CONDUCT_md.html +220 -0
  8. data/doc/CONSIDERTIONS_md.html +227 -0
  9. data/doc/CreateLoggableActivities.html +144 -0
  10. data/doc/CreateLoggableEncryptionKeys.html +140 -0
  11. data/doc/CreateLoggablePayloads.html +144 -0
  12. data/doc/GETTING-STARTED_md.html +186 -0
  13. data/doc/Gemfile.html +104 -0
  14. data/doc/Gemfile_lock.html +152 -0
  15. data/doc/LICENSE_txt.html +104 -0
  16. data/doc/LoggableActivity/CurrentUser.html +99 -0
  17. data/doc/LoggableActivity/Generators/InstallGenerator.html +178 -0
  18. data/doc/LoggableActivity/Generators.html +91 -0
  19. data/doc/README_md.html +138 -0
  20. data/doc/ROADMAP_md.html +151 -0
  21. data/doc/Rakefile.html +102 -0
  22. data/doc/bin/setup.html +100 -0
  23. data/doc/notes/CHEAT_SHEET_md.html +130 -0
  24. data/doc/sig/loggable_activity_rbs.html +101 -0
  25. data/docs/LoggableActivity/Activity.html +494 -0
  26. data/docs/LoggableActivity/Configuration.html +183 -0
  27. data/docs/LoggableActivity/Encryption.html +220 -0
  28. data/docs/LoggableActivity/EncryptionError.html +99 -0
  29. data/docs/LoggableActivity/EncryptionKey.html +327 -0
  30. data/docs/LoggableActivity/Error.html +97 -0
  31. data/docs/LoggableActivity/Hooks.html +172 -0
  32. data/docs/LoggableActivity/Payload.html +159 -0
  33. data/docs/LoggableActivity/PayloadsBuilder.html +154 -0
  34. data/docs/LoggableActivity/UpdatePayloadsBuilder.html +199 -0
  35. data/docs/LoggableActivity.html +100 -0
  36. data/docs/created.rid +10 -0
  37. data/docs/css/fonts.css +167 -0
  38. data/docs/css/rdoc.css +687 -0
  39. data/docs/fonts/Lato-Light.ttf +0 -0
  40. data/docs/fonts/Lato-LightItalic.ttf +0 -0
  41. data/docs/fonts/Lato-Regular.ttf +0 -0
  42. data/docs/fonts/Lato-RegularItalic.ttf +0 -0
  43. data/docs/fonts/SourceCodePro-Bold.ttf +0 -0
  44. data/docs/fonts/SourceCodePro-Regular.ttf +0 -0
  45. data/docs/images/add.png +0 -0
  46. data/docs/images/arrow_up.png +0 -0
  47. data/docs/images/brick.png +0 -0
  48. data/docs/images/brick_link.png +0 -0
  49. data/docs/images/bug.png +0 -0
  50. data/docs/images/bullet_black.png +0 -0
  51. data/docs/images/bullet_toggle_minus.png +0 -0
  52. data/docs/images/bullet_toggle_plus.png +0 -0
  53. data/docs/images/date.png +0 -0
  54. data/docs/images/delete.png +0 -0
  55. data/docs/images/find.png +0 -0
  56. data/docs/images/loadingAnimation.gif +0 -0
  57. data/docs/images/macFFBgHack.png +0 -0
  58. data/docs/images/package.png +0 -0
  59. data/docs/images/page_green.png +0 -0
  60. data/docs/images/page_white_text.png +0 -0
  61. data/docs/images/page_white_width.png +0 -0
  62. data/docs/images/plugin.png +0 -0
  63. data/docs/images/ruby.png +0 -0
  64. data/docs/images/tag_blue.png +0 -0
  65. data/docs/images/tag_green.png +0 -0
  66. data/docs/images/transparent.png +0 -0
  67. data/docs/images/wrench.png +0 -0
  68. data/docs/images/wrench_orange.png +0 -0
  69. data/docs/images/zoom.png +0 -0
  70. data/docs/index.html +91 -0
  71. data/docs/js/darkfish.js +97 -0
  72. data/docs/js/navigation.js +105 -0
  73. data/docs/js/navigation.js.gz +0 -0
  74. data/docs/js/search.js +110 -0
  75. data/docs/js/search_index.js +1 -0
  76. data/docs/js/search_index.js.gz +0 -0
  77. data/docs/js/searcher.js +229 -0
  78. data/docs/js/searcher.js.gz +0 -0
  79. data/docs/table_of_contents.html +184 -0
  80. data/lib/generators/loggable_activity/install_generator.rb +2 -1
  81. data/lib/generators/loggable_activity/templates/current_user.rb +1 -1
  82. data/lib/generators/loggable_activity/templates/loggable_activity.yml +29 -0
  83. data/lib/loggable_activity/activity.rb +208 -59
  84. data/lib/loggable_activity/configuration.rb +18 -0
  85. data/lib/loggable_activity/encryption.rb +20 -1
  86. data/lib/loggable_activity/encryption_key.rb +81 -14
  87. data/lib/loggable_activity/hooks.rb +10 -7
  88. data/lib/loggable_activity/payload.rb +45 -11
  89. data/lib/loggable_activity/payloads_builder.rb +50 -4
  90. data/lib/loggable_activity/update_payloads_builder.rb +64 -2
  91. data/lib/loggable_activity/version.rb +1 -1
  92. metadata +81 -9
  93. data/.DS_Store +0 -0
  94. data/loggable_activity-0.1.32.gem +0 -0
  95. data/loggable_activity-0.1.33.gem +0 -0
  96. data/loggable_activity-0.1.34.gem +0 -0
Binary file
@@ -0,0 +1,184 @@
1
+ <!DOCTYPE html>
2
+
3
+ <html>
4
+ <head>
5
+ <meta charset="UTF-8">
6
+
7
+ <title>Table of Contents - RDoc Documentation</title>
8
+
9
+ <script type="text/javascript">
10
+ var rdoc_rel_prefix = "./";
11
+ var index_rel_prefix = "./";
12
+ </script>
13
+
14
+ <script src="./js/navigation.js" defer></script>
15
+ <script src="./js/search.js" defer></script>
16
+ <script src="./js/search_index.js" defer></script>
17
+ <script src="./js/searcher.js" defer></script>
18
+ <script src="./js/darkfish.js" defer></script>
19
+
20
+ <link href="./css/fonts.css" rel="stylesheet">
21
+ <link href="./css/rdoc.css" rel="stylesheet">
22
+
23
+
24
+ <body id="top" class="table-of-contents">
25
+ <main role="main">
26
+ <h1 class="class">Table of Contents - RDoc Documentation</h1>
27
+
28
+
29
+ <h2 id="classes">Classes and Modules</h2>
30
+ <ul>
31
+ <li class="module">
32
+ <a href="LoggableActivity.html">LoggableActivity</a>
33
+ </li>
34
+ <li class="class">
35
+ <a href="LoggableActivity/Activity.html">LoggableActivity::Activity</a>
36
+ </li>
37
+ <li class="class">
38
+ <a href="LoggableActivity/Configuration.html">LoggableActivity::Configuration</a>
39
+ </li>
40
+ <li class="module">
41
+ <a href="LoggableActivity/Encryption.html">LoggableActivity::Encryption</a>
42
+ </li>
43
+ <li class="class">
44
+ <a href="LoggableActivity/EncryptionError.html">LoggableActivity::EncryptionError</a>
45
+ </li>
46
+ <li class="class">
47
+ <a href="LoggableActivity/EncryptionKey.html">LoggableActivity::EncryptionKey</a>
48
+ </li>
49
+ <li class="module">
50
+ <a href="LoggableActivity/Hooks.html">LoggableActivity::Hooks</a>
51
+ </li>
52
+ <li class="class">
53
+ <a href="LoggableActivity/Payload.html">LoggableActivity::Payload</a>
54
+ </li>
55
+ <li class="module">
56
+ <a href="LoggableActivity/PayloadsBuilder.html">LoggableActivity::PayloadsBuilder</a>
57
+ </li>
58
+ <li class="module">
59
+ <a href="LoggableActivity/UpdatePayloadsBuilder.html">LoggableActivity::UpdatePayloadsBuilder</a>
60
+ </li>
61
+ </ul>
62
+
63
+ <h2 id="methods">Methods</h2>
64
+ <ul>
65
+
66
+ <li class="method">
67
+ <a href="LoggableActivity/Activity.html#method-c-activities_for_actor">::activities_for_actor</a>
68
+ &mdash;
69
+ <span class="container">LoggableActivity::Activity</span>
70
+
71
+ <li class="method">
72
+ <a href="LoggableActivity/Encryption.html#method-c-blank-3F">::blank?</a>
73
+ &mdash;
74
+ <span class="container">LoggableActivity::Encryption</span>
75
+
76
+ <li class="method">
77
+ <a href="LoggableActivity/EncryptionKey.html#method-c-create_encryption_key">::create_encryption_key</a>
78
+ &mdash;
79
+ <span class="container">LoggableActivity::EncryptionKey</span>
80
+
81
+ <li class="method">
82
+ <a href="LoggableActivity/Encryption.html#method-c-decrypt">::decrypt</a>
83
+ &mdash;
84
+ <span class="container">LoggableActivity::Encryption</span>
85
+
86
+ <li class="method">
87
+ <a href="LoggableActivity/Encryption.html#method-c-encrypt">::encrypt</a>
88
+ &mdash;
89
+ <span class="container">LoggableActivity::Encryption</span>
90
+
91
+ <li class="method">
92
+ <a href="LoggableActivity/Configuration.html#method-c-for_class">::for_class</a>
93
+ &mdash;
94
+ <span class="container">LoggableActivity::Configuration</span>
95
+
96
+ <li class="method">
97
+ <a href="LoggableActivity/EncryptionKey.html#method-c-for_record">::for_record</a>
98
+ &mdash;
99
+ <span class="container">LoggableActivity::EncryptionKey</span>
100
+
101
+ <li class="method">
102
+ <a href="LoggableActivity/EncryptionKey.html#method-c-for_record_by_type_and_id">::for_record_by_type_and_id</a>
103
+ &mdash;
104
+ <span class="container">LoggableActivity::EncryptionKey</span>
105
+
106
+ <li class="method">
107
+ <a href="LoggableActivity/Activity.html#method-c-latest">::latest</a>
108
+ &mdash;
109
+ <span class="container">LoggableActivity::Activity</span>
110
+
111
+ <li class="method">
112
+ <a href="LoggableActivity/Configuration.html#method-c-load_config_file">::load_config_file</a>
113
+ &mdash;
114
+ <span class="container">LoggableActivity::Configuration</span>
115
+
116
+ <li class="method">
117
+ <a href="LoggableActivity/EncryptionKey.html#method-c-random_key">::random_key</a>
118
+ &mdash;
119
+ <span class="container">LoggableActivity::EncryptionKey</span>
120
+
121
+ <li class="method">
122
+ <a href="LoggableActivity/Activity.html#method-i-actor_display_name">#actor_display_name</a>
123
+ &mdash;
124
+ <span class="container">LoggableActivity::Activity</span>
125
+
126
+ <li class="method">
127
+ <a href="LoggableActivity/Activity.html#method-i-attrs">#attrs</a>
128
+ &mdash;
129
+ <span class="container">LoggableActivity::Activity</span>
130
+
131
+ <li class="method">
132
+ <a href="LoggableActivity/Payload.html#method-i-attrs">#attrs</a>
133
+ &mdash;
134
+ <span class="container">LoggableActivity::Payload</span>
135
+
136
+ <li class="method">
137
+ <a href="LoggableActivity/PayloadsBuilder.html#method-i-build_payloads">#build_payloads</a>
138
+ &mdash;
139
+ <span class="container">LoggableActivity::PayloadsBuilder</span>
140
+
141
+ <li class="method">
142
+ <a href="LoggableActivity/UpdatePayloadsBuilder.html#method-i-build_update_payloads">#build_update_payloads</a>
143
+ &mdash;
144
+ <span class="container">LoggableActivity::UpdatePayloadsBuilder</span>
145
+
146
+ <li class="method">
147
+ <a href="LoggableActivity/Hooks.html#method-i-log">#log</a>
148
+ &mdash;
149
+ <span class="container">LoggableActivity::Hooks</span>
150
+
151
+ <li class="method">
152
+ <a href="LoggableActivity/EncryptionKey.html#method-i-mark_as_deleted">#mark_as_deleted</a>
153
+ &mdash;
154
+ <span class="container">LoggableActivity::EncryptionKey</span>
155
+
156
+ <li class="method">
157
+ <a href="LoggableActivity/Activity.html#method-i-primary_payload_attrs">#primary_payload_attrs</a>
158
+ &mdash;
159
+ <span class="container">LoggableActivity::Activity</span>
160
+
161
+ <li class="method">
162
+ <a href="LoggableActivity/Activity.html#method-i-record_display_name">#record_display_name</a>
163
+ &mdash;
164
+ <span class="container">LoggableActivity::Activity</span>
165
+
166
+ <li class="method">
167
+ <a href="LoggableActivity/Activity.html#method-i-relations_attrs">#relations_attrs</a>
168
+ &mdash;
169
+ <span class="container">LoggableActivity::Activity</span>
170
+
171
+ <li class="method">
172
+ <a href="LoggableActivity/Activity.html#method-i-update_activity_attrs">#update_activity_attrs</a>
173
+ &mdash;
174
+ <span class="container">LoggableActivity::Activity</span>
175
+ </ul>
176
+ </main>
177
+
178
+
179
+ <footer id="validator-badges" role="contentinfo">
180
+ <p><a href="https://validator.w3.org/check/referer">Validate</a>
181
+ <p>Generated by <a href="https://ruby.github.io/rdoc/">RDoc</a> 6.6.2.
182
+ <p>Based on <a href="http://deveiate.org/projects/Darkfish-RDoc/">Darkfish</a> by <a href="http://deveiate.org">Michael Granger</a>.
183
+ </footer>
184
+
@@ -23,7 +23,8 @@ module LoggableActivity
23
23
 
24
24
  def create_model_file
25
25
  template 'loggable_activity.en.yml', 'config/locales/loggable_activity.en.yml'
26
- template "current_user.rb", 'app/controllers/concerns/loggable_activity/current_user.rb'
26
+ template 'loggable_activity.yml', 'config/loggable_activity.yml'
27
+ template 'current_user.rb', 'app/controllers/concerns/loggable_activity/current_user.rb'
27
28
  end
28
29
  end
29
30
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Stores current user in a thread variable so that is can be accessed from the 'models/conserns/loggable/activities.rb' file.
4
3
  module LoggableActivity
4
+ # Stores current user in a thread variable so is can be accessed from the LoggableActivity::Hook model
5
5
  module CurrentUser
6
6
  extend ActiveSupport::Concern
7
7
 
@@ -0,0 +1,29 @@
1
+ # User:
2
+ # record_display_name: full_name
3
+ # loggable_attrs:
4
+ # - first_name
5
+ # - last_name
6
+ # - age
7
+ # - email
8
+ # - user_type
9
+ # auto_log:
10
+ # - create
11
+ # - update
12
+ # - destroy
13
+ # relations:
14
+ # - has_one: :demo_user_profile
15
+ # model: Demo::UserProfile
16
+ # loggable_attrs:
17
+ # - sex
18
+ # - religion
19
+ # - belongs_to: :demo_address
20
+ # model: Demo::Address
21
+ # loggable_attrs:
22
+ # - street
23
+ # - city
24
+ # - country
25
+ # - postal_code
26
+ # - belongs_to: :demo_club
27
+ # model: Demo::Club
28
+ # loggable_attrs:
29
+ # - name
@@ -1,29 +1,132 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # This is the activity log. It contains an agregation of payloads.
4
- # It reprecent one activity for the log
5
-
6
3
  module LoggableActivity
4
+ # Represents one action in the activity log.
7
5
  class Activity < ActiveRecord::Base
8
6
  self.table_name = 'loggable_activities'
7
+ # Associations
9
8
  has_many :payloads, class_name: 'LoggableActivity::Payload', dependent: :destroy
9
+ belongs_to :record, polymorphic: true, optional: true
10
+ belongs_to :actor, polymorphic: true, optional: true
11
+
10
12
  accepts_nested_attributes_for :payloads
11
13
 
14
+ # Validations
12
15
  validates :actor, presence: true
13
16
  validates :action, presence: true
14
- # validates :encrypted_record_display_name, presence: true
15
- # validates :encrypted_actor_display_name, presence: true
16
-
17
17
  validate :must_have_at_least_one_payload
18
18
 
19
- belongs_to :record, polymorphic: true, optional: true
20
- belongs_to :actor, polymorphic: true, optional: true
21
-
19
+ # Returns a list of attributes of the activity includig the indliced relations.
20
+ # The included relations are devined in the 'config/loggable_activity.yaml' file.
21
+ # The attributes are packed in a way that they can be used to display the activity in the UI.
22
+ #
23
+ # Example:
24
+ # @activity.attrs
25
+ #
26
+ # Returns:
27
+ # [
28
+ # {
29
+ # record_class: "User",
30
+ # payload_type: "primary_payload",
31
+ # attrs: {
32
+ # "first_name" => "David",
33
+ # "last_name" => "Bowie",
34
+ # "age" => "69",
35
+ # "email" => "david@example.com",
36
+ # "user_type" => "Patient"
37
+ # }
38
+ # },
39
+ # {
40
+ # record_class: "Demo::UserProfile",
41
+ # payload_type: "current_association",
42
+ # attrs: {
43
+ # "sex" => "Male",
44
+ # "religion" => "Agnostic"
45
+ # }
46
+ # },
47
+ # {
48
+ # record_class: "Demo::Address",
49
+ # payload_type: "current_association",
50
+ # attrs: {
51
+ # "street" => "Eiffel Tower",
52
+ # "city" => "Paris",
53
+ # "country" => "France",
54
+ # "postal_code" => "75007"
55
+ # }
56
+ # },
57
+ # {
58
+ # record_class: "Demo::Club",
59
+ # payload_type: "current_association",
60
+ # attrs: {
61
+ # "name" => "Mystic Fusion Lounge"
62
+ # }
63
+ # }
64
+ # ]
65
+ #
22
66
  def attrs
23
- # @attrs ||= payloads_attrs
24
- payloads_attrs
67
+ ordered_payloads.map do |payload|
68
+ {
69
+ record_class: payload.record_type,
70
+ payload_type: payload.payload_type,
71
+ attrs: payload.attrs
72
+ }
73
+ end
25
74
  end
26
75
 
76
+ # Returns the attributes of an upddate activity.
77
+ #
78
+ # Example:
79
+ # @activity.update_activity_attrs
80
+ #
81
+ # Returns:
82
+ # {
83
+ # # Update attributes for Demo::Club
84
+ # update_attrs: {
85
+ # record_class: "Demo::Club",
86
+ # attrs: [
87
+ # {
88
+ # "name" => {
89
+ # # Previous name
90
+ # from: "Electric Oasis Club",
91
+ # # New name
92
+ # to: "Electric Oasis Club nr 5"
93
+ # }
94
+ # }
95
+ # ]
96
+ # },
97
+ # # Updated relations attributes
98
+ # updated_relations_attrs: [
99
+ # {
100
+ # record_class: "Demo::Address",
101
+ # previous_attrs: {
102
+ # # Record class
103
+ # record_class: "Demo::Address",
104
+ # # Previous association payload type
105
+ # payload_type: "previous_association",
106
+ # # Previous attributes for Demo::Address
107
+ # attrs: {
108
+ # "street" => "Ice Hotel, Marknadsvägen 63",
109
+ # "city" => "Jukkasjärvi",
110
+ # "country" => "Sweden",
111
+ # "postal_code" => "981 91"
112
+ # }
113
+ # },
114
+ # current_attrs: {
115
+ # record_class: "Demo::Address",
116
+ # # Current association payload type
117
+ # payload_type: "current_association",
118
+ # # Current attributes for Demo::Address
119
+ # attrs: {
120
+ # "street" => "The Palace of Versailles",
121
+ # "city" => "Versailles",
122
+ # "country" => "France",
123
+ # "postal_code" => "78000"
124
+ # }
125
+ # }
126
+ # }
127
+ # ]
128
+ # }
129
+ #
27
130
  def update_activity_attrs
28
131
  {
29
132
  update_attrs:,
@@ -31,46 +134,98 @@ module LoggableActivity
31
134
  }
32
135
  end
33
136
 
137
+ # Returns the attributes for the primary payload, without the relations.
138
+ #
139
+ # Example:
140
+ #
141
+ # @activity.primary_payload_attrs
142
+ #
143
+ # Returns:
144
+ # {
145
+ # "first_name" => "David",
146
+ # "last_name" => "Bowie",
147
+ # "age" => "69",
148
+ # "email" => "david@example.com",
149
+ # "user_type" => "Patient"
150
+ # }
151
+ #
34
152
  def primary_payload_attrs
35
153
  primary_payload ? primary_payload.attrs : {}
36
154
  end
37
155
 
38
- def primary_payload
39
- # @primary_payload ||= ordered_payloads.find { |p| p.payload_type == 'primary_payload' }
40
- ordered_payloads.find { |p| p.payload_type == 'primary_payload' }
156
+ # Returns the attributes for the relations.
157
+ #
158
+ # Example:
159
+ #
160
+ # @activity.relations_attrs
161
+ #
162
+ # Returns:
163
+ # [
164
+ # {
165
+ # record_class: "Demo::Address",
166
+ # # Current association payload type
167
+ # payload_type: "current_association",
168
+ # # Current attributes for Demo::Address
169
+ # attrs: {
170
+ # "street" => "The Palace of Versailles",
171
+ # "city" => "Versailles",
172
+ # "country" => "France",
173
+ # "postal_code" => "78000"
174
+ # }
175
+ # }
176
+ # ]
177
+ def relations_attrs
178
+ attrs.filter { |p| p[:payload_type] == 'current_association' }
41
179
  end
42
180
 
43
- def ordered_payloads
44
- # @ordered_payloads ||= payloads.order(:payload_type)
45
- payloads.order(:payload_type)
46
- end
181
+ # Returns the display name for a record. what method to use if defined in '/config/loggable_activity.yaml'
182
+ #
183
+ # Example:
184
+ #
185
+ # @activity.record_display_name
186
+ #
187
+ # Returns:
188
+ # "David Bowie"
189
+ #
190
+ def record_display_name
191
+ return I18n.t('loggable.activity.deleted') if encrypted_record_display_name.nil?
47
192
 
48
- def relations_attrs
49
- attrs.filter { |p| p[:payload_type] == 'current_association' }
193
+ LoggableActivity::Encryption.decrypt(encrypted_record_display_name, record_key)
50
194
  end
51
195
 
52
- def updated_relations_attrs
53
- grouped_associations = attrs.group_by { |p| p[:record_class] }
196
+ # Returns the display name for a actor. what method to use if defined in '/config/loggable_activity.yaml'
197
+ #
198
+ # Example:
199
+ #
200
+ # @activity.actor_display_name
201
+ #
202
+ # Returns:
203
+ # "Elvis Presley"
204
+ #
205
+ def actor_display_name
206
+ return I18n.t('loggable.activity.deleted') if encrypted_actor_display_name.nil?
54
207
 
55
- grouped_associations.map do |record_class, payloads|
56
- previous_attrs = payloads.find { |p| p[:payload_type] == 'previous_association' }
57
- current_attrs = payloads.find { |p| p[:payload_type] == 'current_association' }
58
- next if previous_attrs.nil? && current_attrs.nil?
208
+ LoggableActivity::Encryption.decrypt(encrypted_actor_display_name, actor_key)
209
+ end
59
210
 
60
- { record_class:, previous_attrs:, current_attrs: }
61
- end.compact
211
+ # Returns a list of activities for a given actor.
212
+ def self.activities_for_actor(actor, limit = 20, params = { offset: 0 })
213
+ LoggableActivity::Activity.latest(limit, params).where(actor:)
62
214
  end
63
215
 
64
- def payloads_attrs
65
- ordered_payloads.map do |payload|
66
- {
67
- record_class: payload.record_type,
68
- payload_type: payload.payload_type,
69
- attrs: payload.attrs
70
- }
71
- end
216
+ # Returns a list of activities ordered by creation date.
217
+ def self.latest(limit = 20, params = { offset: 0 })
218
+ offset = params[:offset] || 0
219
+ LoggableActivity::Activity
220
+ .all
221
+ .order(created_at: :desc)
222
+ .includes(:payloads)
223
+ .offset(offset)
224
+ .limit(limit)
72
225
  end
73
226
 
227
+ private
228
+
74
229
  def update_attrs
75
230
  update_payload_attrs = attrs.find { |p| p[:payload_type] == 'update_payload' }
76
231
  return nil unless update_payload_attrs
@@ -79,42 +234,36 @@ module LoggableActivity
79
234
  update_payload_attrs
80
235
  end
81
236
 
82
- def previous_associations_attrs
83
- attrs.select { |p| p[:payload_type] == 'previous_association' }
237
+ def primary_payload
238
+ ordered_payloads.find { |p| p.payload_type == 'primary_payload' }
84
239
  end
85
240
 
86
- def record_display_name
87
- return I18n.t('loggable.activity.deleted') if encrypted_record_display_name.nil?
241
+ def updated_relations_attrs
242
+ grouped_associations = attrs.group_by { |p| p[:record_class] }
88
243
 
89
- LoggableActivity::Encryption.decrypt(encrypted_record_display_name, record_key)
90
- end
244
+ grouped_associations.map do |record_class, payloads|
245
+ previous_attrs = payloads.find { |p| p[:payload_type] == 'previous_association' }
246
+ current_attrs = payloads.find { |p| p[:payload_type] == 'current_association' }
247
+ next if previous_attrs.nil? && current_attrs.nil?
91
248
 
92
- def actor_display_name
93
- return I18n.t('loggable.activity.deleted') if encrypted_actor_display_name.nil?
249
+ { record_class:, previous_attrs:, current_attrs: }
250
+ end.compact
251
+ end
94
252
 
95
- LoggableActivity::Encryption.decrypt(encrypted_actor_display_name, actor_key)
253
+ def previous_associations_attrs
254
+ attrs.select { |p| p[:payload_type] == 'previous_association' }
96
255
  end
97
256
 
98
- def actor_key
99
- LoggableActivity::EncryptionKey.for_record(actor)&.key
257
+ def ordered_payloads
258
+ payloads.order(:payload_type)
100
259
  end
101
260
 
102
261
  def record_key
103
262
  LoggableActivity::EncryptionKey.for_record(record)&.key
104
263
  end
105
264
 
106
- def self.activities_for_actor(actor)
107
- LoggableActivity::Activity.where(actor:).order(created_at: :desc)
108
- end
109
-
110
- def self.latest(limit = 20, params = { offset: 0 })
111
- offset = params[:offset] || 0
112
- LoggableActivity::Activity
113
- .all
114
- .order(created_at: :desc)
115
- .includes(:payloads)
116
- .offset(offset)
117
- .limit(limit)
265
+ def actor_key
266
+ LoggableActivity::EncryptionKey.for_record(actor)&.key
118
267
  end
119
268
 
120
269
  def must_have_at_least_one_payload
@@ -1,11 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LoggableActivity
4
+ # This class is used to load the configuration file located at config/loggable_activity.yml
4
5
  class Configuration
5
6
  def self.load_config_file(config_file_path)
6
7
  @config_data = YAML.load_file(config_file_path)
7
8
  end
8
9
 
10
+ # Returns the configuration data for the given class
11
+ #
12
+ # Example:
13
+ # LoggableActivity::Configuration.for_class('User')
14
+ # Returns:
15
+ # {
16
+ # "record_display_name": "full_name",
17
+ # "loggable_attrs": [
18
+ # "first_name",
19
+ # "last_name",
20
+ # ],
21
+ # "auto_log": [
22
+ # "create",
23
+ # "update",
24
+ # "destroy"
25
+ # ]
26
+ # }
9
27
  def self.for_class(class_name)
10
28
  @config_data[class_name]
11
29
  end
@@ -5,10 +5,20 @@ require 'openssl'
5
5
  require 'base64'
6
6
 
7
7
  module LoggableActivity
8
+ # This error is raised when encryption or decryption fails
8
9
  class EncryptionError < StandardError
9
10
  end
10
11
 
12
+ # This module is used to encrypt and decrypt attributes
11
13
  module Encryption
14
+ # Encrypts the given data using the given encryption key
15
+ #
16
+ # Example:
17
+ # LoggableActivity::Encryption.encrypt('my secret data', 'my secret key')
18
+ #
19
+ # Returns:
20
+ # "SOME_ENCRYPTED_STRING"
21
+ #
12
22
  def self.encrypt(data, encryption_key)
13
23
  return nil if data.nil?
14
24
  return nil if encryption_key.nil?
@@ -22,8 +32,17 @@ module LoggableActivity
22
32
  raise EncryptionError, "Encryption failed: #{e.message} ***"
23
33
  end
24
34
 
35
+ # Decrypts the given data using the given encryption key
36
+ #
37
+ # Example:
38
+ # LoggableActivity::Encryption.decrypt('SOME_ENCRYPTED_STRING', 'SECRET_KEY')
39
+ #
40
+ # Returns:
41
+ # "my secret data"
42
+ #
25
43
  def self.decrypt(data, encryption_key)
26
- return I18n.t('loggable.activity.deleted') if blank?(data) || blank?(encryption_key)
44
+ return nil if data.nil?
45
+ return I18n.t('loggable.activity.deleted') if encryption_key.nil?
27
46
 
28
47
  cipher = OpenSSL::Cipher.new('AES-128-CBC').decrypt
29
48
  cipher.key = Digest::SHA1.hexdigest(encryption_key)[0..15]