sequel-auditer 0.1.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
+ SHA256:
3
+ metadata.gz: 44286a93587c09bbef33c1977f2d721081054844118d3928f4b85505e075c5aa
4
+ data.tar.gz: 189ea2f2dcaecc8e14126d8915fa9c574ecbfd402f9e726ec6e32f27f58e4db1
5
+ SHA512:
6
+ metadata.gz: 194e4c5c7f5d6f1cf7d41f583c4a1048d938ed299beff7cf919e01597ad723adb699ecfd92feb89118d79e23712c0a6c8566264d436e5fc1a765e240a684e678
7
+ data.tar.gz: aa0386f43e8f235931e73fadfac67525c67b9b49b525d3d23f6aff9f8090227129ad3d8074846f37eb2457c1101f700540a1a24d33c76b354b672347e2bf1253
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,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at gencer@nienbo.co.uk. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in sequel-auditer.gemspec
6
+ 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,614 @@
1
+ # Sequel::Auditer
2
+
3
+ **sequel-auditer** 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-auditer'
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-auditer
45
+ ```
46
+
47
+
48
+ ### 2) Generate Migration
49
+
50
+ In your apps Rakefile add the following:
51
+
52
+ ```ruby
53
+ load 'tasks/sequel-auditer/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:migrate:install # Installs Sequel::auditer migration, but does not run it.
67
+ ....
68
+ ```
69
+
70
+ Run the sequel_audit rake task:
71
+
72
+ ```bash
73
+ bundle exec rake audited:migrate:install
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
+ ## Devise
83
+
84
+ This gem will try to get user from warden based authentications. When available, auditer will be fetched from warden, otherwise global function will be fired.
85
+
86
+
87
+ ### IMPORTANT SIDENOTE!
88
+
89
+ If you are using PostgreSQL as your database, then it's a good idea to convert the the `changed`
90
+ column to JSON type for automatic translations into a Ruby hash.
91
+
92
+ Otherwise, you have to use `JSON.parse(@v.changed)` to convert it to a hash if and when you want
93
+ to use it.
94
+
95
+ <br>
96
+
97
+ ----
98
+
99
+
100
+ <a name="usage"></a>
101
+ ## Usage
102
+
103
+
104
+ Using this plugin is fairly simple and straight-forward. Just add it to the model you wish to
105
+ have audits (versions) for.
106
+
107
+ ```ruby
108
+ # auditing single model
109
+ Post.plugin :auditer
110
+
111
+ # auditing all models. NOT RECOMMENDED!
112
+ Sequel::Model.plugin :auditer
113
+ ```
114
+
115
+ By default this will audit / version all columns on the model, **except** the default ignored columns configured in `Sequel::auditer.audited_default_ignored_columns` (see [Configuration Options](#configuration-options) below).
116
+
117
+
118
+ #### `plugin(:auditer)`
119
+
120
+ ```ruby
121
+ # Given a Post model with these columns:
122
+ [:id, :category_id, :title, :body, :author_id, :created_at, :updated_at]
123
+
124
+ # Auditing all columns*
125
+
126
+ Post.plugin :auditer
127
+
128
+ #=> [:id, :category_id, :title, :body, :author_id] # audited columns
129
+ #=> [:created_at, :updated_at] # ignored columns
130
+ ```
131
+ <br>
132
+
133
+ #### `plugin(:auditer, :only => [...])`
134
+
135
+ ```ruby
136
+ # Auditing a Single column
137
+
138
+ Post.plugin :auditer, only: [:title]
139
+
140
+ #=> [:title] # audited columns
141
+ #=> [:id, :category_id, :body, :author_id, :created_at, :updated_at] # ignored columns
142
+
143
+
144
+ # Auditing Multiple columns
145
+
146
+ Post.plugin :auditer, only: [:title, :body]
147
+ #=> [:title, :body] # audited columns
148
+ #=> [:id, :category_id, :author_id, :created_at, :updated_at] # ignored columns
149
+
150
+ ```
151
+ <br>
152
+
153
+ #### `plugin(:auditer, :except => [...])`
154
+
155
+ **NOTE!** this option does NOT ignore the default ignored columns, so use with care.
156
+
157
+ ```ruby
158
+ # Auditing all columns except specified columns
159
+
160
+ Post.plugin :auditer, except: [:title]
161
+
162
+ #=> [:id, :category_id, :author_id, :created_at, :updated_at] # audited columns
163
+ #=> [:title] # ignored columns
164
+
165
+
166
+ Post.plugin :auditer, except: [:title, :author_id]
167
+
168
+ #=> [:id, :category_id, :created_at, :updated_at] # audited columns
169
+ #=> [:title, :author_id] # ignored columns
170
+
171
+ ```
172
+ <br>
173
+
174
+ ---
175
+
176
+ <br>
177
+
178
+ ## So what does it do??
179
+
180
+ You have to look behind the curtain to see what this plugin actually does.
181
+
182
+ In a new clean DB...
183
+
184
+ ### 1) Create
185
+
186
+ When you create a new record like this:
187
+
188
+ ```ruby
189
+ Category.create(name: 'Sequel')
190
+ #<Category @values={
191
+ :id => 1,
192
+ :name => "Sequel",
193
+ :position => 1,
194
+ :created_at => <timestamp>,
195
+ :updated_at => nil
196
+ }>
197
+
198
+ # in the background a new row in DB[:audit_logs] has been added with the following info:
199
+
200
+ #<AuditLog @values={
201
+ :id => 1,
202
+ :associated_type => "Category",
203
+ :associated_id => 1,
204
+ :event => "create",
205
+ # NOTE! all filled values are stored.
206
+ :changed => "{\"id\":1,\"name\":\"Sequel\",\"created_at\":\"<timestamp>\"}",
207
+ :version => 1,
208
+ :modifier_id => 88,
209
+ :modifier_type => "User",
210
+ :additional_info => "",
211
+ :created_at => <timestamp>
212
+ }>
213
+ ```
214
+
215
+ ### 2) Updates
216
+
217
+ When you update a record like this:
218
+
219
+ ```ruby
220
+ cat.update(name: 'Ruby Sequel')
221
+ #<Category @values={
222
+ :id => 1,
223
+ :name => "Ruby Sequel",
224
+ :position => 1,
225
+ :created_at => <timestamp>,
226
+ :updated_at => <timestamp>
227
+ }>
228
+
229
+ # in the background a new row in DB[:audit_logs] has been added with the following info:
230
+
231
+ #<AuditLog @values={
232
+ :id => 2,
233
+ :associated_type => "Category",
234
+ :associated_id => 1,
235
+ :event => "update",
236
+ # NOTE! only the changes are stored
237
+ :changed => "{\"name\":[\"Sequel\",\"Ruby Sequel\"],\"updated_at\":\"<timestamp>\"}",
238
+ :version => 2,
239
+ :modifier_id => 88,
240
+ :modifier_type => "User",
241
+ :additional_info => "",
242
+ :created_at => <timestamp>
243
+ }>
244
+ ```
245
+
246
+
247
+ ### 3) Destroys (Deletes)
248
+
249
+ When you delete a record like this:
250
+
251
+ ```ruby
252
+ cat.delete
253
+
254
+ # in the background a new row in DB[:audit_logs] is added with the info:
255
+
256
+ #<AuditLog @values={
257
+ :id => 3,
258
+ :associated_type => "Category",
259
+ :associated_id => 1,
260
+ :event => "destroy",
261
+ # NOTE! all values at exit time are stored
262
+ :changed => "{\"id\":1,\"name\":\"Ruby Sequel\",\"created_at\":\"<timestamp>\",\"updated_at\":\"<timestamp>\"}",
263
+ :version => 3,
264
+ :modifier_id => 88,
265
+ :modifier_type => "User",
266
+ :additional_info => "",
267
+ :created_at => <timestamp>
268
+ }>
269
+ ```
270
+
271
+
272
+ This way you can **easily track what was created, changed or deleted** and **who did it** and **when they did it**.
273
+
274
+ <br>
275
+
276
+ ---
277
+
278
+ <br>
279
+
280
+
281
+ <a name="configuration-options"></a>
282
+ ## Configuration Options
283
+
284
+
285
+ **sequel-auditer** supports two forms of configurations:
286
+
287
+ ### A) Global configuration options
288
+
289
+ #### `Sequel::auditer.audited_current_user_method`
290
+
291
+ Sets the name of the global method that provides the current user object.
292
+ Default is: `:current_user`.
293
+
294
+ You can easily change the name of this method by calling:
295
+
296
+ ```ruby
297
+ Sequel::auditer.audited_current_user_method = :auditer_user
298
+ ```
299
+
300
+ **Note!** the name of the function must be given as a symbol.
301
+ **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>
302
+ So if you want to customize the modifier per model you can do that here.
303
+
304
+ <br>
305
+
306
+ #### `Sequel::auditer.audited_additional_info_method`
307
+
308
+ Sets the name of the global method that provides the additional info object (Hash).
309
+ Default is: `:additional_info`.
310
+
311
+ You can easily change the name of this method by calling:
312
+
313
+ ```ruby
314
+ Sequel::auditer.audited_additional_info_method = :additional_info
315
+ ```
316
+
317
+ **Note!** the name of the function must be given as a symbol.
318
+ **Note!!** method should return a **Hash** value.
319
+
320
+ <br>
321
+
322
+
323
+ #### `Sequel::auditer.audited_model_name`
324
+
325
+ Enables adding your own Audit model. Default is: `:AuditLog`
326
+
327
+ ```ruby
328
+ Sequel:: Audited.audited_model_name = :YourCustomModel
329
+ ```
330
+ **Note!** the name of the model must be given as a symbol.
331
+ <br>
332
+
333
+
334
+ #### `Sequel::auditer.audited_enabled`
335
+
336
+ Toggle for enabling / disabling auditing throughout all audited models.
337
+ Default is: `true` i.e: enabled.
338
+
339
+ <br>
340
+
341
+
342
+ #### `Sequel::auditer.audited_default_ignored_columns`
343
+
344
+ An array of columns that are ignored by default. Default value is:
345
+
346
+ ```ruby
347
+ [:lock_version, :created_at, :updated_at, :created_on, :updated_on]
348
+ ```
349
+ NOTE! `:timestamps` related columns must be ignored or you may end up with situation
350
+ where an update triggers multiple copies of the record in the audit log.
351
+
352
+ <br>
353
+
354
+
355
+ ```ruby
356
+ # NOTE! array values must be given as symbols.
357
+ Sequel::auditer.audited_default_ignored_columns = [:id, :mycolumn, ...]
358
+ ```
359
+
360
+ <br>
361
+
362
+ ### B) Per Audited Model configurations
363
+
364
+ You can also set these settings on a per model setting by passing the following options:
365
+
366
+ #### `:user_method => :something`
367
+
368
+ This option will use a different method for the current user within this model only.
369
+
370
+ Example:
371
+
372
+ ```ruby
373
+ # if you have a global method like
374
+ def current_client
375
+ @current_client ||= Client[session[:client_id]]
376
+ end
377
+
378
+ # if you have a global method for info like
379
+ def additional_info
380
+ @additional_info ||= { ip: request.ip, user_agent: env['HTTP_USER_AGENT'] }
381
+ end
382
+
383
+ # and set
384
+ ClientProfile.plugin(:auditer, :user_method => :current_client, :additional_info => :additional_info)
385
+
386
+ # then the user info will be taken from DB[:clients].
387
+ #<Client @values={:id=>99,:username=>"happyclient"... }>
388
+
389
+ ```
390
+
391
+ **NOTE!** the current user model must respond to `:id` attributes.
392
+
393
+ <br>
394
+
395
+ #### `:default_ignored_columns => [...]`
396
+
397
+ This option allows you to set custom default ignored columns in the audited model. It's basically
398
+ just an option *just-in-case*, but it's probably better to use the `:only => []` or `:except => []`
399
+ options instead (see [Usage](#usage) above).
400
+
401
+ <br>
402
+
403
+ ----
404
+
405
+ <br>
406
+
407
+
408
+
409
+ ## Class Methods
410
+
411
+ You can easily track all changes made to a model / row / field(s) like this:
412
+
413
+
414
+ ### `#.audited_version?`
415
+
416
+ ```ruby
417
+ # check if model have any audits (only works on audited models)
418
+ Post.audited_versions?
419
+ #=> returns true / false if any audits have been made
420
+ ```
421
+
422
+ ### `#.audited_version([conditions])`
423
+
424
+ ```ruby
425
+ # grab all audits for a particular model. Returns an array.
426
+ Post.audited_versions
427
+ #=> [
428
+ { id: 1, associated_type: 'Post', associated_id: '11', version: 1,
429
+ changed: "{JSON SERIALIZED OBJECT}", modifier_id: 88,
430
+ username: "joeblogs", created_at: TIMESTAMP
431
+ },
432
+ {...}
433
+ ]
434
+
435
+
436
+ # filtered by primary_key value
437
+ Posts.audited_versions(associated_id: 123)
438
+
439
+ # filtered by user :id value
440
+ Posts.audited_versions(modifier_id: 88)
441
+
442
+ # filtered to last two (2) days only
443
+ Posts.audited_versions(:created_at < Date.today - 2)
444
+
445
+ ```
446
+
447
+
448
+
449
+ 2) Track all changes made by a user / modifier_group.
450
+
451
+ ```ruby
452
+ joe = User[88]
453
+
454
+ joe.audited_versions
455
+ #=> returns all audits made by joe
456
+ ['SELECT * FROM `audit_logs` WHERE modifier_id = 88 ORDER BY created_at DESC']
457
+
458
+ joe.audited_versions(:associated_type => Post)
459
+ #=> returns all audits made by joe on the Post model
460
+ ['SELECT * FROM `audit_logs` WHERE modifier_id = 88 AND associated_type = 'Post' ORDER BY created_at DESC']
461
+ ```
462
+
463
+
464
+
465
+ ## Instance Mehtods
466
+
467
+ When you active `.plugin(:auditer)` in your model, you get these methods:
468
+
469
+
470
+ ### `.versions`
471
+
472
+ ```ruby
473
+ class Post < Sequel::Model
474
+ plugin :auditer # options here
475
+ end
476
+
477
+ # Returns this post's versions.
478
+ post.versions #=> []
479
+ ```
480
+
481
+
482
+ ### `.blame`
483
+ -- aliased as: `.last_audited_by`
484
+
485
+ ```ruby
486
+ # Returns the user model of the user who last changed the record
487
+ post.blame
488
+ post.last_audited_by #=> User model
489
+ ```
490
+
491
+
492
+ ### `.last_audited_at`
493
+ -- aliased as: `.last_audited_on`
494
+
495
+ ```ruby
496
+ # Returns the timestamp last changed the record
497
+ post.last_audited_at
498
+ post.last_audited_on #=> <timestamp>
499
+ ```
500
+
501
+
502
+ ### To be implemented
503
+
504
+ ```ruby
505
+ # Returns the post (not a version) as it looked at around the the given timestamp.
506
+ post.version_at(timestamp)
507
+
508
+ # Returns the objects (not Versions) as they were between the given times.
509
+ post.versions_between(start_time, end_time)
510
+
511
+ # Returns the post (not a version) as it was most recently.
512
+ post.previous_version
513
+
514
+ # Returns the post (not a version) as it became next.
515
+ post.next_version
516
+
517
+
518
+ # Turn Audited on for all posts.
519
+ post.audited_on!
520
+
521
+ # Turn Audited off for all posts.
522
+ post.audited_off!
523
+ ```
524
+
525
+
526
+
527
+ <br>
528
+
529
+ ----
530
+
531
+ <br>
532
+
533
+
534
+ ## TODO's
535
+
536
+ Not everything is perfect or fully formed, so this gem may be in need of the following:
537
+
538
+ * It needs some **stress testing** and **THREADS support & testing**. Does the gem work in all
539
+ situations / instances?
540
+
541
+ I really would appreciate the wisdom of someone with a good understanding of these type of
542
+ things. Please help me ensure it's working great at all times.
543
+
544
+
545
+ * It could probably be cleaned up and made more efficient by a much better programmer than me.
546
+ Please feel free to provide some suggestions or pull-requests.
547
+
548
+
549
+ * Solid **testing and support for more DB's, other than PostgreSQL and SQLite3** currently tested
550
+ against. Not a priority as I currently have no such requirements. Please feel free to
551
+ submit a pull-request.
552
+
553
+ * Testing for use with Rails, Sinatra or other Ruby frameworks. I don't see much issues here, but
554
+ I'm NOT bothered to do this testing as [Roda](http://roda.jeremyevans.net/) is my preferred
555
+ Ruby framework. Please feel free to submit a pull-request.
556
+
557
+ * Support for `:on => [:create, :update]` option to limit auditing to only some actions. Not sure
558
+ if this is really worthwhile, but could be added as a feature. Please feel free to submit a
559
+ pull-request.
560
+
561
+ * Support for sweeping (compacting) old updates if there are too many. Not sure how to handle this.
562
+ Suggestions and ideas are most welcome.
563
+
564
+ I think a simple cron job could extract all records with `event: 'update'` older than a specific
565
+ time period (3 - 6 months) and dump them into something else, instead of adding this feature.
566
+
567
+ If you are running this on a free app on Heroku, with many and frequent updates, you might want
568
+ to pay attention to this functionality as there's a 10,000 rows limit on Heroku.
569
+
570
+
571
+
572
+
573
+ ## Development
574
+
575
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run
576
+ the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
577
+
578
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version,
579
+ update the version number in `version.rb`, and then run `bundle exec rake release`, which will create
580
+ a git tag for the version, push git commits and tags, and push the `.gem` file to
581
+ [rubygems.org](https://rubygems.org).
582
+
583
+
584
+
585
+ ## Contributing
586
+
587
+ Bug reports and pull requests are welcome on GitHub at https://github.com/jnylen/sequel-auditer.
588
+
589
+ Please run `bundle exec rake coverage` and `bundle exec rake rubocop` on your code before you
590
+ send a pull-request.
591
+
592
+
593
+ This project is intended to be a safe, welcoming space for collaboration, and contributors are
594
+ expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
595
+
596
+
597
+ ## License
598
+
599
+ &copy; Copyright Kematzy, 2015<br>
600
+ &copy; Copyright jnylen, 2017
601
+
602
+ Heavily inspired by:
603
+
604
+ * the [audited](https://github.com/collectiveidea/audited) gem by Brandon Keepers, Kenneth Kalmer,
605
+ Daniel Morrison, Brian Ryckbost, Steve Richert & Ryan Glover released under the MIT licence.
606
+
607
+ * the [paper_trail](https://github.com/airblade/paper_trail) gem by Andy Stewart & Ben Atkins
608
+ released under the MIT license.
609
+
610
+ * the [sequel](https://github.com/jeremyevans/sequel) gem by Jeremy Evans and many others released
611
+ under the MIT license.
612
+
613
+ The gem is available as open source under the terms of the
614
+ [MIT License](http://opensource.org/licenses/MIT).