with_attributes 0.1.0
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 +7 -0
- data/LICENSE +21 -0
- data/README.md +201 -0
- data/lib/with_attributes/version.rb +3 -0
- data/lib/with_attributes.rb +79 -0
- metadata +51 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1cd2b0040307ecf193dd4aeaa8e964a52676f1511b0fae3afe4452eaa27a9eb3
|
4
|
+
data.tar.gz: 645c20b445e473ae0063b434140fb8a0de5ae42c0d9b4be5512b71d7cfcb18d8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3b60ed7e8a7916f117077c4a1bf45cb19ce5760ffe4ebc969eb438ed8454cb721261ca0ccffaa69b145ff21dad9c4555628b27c0363d446b4108d6acad5095a5
|
7
|
+
data.tar.gz: 2c0c15826a68fac8af53c54eeca2c4203f3e24f17fccc8979bb915b7f52c81880cba280dfdb2954e094741156cc0dda19ab20ea244b53e050046416f04190380
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2024 Javier Aranda
|
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 all
|
13
|
+
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 THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
# WithAttributes
|
2
|
+
|
3
|
+
[](https://github.com/javierav/with_attributes/actions/workflows/testing.yml)
|
4
|
+
|
5
|
+
Temporarily enabling or disabling boolean attributes on classes and instances using `with` and `without` dynamic methods.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem "with_attributes"
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
```shell
|
18
|
+
bundle install
|
19
|
+
```
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
class User
|
25
|
+
extend WithAttributes
|
26
|
+
|
27
|
+
with_attribute :notifications, default: false
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
This add the following methods:
|
32
|
+
|
33
|
+
* `User.with_notifications` for enable notifications in class-level during block execution.
|
34
|
+
|
35
|
+
* `User.without_notifications` for disable notifications in class-level during block execution.
|
36
|
+
|
37
|
+
* `User.enable_notifications`for enable notifications in class-level permanently.
|
38
|
+
|
39
|
+
* `User.disable_notifications`for disable notifications in class-level permanently.
|
40
|
+
|
41
|
+
* `User.notifications?`for check current status of notifications in class-level, using default value if not inside `with_notifications` or `without_notifications` modifiers.
|
42
|
+
|
43
|
+
* `user.with_notifications` for enable notifications in instance-level during block execution.
|
44
|
+
|
45
|
+
* `user.without_notifications` for disable notifications in instance-level during block execution.
|
46
|
+
|
47
|
+
* `user.enable_notifications`for enable notifications in instance-level permanently.
|
48
|
+
- `user.disable_notifications`for disable notifications in instance-level permanently.
|
49
|
+
|
50
|
+
- `user.notifications?`for check current status of notifications in instance-level, using class-level value if not inside `with_notifications` or `without_notifications` modifiers.
|
51
|
+
|
52
|
+
Example using class-level methods:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
# using default value
|
56
|
+
User.notifications? # => false
|
57
|
+
|
58
|
+
User.with_notifications do
|
59
|
+
User.notifications? # => true
|
60
|
+
|
61
|
+
User.without_notifications do
|
62
|
+
User.notifications? # => false
|
63
|
+
end
|
64
|
+
|
65
|
+
User.notifications? # => true
|
66
|
+
end
|
67
|
+
|
68
|
+
User.notifications? # => false
|
69
|
+
```
|
70
|
+
|
71
|
+
Example using instance level methods:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
user = User.new
|
75
|
+
|
76
|
+
# if notifications is not changed in this instance, using value of class or default if class value is also not changed
|
77
|
+
user.notifications? # => false
|
78
|
+
|
79
|
+
# changing value in class-level
|
80
|
+
User.with_notifications do
|
81
|
+
user.notifications? # => true
|
82
|
+
end
|
83
|
+
|
84
|
+
# changing value in instance-level
|
85
|
+
user.with_notifications do
|
86
|
+
user.notifications? # => true
|
87
|
+
end
|
88
|
+
|
89
|
+
# using nested
|
90
|
+
User.with_notifications do
|
91
|
+
user.without_notifications do
|
92
|
+
user.notifications? # => false
|
93
|
+
end
|
94
|
+
end
|
95
|
+
```
|
96
|
+
|
97
|
+
## Real world example
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
module Trackable
|
101
|
+
extend ActiveSupport::Concern
|
102
|
+
|
103
|
+
class_methods do
|
104
|
+
include WithAttributes
|
105
|
+
end
|
106
|
+
|
107
|
+
included do
|
108
|
+
with_attribute :tracking, default: true
|
109
|
+
|
110
|
+
with_options if: :tracking? do
|
111
|
+
after_create { ... }
|
112
|
+
after_update { ... }
|
113
|
+
after_destroy { ... }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class User
|
119
|
+
include Trackable
|
120
|
+
end
|
121
|
+
|
122
|
+
User.without_tracking do
|
123
|
+
User.create(...)
|
124
|
+
end
|
125
|
+
|
126
|
+
User.find(...).tap do |user|
|
127
|
+
user.without_tracking do
|
128
|
+
user.update(...)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
```
|
132
|
+
|
133
|
+
|
134
|
+
|
135
|
+
## Thread safety
|
136
|
+
|
137
|
+
Thread safety is guaranteed using class-level methods. Instance-level methods only are thread safe if each thread not share the same instance with others.
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
# thread safe example
|
141
|
+
|
142
|
+
t1 = Thread.new do
|
143
|
+
User.without_notifications do
|
144
|
+
User.create(...) # user created without notifications
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
t2 = Thread.new do
|
149
|
+
User.with_notifications do
|
150
|
+
User.create(...) # user created with notifications
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
[t1, t2].map(&:join)
|
155
|
+
```
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
# thread safe example
|
159
|
+
|
160
|
+
t1 = Thread.new do
|
161
|
+
user = User.find(...)
|
162
|
+
|
163
|
+
user.without_notifications do
|
164
|
+
user.update(...) # user updated without notifications
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
t2 = Thread.new do
|
169
|
+
user = User.find(...)
|
170
|
+
|
171
|
+
user.with_notifications do
|
172
|
+
user.update(...) # user updated with notifications
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
[t1, t2].map(&:join)
|
177
|
+
```
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
# thread unsafe example
|
181
|
+
|
182
|
+
user = User.new
|
183
|
+
|
184
|
+
t1 = Thread.new do
|
185
|
+
user.without_notifications do
|
186
|
+
user.update(...) # unexpected behaviour, can be created with or without notifications
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
t2 = Thread.new do
|
191
|
+
user.with_notifications do
|
192
|
+
user.update(...) # unexpected behaviour, can be created with or without notifications
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
[t1, t2].map(&:join)
|
197
|
+
```
|
198
|
+
|
199
|
+
## Licence
|
200
|
+
|
201
|
+
Copyright © 2024 Javier Aranda. Released under the terms of the [MIT license](LICENSE).
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require_relative "with_attributes/version"
|
2
|
+
|
3
|
+
module WithAttributes
|
4
|
+
def with_attribute(*attrs, default: true)
|
5
|
+
raise ArgumentError, "invalid with_attribute default option" unless [true, false].include?(default)
|
6
|
+
|
7
|
+
attrs.each do |attr|
|
8
|
+
raise NameError, "invalid with_attribute name: #{attr}" unless /^[_A-Za-z]\w*$/.match?(attr)
|
9
|
+
|
10
|
+
class_eval(<<~CODE, __FILE__, __LINE__ + 1)
|
11
|
+
def self.#{attr}?
|
12
|
+
value = Thread.current["with_attribute_#{attr}_\#{object_id}"]
|
13
|
+
|
14
|
+
if value.nil?
|
15
|
+
if superclass.respond_to?(:#{attr}?)
|
16
|
+
superclass.#{attr}?
|
17
|
+
else
|
18
|
+
#{default}
|
19
|
+
end
|
20
|
+
else
|
21
|
+
value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.with_#{attr}
|
26
|
+
current = Thread.current["with_attribute_#{attr}_\#{object_id}"]
|
27
|
+
enable_#{attr}
|
28
|
+
yield if block_given?
|
29
|
+
ensure
|
30
|
+
Thread.current["with_attribute_#{attr}_\#{object_id}"] = current
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.without_#{attr}
|
34
|
+
current = Thread.current["with_attribute_#{attr}_\#{object_id}"]
|
35
|
+
disable_#{attr}
|
36
|
+
yield if block_given?
|
37
|
+
ensure
|
38
|
+
Thread.current["with_attribute_#{attr}_\#{object_id}"] = current
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.enable_#{attr}
|
42
|
+
Thread.current["with_attribute_#{attr}_\#{object_id}"] = true
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.disable_#{attr}
|
46
|
+
Thread.current["with_attribute_#{attr}_\#{object_id}"] = false
|
47
|
+
end
|
48
|
+
|
49
|
+
def #{attr}?
|
50
|
+
@#{attr}.nil? ? self.class.#{attr}? : @#{attr}
|
51
|
+
end
|
52
|
+
|
53
|
+
def with_#{attr}
|
54
|
+
current = @#{attr}
|
55
|
+
enable_#{attr}
|
56
|
+
yield if block_given?
|
57
|
+
ensure
|
58
|
+
@#{attr} = current
|
59
|
+
end
|
60
|
+
|
61
|
+
def without_#{attr}
|
62
|
+
current = @#{attr}
|
63
|
+
disable_#{attr}
|
64
|
+
yield if block_given?
|
65
|
+
ensure
|
66
|
+
@#{attr} = current
|
67
|
+
end
|
68
|
+
|
69
|
+
def enable_#{attr}
|
70
|
+
@#{attr} = true
|
71
|
+
end
|
72
|
+
|
73
|
+
def disable_#{attr}
|
74
|
+
@#{attr} = false
|
75
|
+
end
|
76
|
+
CODE
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
metadata
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: with_attributes
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Javier Aranda
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-12-08 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email: javier@aranda.dev
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- LICENSE
|
20
|
+
- README.md
|
21
|
+
- lib/with_attributes.rb
|
22
|
+
- lib/with_attributes/version.rb
|
23
|
+
homepage: https://github.com/javierav/with_attributes
|
24
|
+
licenses:
|
25
|
+
- MIT
|
26
|
+
metadata:
|
27
|
+
changelog_uri: https://github.com/javierav/with_attributes/releases
|
28
|
+
homepage_uri: https://github.com/javierav/with_attributes
|
29
|
+
source_code_uri: https://github.com/javierav/with_attributes/tree/v0.1.0
|
30
|
+
bug_tracker_uri: https://github.com/javierav/with_attributes/issues
|
31
|
+
rubygems_mfa_required: 'true'
|
32
|
+
post_install_message:
|
33
|
+
rdoc_options: []
|
34
|
+
require_paths:
|
35
|
+
- lib
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.0'
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
requirements: []
|
47
|
+
rubygems_version: 3.5.23
|
48
|
+
signing_key:
|
49
|
+
specification_version: 4
|
50
|
+
summary: temporarily enabling or disabling boolean attributes on classes and instances
|
51
|
+
test_files: []
|