sequel_audited 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 588936fb470f24b13cc2c4a2bb428ebca0c9b6ba
4
+ data.tar.gz: 40f529bdd54785950add19fba2edde881aff4297
5
+ SHA512:
6
+ metadata.gz: 7d999de849aaae4777e095be1390c34aedd917d18c6731d643554d85f3bac48fddee225426647cb48dba59574d4d0ea3a9d858166d67926566740d5f0315da8b
7
+ data.tar.gz: da89ba1160221b66ed8c23151da6a6cd47a2d7ef2e98cc50a57f8aed6b3ea2b09eaca0ef09a64732b515b96dbfe7673320116fcd1d530f4eb49d955bb1744fa5
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ spec/sequel-audited-test.db
11
+ .env.test
12
+ rubocop
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at kematzy@gmail.com. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sequel-audited.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Kematzy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,587 @@
1
+ # Sequel::Audited
2
+
3
+ **sequel_audited** is a [Sequel](http://sequel.jeremyevans.net/) plugin that logs changes made to an
4
+ audited model, including who created, updated and destroyed the record, and what was changed
5
+ and when the change was made.
6
+
7
+ This plugin provides model auditing (a.k.a: record versioning) for DB scenarios when DB triggers
8
+ are not possible. (ie: on a web app on Heroku).
9
+
10
+
11
+
12
+ ## Disclaimer
13
+
14
+ This is still **work-in-progress**, and therefore **NOT production ready**, so **use with care**
15
+ and test thoroughly before depending upon this gem for mission-critical stuff!
16
+ You have been warned! No warranties and guarantees expressed or implied!
17
+
18
+ <br>
19
+
20
+ ----
21
+
22
+ <br>
23
+
24
+ ## Installation
25
+
26
+ ### 1) Install the gem
27
+
28
+ Add this line to your app's Gemfile:
29
+
30
+
31
+ ```ruby
32
+ gem 'sequel_audited'
33
+ ```
34
+
35
+ And then execute:
36
+
37
+ ```bash
38
+ $ bundle
39
+ ```
40
+
41
+ Or install it yourself as:
42
+
43
+ ```bash
44
+ $ gem install sequel_audited
45
+ ```
46
+
47
+
48
+ ### 2) Generate Migration
49
+
50
+ In your apps Rakefile add the following:
51
+
52
+ ```ruby
53
+ load 'tasks/sequel_audited/migrate.rake'
54
+ ```
55
+
56
+ Then verify that the Rake task is available by calling:
57
+
58
+ ```bash
59
+ bundle exec rake -T
60
+ ```
61
+
62
+ which should output something like this:
63
+
64
+ ```bash
65
+ ....
66
+ rake audited:add_migration # Installs Sequel::Audited migration, but does not run it.
67
+ ....
68
+ ```
69
+
70
+ Run the sequel_audit rake task:
71
+
72
+ ```bash
73
+ bundle exec rake audited:add_migration
74
+ ```
75
+ After this you can comment out the rake task in your Rakefile until you need to update. And then
76
+ finally run db:migrate to update your DB.
77
+
78
+ ```bash
79
+ bundle exec rake db:migrate
80
+ ```
81
+
82
+
83
+
84
+ ### IMPORTANT SIDENOTE!
85
+
86
+ If you are using PostgreSQL as your database, then it's a good idea to convert the the `changed`
87
+ column to JSON type for automatic translations into a Ruby hash.
88
+
89
+ Otherwise, you have to use `JSON.parse(@v.changed)` to convert it to a hash if and when you want
90
+ to use it.
91
+
92
+ <br>
93
+
94
+ ----
95
+
96
+
97
+ <a name="usage"></a>
98
+ ## Usage
99
+
100
+
101
+ Using this plugin is fairly simple and straight-forward. Just add it to the model you wish to
102
+ have audits (versions) for.
103
+
104
+ ```ruby
105
+ # auditing single model
106
+ Post.plugin :audited
107
+
108
+ # auditing all models. NOT RECOMMENDED!
109
+ Sequel::Model.plugin :audited
110
+ ```
111
+
112
+ By default this will audit / version all columns on the model, **except** the default ignored columns configured in `Sequel::Audited.audited_default_ignored_columns` (see [Configuration Options](#configuration-options) below).
113
+
114
+
115
+ #### `plugin(:audited)`
116
+
117
+ ```ruby
118
+ # Given a Post model with these columns:
119
+ [:id, :category_id, :title, :body, :author_id, :created_at, :updated_at]
120
+
121
+ # Auditing all columns*
122
+
123
+ Post.plugin :audited
124
+
125
+ #=> [:id, :category_id, :title, :body, :author_id] # audited columns
126
+ #=> [:created_at, :updated_at] # ignored columns
127
+ ```
128
+ <br>
129
+
130
+ #### `plugin(:audited, :only => [...])`
131
+
132
+ ```ruby
133
+ # Auditing a Single column
134
+
135
+ Post.plugin :audited, only: [:title]
136
+
137
+ #=> [:title] # audited columns
138
+ #=> [:id, :category_id, :body, :author_id, :created_at, :updated_at] # ignored columns
139
+
140
+
141
+ # Auditing Multiple columns
142
+
143
+ Post.plugin :audited, only: [:title, :body]
144
+ #=> [:title, :body] # audited columns
145
+ #=> [:id, :category_id, :author_id, :created_at, :updated_at] # ignored columns
146
+
147
+ ```
148
+ <br>
149
+
150
+ #### `plugin(:audited, :except => [...])`
151
+
152
+ **NOTE!** this option does NOT ignore the default ignored columns, so use with care.
153
+
154
+ ```ruby
155
+ # Auditing all columns except specified columns
156
+
157
+ Post.plugin :audited, except: [:title]
158
+
159
+ #=> [:id, :category_id, :author_id, :created_at, :updated_at] # audited columns
160
+ #=> [:title] # ignored columns
161
+
162
+
163
+ Post.plugin :audited, except: [:title, :author_id]
164
+
165
+ #=> [:id, :category_id, :created_at, :updated_at] # audited columns
166
+ #=> [:title, :author_id] # ignored columns
167
+
168
+ ```
169
+ <br>
170
+
171
+ ---
172
+
173
+ <br>
174
+
175
+ ## So what does it do??
176
+
177
+ You have to look behind the curtain to see what this plugin actually does.
178
+
179
+ In a new clean DB...
180
+
181
+ ### 1) Create
182
+
183
+ When you create a new record like this:
184
+
185
+ ```ruby
186
+ Category.create(name: 'Sequel')
187
+ #<Category @values={
188
+ :id => 1,
189
+ :name => "Sequel",
190
+ :position => 1,
191
+ :created_at => <timestamp>,
192
+ :updated_at => nil
193
+ }>
194
+
195
+ # in the background a new row in DB[:audit_logs] has been added with the following info:
196
+
197
+ #<AuditLog @values={
198
+ :id => 1,
199
+ :associated_type => "Category",
200
+ :associated_id => 1,
201
+ :event => "create",
202
+ # NOTE! all filled values are stored.
203
+ :changed => "{\"id\":1,\"name\":\"Sequel\",\"created_at\":\"<timestamp>\"}",
204
+ :version => 1,
205
+ :modifier_id => 88,
206
+ :modifier_type => "User",
207
+ :created_at => <timestamp>
208
+ }>
209
+ ```
210
+
211
+ ### 2) Updates
212
+
213
+ When you update a record like this:
214
+
215
+ ```ruby
216
+ cat.update(name: 'Ruby Sequel')
217
+ #<Category @values={
218
+ :id => 1,
219
+ :name => "Ruby Sequel",
220
+ :position => 1,
221
+ :created_at => <timestamp>,
222
+ :updated_at => <timestamp>
223
+ }>
224
+
225
+ # in the background a new row in DB[:audit_logs] has been added with the following info:
226
+
227
+ #<AuditLog @values={
228
+ :id => 2,
229
+ :associated_type => "Category",
230
+ :associated_id => 1,
231
+ :event => "update",
232
+ # NOTE! only the changes are stored
233
+ :changed => "{\"name\":[\"Sequel\",\"Ruby Sequel\"],\"updated_at\":\"<timestamp>\"}",
234
+ :version => 2,
235
+ :modifier_id => 88,
236
+ :modifier_type => "User",
237
+ :created_at => <timestamp>
238
+ }>
239
+ ```
240
+
241
+
242
+ ### 3) Destroys (Deletes)
243
+
244
+ When you delete a record like this:
245
+
246
+ ```ruby
247
+ cat.delete
248
+
249
+ # in the background a new row in DB[:audit_logs] is added with the info:
250
+
251
+ #<AuditLog @values={
252
+ :id => 3,
253
+ :associated_type => "Category",
254
+ :associated_id => 1,
255
+ :event => "destroy",
256
+ # NOTE! all values at exit time are stored
257
+ :changed => "{\"id\":1,\"name\":\"Ruby Sequel\",\"created_at\":\"<timestamp>\",\"updated_at\":\"<timestamp>\"}",
258
+ :version => 3,
259
+ :modifier_id => 88,
260
+ :modifier_type => "User",
261
+ :created_at => <timestamp>
262
+ }>
263
+ ```
264
+
265
+
266
+ This way you can **easily track what was created, changed or deleted** and **who did it** and **when they did it**.
267
+
268
+ <br>
269
+
270
+ ---
271
+
272
+ <br>
273
+
274
+
275
+ <a name="configuration-options"></a>
276
+ ## Configuration Options
277
+
278
+
279
+ **sequel_audited** supports two forms of configurations:
280
+
281
+ ### A) Global configuration options
282
+
283
+ #### `Sequel::Audited.audited_current_user_method`
284
+
285
+ Sets the name of the global method that provides the current user object.
286
+ Default is: `:current_user`.
287
+
288
+ You can easily change the name of this method by calling:
289
+
290
+ ```ruby
291
+ Sequel::Audited.audited_current_user_method = :audited_user
292
+ ```
293
+
294
+ **Note!** the name of the function must be given as a symbol.
295
+ **Note!!** it will first try to hit the method on the model (i.e. Post) itself first. Then it will hit the global method.<br>
296
+ So if you want to customize the modifier per model you can do that here.
297
+
298
+ <br>
299
+
300
+
301
+ #### `Sequel::Audited.audited_model_name`
302
+
303
+ Enables adding your own Audit model. Default is: `:AuditLog`
304
+
305
+ ```ruby
306
+ Sequel:: Audited.audited_model_name = :YourCustomModel
307
+ ```
308
+ **Note!** the name of the model must be given as a symbol.
309
+ <br>
310
+
311
+
312
+ #### `Sequel::Audited.audited_enabled`
313
+
314
+ Toggle for enabling / disabling auditing throughout all audited models.
315
+ Default is: `true` i.e: enabled.
316
+
317
+ <br>
318
+
319
+
320
+ #### `Sequel::Audited.audited_default_ignored_columns`
321
+
322
+ An array of columns that are ignored by default. Default value is:
323
+
324
+ ```ruby
325
+ [:lock_version, :created_at, :updated_at, :created_on, :updated_on]
326
+ ```
327
+ NOTE! `:timestamps` related columns must be ignored or you may end up with situation
328
+ where an update triggers multiple copies of the record in the audit log.
329
+
330
+ <br>
331
+
332
+
333
+ ```ruby
334
+ # NOTE! array values must be given as symbols.
335
+ Sequel::Audited.audited_default_ignored_columns = [:id, :mycolumn, ...]
336
+ ```
337
+
338
+ <br>
339
+
340
+ ### B) Per Audited Model configurations
341
+
342
+ You can also set these settings on a per model setting by passing the following options:
343
+
344
+ #### `:user_method => :something`
345
+
346
+ This option will use a different method for the current user within this model only.
347
+
348
+ Example:
349
+
350
+ ```ruby
351
+ # if you have a global method like
352
+ def current_client
353
+ @current_client ||= Client[session[:client_id]]
354
+ end
355
+
356
+ # and set
357
+ ClientProfile.plugin(:audited, :user_method => :current_client)
358
+
359
+ # then the user info will be taken from DB[:clients].
360
+ #<Client @values={:id=>99,:username=>"happyclient"... }>
361
+
362
+ ```
363
+
364
+ **NOTE!** the current user model must respond to `:id` attributes.
365
+
366
+ <br>
367
+
368
+ #### `:default_ignored_columns => [...]`
369
+
370
+ This option allows you to set custom default ignored columns in the audited model. It's basically
371
+ just an option *just-in-case*, but it's probably better to use the `:only => []` or `:except => []`
372
+ options instead (see [Usage](#usage) above).
373
+
374
+ <br>
375
+
376
+ ----
377
+
378
+ <br>
379
+
380
+
381
+
382
+ ## Class Methods
383
+
384
+ You can easily track all changes made to a model / row / field(s) like this:
385
+
386
+
387
+ ### `#.audited_version?`
388
+
389
+ ```ruby
390
+ # check if model have any audits (only works on audited models)
391
+ Post.audited_versions?
392
+ #=> returns true / false if any audits have been made
393
+ ```
394
+
395
+ ### `#.audited_version([conditions])`
396
+
397
+ ```ruby
398
+ # grab all audits for a particular model. Returns an array.
399
+ Post.audited_versions
400
+ #=> [
401
+ { id: 1, associated_type: 'Post', associated_id: '11', version: 1,
402
+ changed: "{JSON SERIALIZED OBJECT}", modifier_id: 88,
403
+ username: "joeblogs", created_at: TIMESTAMP
404
+ },
405
+ {...}
406
+ ]
407
+
408
+
409
+ # filtered by primary_key value
410
+ Posts.audited_versions(associated_id: 123)
411
+
412
+ # filtered by user :id value
413
+ Posts.audited_versions(modifier_id: 88)
414
+
415
+ # filtered to last two (2) days only
416
+ Posts.audited_versions(:created_at < Date.today - 2)
417
+
418
+ ```
419
+
420
+
421
+
422
+ 2) Track all changes made by a user / modifier_group.
423
+
424
+ ```ruby
425
+ joe = User[88]
426
+
427
+ joe.audited_versions
428
+ #=> returns all audits made by joe
429
+ ['SELECT * FROM `audit_logs` WHERE modifier_id = 88 ORDER BY created_at DESC']
430
+
431
+ joe.audited_versions(:associated_type => Post)
432
+ #=> returns all audits made by joe on the Post model
433
+ ['SELECT * FROM `audit_logs` WHERE modifier_id = 88 AND associated_type = 'Post' ORDER BY created_at DESC']
434
+ ```
435
+
436
+
437
+
438
+ ## Instance Mehtods
439
+
440
+ When you active `.plugin(:audited)` in your model, you get these methods:
441
+
442
+
443
+ ### `.versions`
444
+
445
+ ```ruby
446
+ class Post < Sequel::Model
447
+ plugin :audited # options here
448
+ end
449
+
450
+ # Returns this post's versions.
451
+ post.versions #=> []
452
+ ```
453
+
454
+
455
+ ### `.blame`
456
+ -- aliased as: `.last_audited_by`
457
+
458
+ ```ruby
459
+ # Returns the user model of the user who last changed the record
460
+ post.blame
461
+ post.last_audited_by #=> User model
462
+ ```
463
+
464
+
465
+ ### `.last_audited_at`
466
+ -- aliased as: `.last_audited_on`
467
+
468
+ ```ruby
469
+ # Returns the timestamp last changed the record
470
+ post.last_audited_at
471
+ post.last_audited_on #=> <timestamp>
472
+ ```
473
+
474
+
475
+ ### To be implemented
476
+
477
+ ```ruby
478
+ # Returns the post (not a version) as it looked at around the the given timestamp.
479
+ post.version_at(timestamp)
480
+
481
+ # Returns the objects (not Versions) as they were between the given times.
482
+ post.versions_between(start_time, end_time)
483
+
484
+ # Returns the post (not a version) as it was most recently.
485
+ post.previous_version
486
+
487
+ # Returns the post (not a version) as it became next.
488
+ post.next_version
489
+
490
+
491
+ # Turn Audited on for all posts.
492
+ post.audited_on!
493
+
494
+ # Turn Audited off for all posts.
495
+ post.audited_off!
496
+ ```
497
+
498
+
499
+
500
+ <br>
501
+
502
+ ----
503
+
504
+ <br>
505
+
506
+
507
+ ## TODO's
508
+
509
+ Not everything is perfect or fully formed, so this gem may be in need of the following:
510
+
511
+ * It needs some **stress testing** and **THREADS support & testing**. Does the gem work in all
512
+ situations / instances?
513
+
514
+ I really would appreciate the wisdom of someone with a good understanding of these type of
515
+ things. Please help me ensure it's working great at all times.
516
+
517
+
518
+ * It could probably be cleaned up and made more efficient by a much better programmer than me.
519
+ Please feel free to provide some suggestions or pull-requests.
520
+
521
+
522
+ * Solid **testing and support for more DB's, other than PostgreSQL and SQLite3** currently tested
523
+ against. Not a priority as I currently have no such requirements. Please feel free to
524
+ submit a pull-request.
525
+
526
+ * Testing for use with Rails, Sinatra or other Ruby frameworks. I don't see much issues here, but
527
+ I'm NOT bothered to do this testing as [Roda](http://roda.jeremyevans.net/) is my preferred
528
+ Ruby framework. Please feel free to submit a pull-request.
529
+
530
+ * Support for `:on => [:create, :update]` option to limit auditing to only some actions. Not sure
531
+ if this is really worthwhile, but could be added as a feature. Please feel free to submit a
532
+ pull-request.
533
+
534
+ * Support for sweeping (compacting) old updates if there are too many. Not sure how to handle this.
535
+ Suggestions and ideas are most welcome.
536
+
537
+ I think a simple cron job could extract all records with `event: 'update'` older than a specific
538
+ time period (3 - 6 months) and dump them into something else, instead of adding this feature.
539
+
540
+ If you are running this on a free app on Heroku, with many and frequent updates, you might want
541
+ to pay attention to this functionality as there's a 10,000 rows limit on Heroku.
542
+
543
+
544
+
545
+
546
+ ## Development
547
+
548
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run
549
+ the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
550
+
551
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version,
552
+ update the version number in `version.rb`, and then run `bundle exec rake release`, which will create
553
+ a git tag for the version, push git commits and tags, and push the `.gem` file to
554
+ [rubygems.org](https://rubygems.org).
555
+
556
+
557
+
558
+ ## Contributing
559
+
560
+ Bug reports and pull requests are welcome on GitHub at https://github.com/jnylen/sequel_audited.
561
+
562
+ Please run `bundle exec rake coverage` and `bundle exec rake rubocop` on your code before you
563
+ send a pull-request.
564
+
565
+
566
+ This project is intended to be a safe, welcoming space for collaboration, and contributors are
567
+ expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
568
+
569
+
570
+ ## License
571
+
572
+ &copy; Copyright Kematzy, 2015<br>
573
+ &copy; Copyright jnylen, 2017
574
+
575
+ Heavily inspired by:
576
+
577
+ * the [audited](https://github.com/collectiveidea/audited) gem by Brandon Keepers, Kenneth Kalmer,
578
+ Daniel Morrison, Brian Ryckbost, Steve Richert & Ryan Glover released under the MIT licence.
579
+
580
+ * the [paper_trail](https://github.com/airblade/paper_trail) gem by Andy Stewart & Ben Atkins
581
+ released under the MIT license.
582
+
583
+ * the [sequel](https://github.com/jeremyevans/sequel) gem by Jeremy Evans and many others released
584
+ under the MIT license.
585
+
586
+ The gem is available as open source under the terms of the
587
+ [MIT License](http://opensource.org/licenses/MIT).