lockless 0.1.0 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 309a363dd9ca963e995c6e95b2e8b5a7d3b56e4a18e5a1db38c907f1849e3582
4
- data.tar.gz: 35ab5c79f82880b34f741a505232d75bd2b9675bf9be6362ec621d4314084947
3
+ metadata.gz: dfbf0af85c74d0fc3b05165382effa2efcc35648ba4d6fa0c378a9d7bb2b53ff
4
+ data.tar.gz: ab37c259d96aeb2689756b84748be5648ced851c374db572ff6d215fb17167c6
5
5
  SHA512:
6
- metadata.gz: 05c084e18869a15d55038959dd0e18c170b95f8ff11be5a9799cafaf9a8e88381a6c74394493b9739674e6ee7c824e2f17dc44abfbd748d0d27765e33f5c4a5e
7
- data.tar.gz: 86451dffd6f60aff22d91808d97a8712a472aef3e54574b14bfa9d7eb0152c91089213cfc2dcfdbb68f73c1d3f0ca24137569ed20b23478dc2a7f3dae01dbc32
6
+ metadata.gz: 99c8c3878e4353f8a8863dc0d4efb0b24a63ac6460fdf438274a225404cf0a106af52e677cf3869fced18aa736c9a0d3ad9afdee5fa97ae6219fb3cd54861360
7
+ data.tar.gz: c06923cbc493206e78bf8c5376a3b3ba21c2d7265ed461d2018fe50ca1358e2d1af66def2d03e015cd1a97ad032f4f944bacb15d6391e7ed60d7b97212948b1a
@@ -1,4 +1,4 @@
1
- name: Ruby
1
+ name: RubyCI
2
2
 
3
3
  on: [push,pull_request]
4
4
 
data/.rspec_status CHANGED
@@ -1,7 +1,9 @@
1
1
  example_id | status | run_time |
2
2
  ------------------------------------------ | ------ | --------------- |
3
- ./spec/lockless/model_spec.rb[1:1] | passed | 0.01249 seconds |
4
- ./spec/lockless/model_spec.rb[1:2:1:1] | passed | 0.02708 seconds |
5
- ./spec/lockless/model_spec.rb[1:2:2:1:1] | passed | 0.00505 seconds |
6
- ./spec/lockless/model_spec.rb[1:2:2:1:2:1] | passed | 0.02621 seconds |
7
- ./spec/lockless/model_spec.rb[1:2:2:2:1] | passed | 0.00658 seconds |
3
+ ./spec/lockless/model_spec.rb[1:1] | passed | 0.01148 seconds |
4
+ ./spec/lockless/model_spec.rb[1:2:1:1] | passed | 0.02232 seconds |
5
+ ./spec/lockless/model_spec.rb[1:3:1:1] | passed | 0.00415 seconds |
6
+ ./spec/lockless/model_spec.rb[1:3:2:1:1] | passed | 0.005 seconds |
7
+ ./spec/lockless/model_spec.rb[1:3:2:1:2:1] | passed | 0.03027 seconds |
8
+ ./spec/lockless/model_spec.rb[1:3:2:2:1] | passed | 0.00563 seconds |
9
+ ./spec/lockless/model_spec.rb[1:3:2:2:2] | passed | 0.00243 seconds |
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Lockless
2
2
 
3
- Concurrently update records without locks.
3
+ [![Gem Version](https://badge.fury.io/rb/lockless.png)](https://badge.fury.io/rb/lockless)
4
+ [![CI Status](https://github.com/cianmce/lockless/actions/workflows/main.yml/badge.svg)](https://github.com/cianmce/lockless/actions)
4
5
 
5
6
  Allows for safe concurrent updates to a single record without the need for locks.
6
7
 
@@ -54,6 +55,15 @@ class AddLocklessUuidToUsers < ActiveRecord::Migration[5.0]
54
55
  end
55
56
  ```
56
57
 
58
+ ### Custom column name
59
+
60
+ ```ruby
61
+ class User < ActiveRecord::Base
62
+ include Lockless::Model
63
+ self.lockless_column = :custom_uuid
64
+ end
65
+ ```
66
+
57
67
  ### Sample Usage
58
68
 
59
69
  ```ruby
@@ -66,15 +76,28 @@ user2.name = "new name2"
66
76
 
67
77
  # Save user2 before saving user1
68
78
  user2.lockless_save! # => true
79
+
80
+ # user1 fails to save as it's been updated earlier by user2
69
81
  user1.lockless_save! # => false
70
82
 
71
- # when lockless_save doesn't work you can either ignored the update
83
+ # when lockless_save doesn't work you can either ignore the update and continue
72
84
  # or reload the record and try again
85
+ # or calling `.save!` will forcefully save changes without respecting lockless
86
+ # `.save!` will cause the UUID to be changed again
73
87
 
74
88
  user1.reload
75
89
  user1.name # => "new name2"
76
90
  user1.name = "new name3"
77
91
  user1.lockless_save! # => true
92
+ user1.reload.name # => "new name3"
93
+
94
+ user1.reload
95
+ user1.name = "new name1"
96
+ User.all.update_all(name: "new name4")
97
+
98
+ # user1 fails to save as it's been updated earlier in `update_all`
99
+ user1.lockless_save! # => false
100
+ user1.reload.name # => "new name4"
78
101
  ```
79
102
 
80
103
  ## Development
@@ -83,13 +106,26 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
83
106
 
84
107
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
85
108
 
86
- ### Run all specs + standardrb
109
+ ### Testing
110
+ #### Run all specs + standardrb
87
111
 
88
112
  ```sh
89
113
  bundle exec rake
90
114
  ```
91
115
 
92
- ### Run specs using guard
116
+ #### Run only standardrb
117
+
118
+ ```sh
119
+ bundle exec rake standard
120
+ ````
121
+
122
+ #### Apply standardrb auto fixes
123
+
124
+ ```sh
125
+ bundle exec rake standard:fix
126
+ ```
127
+
128
+ #### Run specs using guard
93
129
 
94
130
  ```sh
95
131
  bundle exec guard
@@ -111,6 +147,5 @@ Everyone interacting in the Lockless project's codebases, issue trackers, chat r
111
147
 
112
148
  ## TODO
113
149
 
114
- - [ ] Allow for custom lockless column name
115
150
  - [ ] Allow for custom primary key column name
116
151
  - [ ] Allow a boolean to be passed to allow for validation to be skipped like in `.save`
@@ -2,6 +2,11 @@
2
2
 
3
3
  module Lockless
4
4
  # Handles lockless saves
5
+
6
+ #
7
+ # Options:
8
+ #
9
+ # - :lockless_column - The columns used to track soft delete, defaults to `:lockless_uuid`.
5
10
  module Model
6
11
  extend ActiveSupport::Concern
7
12
 
@@ -10,6 +15,14 @@ module Lockless
10
15
  self.lockless_column = :lockless_uuid
11
16
 
12
17
  before_save :set_lockless_uuid
18
+
19
+ delegate :generate_uuid, :lockless_column, to: :class
20
+ end
21
+
22
+ class_methods do
23
+ def generate_uuid
24
+ SecureRandom.uuid
25
+ end
13
26
  end
14
27
 
15
28
  # Saves record if it has not be modified in the time after it was loaded from the database.
@@ -18,18 +31,18 @@ module Lockless
18
31
  # false is returned if record is outdated or invalid
19
32
  def lockless_save
20
33
  return false unless valid?
21
- old_lockless_uuid = lockless_uuid
34
+ old_lockless_uuid = public_send(lockless_column)
22
35
  return save if new_record?
23
36
 
24
37
  run_callbacks(:save) do |variable|
25
38
  new_attrs = changed.collect { |prop| [prop.to_sym, self[prop]] }.to_h
26
39
 
27
- update_count = self.class.where(id: id, lockless_uuid: old_lockless_uuid).update_all(new_attrs)
40
+ update_count = self.class.where(:id => id, lockless_column => old_lockless_uuid).update_all(new_attrs)
28
41
  if update_count == 1
29
42
  changes_applied
30
43
  true
31
44
  else
32
- self.lockless_uuid = old_lockless_uuid
45
+ public_send("#{lockless_column}=", old_lockless_uuid)
33
46
  false
34
47
  end
35
48
  end
@@ -49,7 +62,7 @@ module Lockless
49
62
  private
50
63
 
51
64
  def set_lockless_uuid
52
- self.lockless_uuid = SecureRandom.uuid
65
+ public_send("#{lockless_column}=", generate_uuid)
53
66
  end
54
67
  end
55
68
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lockless
4
+ module Relation
5
+ # Appends update attributes with a random UUID when the model is a lockless model
6
+ #
7
+ # @param [String, Array, Hash] A string, array, or hash representing the SET part of an SQL statement.
8
+ # Lockless will only append random UUID updates if the param is a Hash
9
+ #
10
+ # @return [Boolean] Similar to `.save`, true is returned if the model is updated
11
+ # false is returned if record is outdated or invalid
12
+ def update_all(updates)
13
+ if updates.is_a?(Hash)
14
+ if model.method_defined?(:lockless_column) && updates[model.lockless_column].blank?
15
+ updates[model.lockless_column] = model.generate_uuid
16
+ end
17
+ end
18
+
19
+ super(updates)
20
+ end
21
+ end
22
+ end
23
+
24
+ ActiveRecord::Relation.prepend(Lockless::Relation)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Lockless
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.2"
5
5
  end
data/lib/lockless.rb CHANGED
@@ -4,3 +4,4 @@ require "active_record"
4
4
 
5
5
  require "lockless/version"
6
6
  require "lockless/model"
7
+ require "lockless/relation"
data/lockless.gemspec CHANGED
@@ -14,10 +14,9 @@ Gem::Specification.new do |spec|
14
14
  spec.license = "MIT"
15
15
  spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
16
16
 
17
- # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
18
-
19
17
  spec.metadata["homepage_uri"] = spec.homepage
20
18
  spec.metadata["source_code_uri"] = spec.homepage
19
+ spec.metadata["documentation_uri"] = "https://www.rubydoc.info/gems/lockless/#{Lockless::VERSION}"
21
20
 
22
21
  # Specify which files should be added to the gem when it is released.
23
22
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -28,12 +27,6 @@ Gem::Specification.new do |spec|
28
27
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
29
28
  spec.require_paths = ["lib"]
30
29
 
31
- # Uncomment to register a new dependency of your gem
32
- # spec.add_dependency "example-gem", "~> 1.0"
33
-
34
- # For more information and examples about making a new gem, checkout our
35
- # guide at: https://bundler.io/guides/creating_gem.html
36
-
37
30
  spec.add_development_dependency "standardrb", "~> 1.0"
38
31
  spec.add_development_dependency "bundler", ">= 1"
39
32
  spec.add_development_dependency "rake", "~> 13.0"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lockless
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cian McElhinney
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-11-21 00:00:00.000000000 Z
11
+ date: 2021-11-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: standardrb
@@ -215,6 +215,7 @@ files:
215
215
  - bin/setup
216
216
  - lib/lockless.rb
217
217
  - lib/lockless/model.rb
218
+ - lib/lockless/relation.rb
218
219
  - lib/lockless/version.rb
219
220
  - lockless.gemspec
220
221
  homepage: https://github.com/cianmce/lockless
@@ -223,6 +224,7 @@ licenses:
223
224
  metadata:
224
225
  homepage_uri: https://github.com/cianmce/lockless
225
226
  source_code_uri: https://github.com/cianmce/lockless
227
+ documentation_uri: https://www.rubydoc.info/gems/lockless/0.2.2
226
228
  post_install_message:
227
229
  rdoc_options: []
228
230
  require_paths: