gibbler 0.9.0 → 0.10.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/.github/workflows/main.yml +27 -0
- data/.github/workflows/ruby-rake.yaml +38 -0
- data/.gitignore +16 -0
- data/.pre-commit-config.yaml +60 -0
- data/.rubocop.yml +27 -0
- data/{CHANGES.txt → CHANGELOG.md} +65 -82
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +55 -0
- data/LICENSE.txt +4 -2
- data/README.md +258 -0
- data/VERSION.yml +2 -2
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/gibbler.gemspec +17 -66
- data/img/whoababy.gif +0 -0
- data/lib/gibbler/history.rb +39 -41
- data/lib/gibbler/mixins.rb +3 -4
- data/lib/gibbler/version.rb +17 -0
- data/lib/gibbler.rb +147 -187
- data/try/02_compat_try.rb +1 -10
- data/try/11_basic_try.rb +31 -25
- data/try/15_file_try.rb +8 -8
- metadata +75 -26
- data/README.rdoc +0 -236
- data/Rakefile +0 -39
data/README.md
ADDED
@@ -0,0 +1,258 @@
|
|
1
|
+
# Gibbler - v0.10.0
|
2
|
+
|
3
|
+
Git-like hashes and history for Ruby objects for Ruby 3.1+.
|
4
|
+
|
5
|
+
Check out [this post on RubyInside](http://www.rubyinside.com/gibbler-git-like-hashes-and-history-for-ruby-objects-1980.html).
|
6
|
+
|
7
|
+
* [Repo](https://github.com/delano/gibbler)
|
8
|
+
* [Docs](https://delanotes.com/gibbler)
|
9
|
+
* [Sponsor](https://solutious.com/)
|
10
|
+
* [Inspiration](https://www.youtube.com/watch?v=fipD4DdV48g)
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Install the gem and add to the application's Gemfile by executing:
|
15
|
+
|
16
|
+
```bash
|
17
|
+
$ bundle add gibbler
|
18
|
+
```
|
19
|
+
|
20
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
21
|
+
|
22
|
+
```bash
|
23
|
+
$ gem install gibbler
|
24
|
+
```
|
25
|
+
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
### Example 1 -- Standalone Usage
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
require 'gibbler'
|
33
|
+
|
34
|
+
g = Gibbler.new 'id', 1001 # => f4fb3796ababa3788d1bded8fdc589ab1ccb1c3d
|
35
|
+
g.base(36) # => sm71s7eam4hm5jlsuzlqkbuktwpe5h9
|
36
|
+
|
37
|
+
g == 'f4fb3796ababa3788d1bded8fdc589ab1ccb1c3d' # => true
|
38
|
+
g === 'f4fb379' # => true
|
39
|
+
```
|
40
|
+
|
41
|
+
### Example 2 -- Mixins Usage
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
require 'gibbler/mixins'
|
45
|
+
|
46
|
+
"kimmy".gibbler # => c8027100ecc54945ab15ddac529230e38b1ba6a1
|
47
|
+
:kimmy.gibbler # => 52be7494a602d85ff5d8a8ab4ffe7f1b171587df
|
48
|
+
|
49
|
+
config = {}
|
50
|
+
config.gibbler # => 4fdcadc66a38feb9c57faf3c5a18d5e76a6d29bf
|
51
|
+
config.gibbled? # => false
|
52
|
+
|
53
|
+
config[:server] = {
|
54
|
+
:users => [:dave, :ali],
|
55
|
+
:ports => [22, 80, 443]
|
56
|
+
}
|
57
|
+
config.gibbled? # => true
|
58
|
+
config.gibbler # => ef23d605f8c4fc80a8e580f9a0e8dab8426454a8
|
59
|
+
|
60
|
+
config[:server][:users] << :yanni
|
61
|
+
|
62
|
+
config.gibbler # => 4c558a56bc2abf5f8a845a69e47ceb5e0003683f
|
63
|
+
|
64
|
+
config.gibbler.short # => 4c558a56
|
65
|
+
|
66
|
+
config.gibbler.base36 # => 8x00l83jov4j80i9vfzpaxr9jag23wf
|
67
|
+
|
68
|
+
config.gibbler.base36.short # => 8x00l83j
|
69
|
+
```
|
70
|
+
|
71
|
+
### Example 3 -- Object History
|
72
|
+
|
73
|
+
Gibbler can also keep track of the history of changes to an object. By default Gibbler supports history for Hash, Array, and String objects. The `gibbler_commit` method creates a clone of the current object and stores in an instance variable using the current hash digest as the key.
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
require 'gibbler/mixins'
|
77
|
+
require 'gibbler/history'
|
78
|
+
|
79
|
+
a = { :magic => :original }
|
80
|
+
a.gibbler_commit # => d7049916ddb25e6cc438b1028fb957e5139f9910
|
81
|
+
|
82
|
+
a[:magic] = :updated
|
83
|
+
a.gibbler_commit # => b668098e16d08898532bf3aa33ce2253a3a4150e
|
84
|
+
|
85
|
+
a[:magic] = :changed
|
86
|
+
a.gibbler_commit # => 0b11c377fccd44554a601e5d2b135c46dc1c4cb1
|
87
|
+
|
88
|
+
a.gibbler_history # => d7049916, b668098e, 0b11c377
|
89
|
+
|
90
|
+
a.gibbler_revert! 'd7049916' # Return to a specific commit
|
91
|
+
a.gibbler # => d7049916ddb25e6cc438b1028fb957e5139f9910
|
92
|
+
a # => { :magic => :original }
|
93
|
+
|
94
|
+
a.delete :magic
|
95
|
+
|
96
|
+
a.gibbler_revert! # Return to the previous commit
|
97
|
+
a.gibbler # => 0b11c377fccd44554a601e5d2b135c46dc1c4cb1
|
98
|
+
a # => { :magic => :changed }
|
99
|
+
|
100
|
+
|
101
|
+
a.gibbler_object 'b668098e' # => { :magic => :updated }
|
102
|
+
a.gibbler_stamp # => 2009-07-01 18:56:52 -0400
|
103
|
+
```
|
104
|
+
|
105
|
+

|
106
|
+
|
107
|
+
|
108
|
+
### Example 4 -- Method Aliases
|
109
|
+
|
110
|
+
If you have control over the namespaces of your objects, you can use the method aliases to tighten up your code a bit. The "gibbler" and "gibbled?" methods can be accessed via "digest" and "changed?", respectively. (The reason they're not enabled by default is to avoid conflicts.)
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
require 'gibbler/aliases'
|
114
|
+
|
115
|
+
"kimmy".digest # => c8027100ecc54945ab15ddac529230e38b1ba6a1
|
116
|
+
:kimmy.digest # => 52be7494a602d85ff5d8a8ab4ffe7f1b171587df
|
117
|
+
|
118
|
+
a = [:a, :b, :c]
|
119
|
+
a.digest # => e554061823b8f06367555d1ee4c25b4ffee61944
|
120
|
+
a << :d
|
121
|
+
a.changed? # => true
|
122
|
+
```
|
123
|
+
|
124
|
+
The history methods also have aliases which remove the "gibbler_" prefix.
|
125
|
+
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
require 'gibbler/aliases'
|
129
|
+
require 'gibbler/history'
|
130
|
+
|
131
|
+
a = { :magic => :original }
|
132
|
+
a.commit
|
133
|
+
a.history
|
134
|
+
a.revert!
|
135
|
+
# etc...
|
136
|
+
```
|
137
|
+
|
138
|
+
### Example 5 -- Different Digest types
|
139
|
+
|
140
|
+
By default Gibbler creates SHA1 hashes. You can change this globally or per instance.
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
require 'gibbler/mixins'
|
144
|
+
|
145
|
+
Gibbler.digest_type = Digest::MD5
|
146
|
+
|
147
|
+
:kimmy.gibbler # => 0c61ff17f46223f355759934154d5dcb
|
148
|
+
|
149
|
+
:kimmy.gibbler(Digest::SHA1) # => 52be7494a602d85ff5d8a8ab4ffe7f1b171587df
|
150
|
+
```
|
151
|
+
|
152
|
+
In Jruby, you can grab the digest types from the openssl library.
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
require 'openssl'
|
156
|
+
|
157
|
+
Gibbler.digest_type = OpenSSL::Digest::SHA256
|
158
|
+
|
159
|
+
:kimmy.gibbler # => 1069428e6273cf329436c3dce9b680d4d4e229d7b7...
|
160
|
+
```
|
161
|
+
|
162
|
+
### Example 6 -- All your base
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
require 'gibbler/mixins'
|
166
|
+
|
167
|
+
:kimmy.gibbler # => 52be7494a602d85ff5d8a8ab4ffe7f1b171587df
|
168
|
+
:kimmy.gibbler.base(16) # => 52be7494a602d85ff5d8a8ab4ffe7f1b171587df
|
169
|
+
:kimmy.gibbler.base(36) # => 9nydr6mpv6w4k8ngo3jtx0jz1n97h7j
|
170
|
+
|
171
|
+
:kimmy.gibbler.base(10) # => 472384540402900668368761869477227308873774630879
|
172
|
+
:kimmy.gibbler.to_i # => 472384540402900668368761869477227308873774630879
|
173
|
+
```
|
174
|
+
|
175
|
+
### Example 7 -- Global secret
|
176
|
+
|
177
|
+
Gibbler can prepend all digest inputs with a global secret. You can set this once per project to ensure your project's digests are unique.
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
require 'gibbler/mixins'
|
181
|
+
|
182
|
+
:kimmy.gibbler # => 52be7494a602d85ff5d8a8ab4ffe7f1b171587df
|
183
|
+
|
184
|
+
Gibbler.secret = "sUp0r5ekRu7"
|
185
|
+
|
186
|
+
:kimmy.gibbler # => 6c5f5aff4d809cec7e7da091214a35a2698489f8
|
187
|
+
```
|
188
|
+
|
189
|
+
### Supported Classes
|
190
|
+
|
191
|
+
Gibbler methods are available only to the classes which explicitly include them [see docs'(https://delanotes.com/gibbler) for details on which classes are supported by default). You can also extend custom objects:
|
192
|
+
|
193
|
+
```ruby
|
194
|
+
class FullHouse
|
195
|
+
include Gibbler::Complex
|
196
|
+
attr_accessor :roles
|
197
|
+
end
|
198
|
+
|
199
|
+
a = FullHouse.new
|
200
|
+
a.gibbler # => 4192d4cb59975813f117a51dcd4454ac16df6703
|
201
|
+
|
202
|
+
a.roles = [:jesse, :joey, :danny, :kimmy, :michelle, :dj, :stephanie]
|
203
|
+
a.gibbler # => 6ea546919dc4caa2bab69799b71d48810a1b48fa
|
204
|
+
```
|
205
|
+
|
206
|
+
`Gibbler::Complex` creates a digest based on the name of the class and the names and values of the instance variables. See the RDocs[http://delano.github.com/gibbler] for other Gibbler::* types.
|
207
|
+
|
208
|
+
If you want to support all Ruby objects, add the following to your application:
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
class Object
|
212
|
+
include Gibbler::String
|
213
|
+
end
|
214
|
+
```
|
215
|
+
|
216
|
+
`Gibbler::String` creates a digest based on the name of the class and the output of the to_s method. This is a reasonable default for most objects however any object that includes the object address in to_s (e.g. "Object:0x0x4ac9f0...") will produce unreliable digests (because the address can change).
|
217
|
+
|
218
|
+
As of 0.7 all Proc objects have the same digest: `12075835e94be34438376cd7a54c8db7e746f15d`.
|
219
|
+
|
220
|
+
### Some things to keep in mind
|
221
|
+
|
222
|
+
* Digest calculation may change between minor releases (as it did between 0.6 and 0.7)
|
223
|
+
* Gibbler history is not suitable for very large objects since it keeps complete copies of the object in memory. This is a very early implementation of this feature so don't rely on it for production code.
|
224
|
+
* Don't forget to enjoy your life!
|
225
|
+
|
226
|
+
## What People Are Saying
|
227
|
+
|
228
|
+
* "nice approach - everything is an object, every object is 'gittish'" -- [@olgen_morten](https://twitter.com/olgen_morten/statuses/2629909133)
|
229
|
+
* "gibbler is just awesome" -- [@TomK32](https://twitter.com/TomK32/statuses/2618542872)
|
230
|
+
* "wie cool ist Gibbler eigentlich?" -- [@we5](https://twitter.com/we5/statuses/2615274279)
|
231
|
+
* "it's nice idea and implementation!" --[HristoHristov](https://www.rubyinside.com/gibbler-git-like-hashes-and-history-for-ruby-objects-1980.html#comment-39092)
|
232
|
+
|
233
|
+
## Development
|
234
|
+
|
235
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
236
|
+
|
237
|
+
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).
|
238
|
+
|
239
|
+
### Contributing
|
240
|
+
|
241
|
+
Bug reports and pull requests are welcome [GitHub Issues](https://github.com/delano/gibbler/issues). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/delano/gibbler/blob/main/CODE_OF_CONDUCT.md).
|
242
|
+
|
243
|
+
### Thanks
|
244
|
+
|
245
|
+
* Kalin Harvey ([krrh](https://github.com/kalin)) for the early feedback and artistic direction.
|
246
|
+
* Alex Peuchert ([aaalex](https://github.com/aaalex)) for creating the screencast.
|
247
|
+
|
248
|
+
* Andrea Barber
|
249
|
+
|
250
|
+
|
251
|
+
## Code of Conduct
|
252
|
+
|
253
|
+
Everyone interacting in the Gibbler project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/delano/gibbler/blob/main/CODE_OF_CONDUCT.md).
|
254
|
+
|
255
|
+
|
256
|
+
## License
|
257
|
+
|
258
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/VERSION.yml
CHANGED
data/bin/console
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "gibbler"
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
require "irb"
|
11
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/gibbler.gemspec
CHANGED
@@ -1,71 +1,22 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
-
# -*- encoding: utf-8 -*-
|
5
|
-
|
6
1
|
Gem::Specification.new do |s|
|
7
|
-
|
8
|
-
|
2
|
+
s.name = "gibbler"
|
3
|
+
s.version = "0.10.0"
|
4
|
+
s.summary = "Git-like hashes for Ruby objects"
|
5
|
+
s.description = "About Gibbler: Git-like hashes for Ruby objects"
|
6
|
+
s.authors = ["Delano Mandelbaum"]
|
7
|
+
s.email = "gems@solutious.com"
|
8
|
+
s.homepage = "https://github.com/delano/gibbler"
|
9
|
+
s.license = "MIT"
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
s.email = "delano@solutious.com"
|
15
|
-
s.extra_rdoc_files = [
|
16
|
-
"LICENSE.txt",
|
17
|
-
"README.rdoc"
|
18
|
-
]
|
19
|
-
s.files = [
|
20
|
-
"CHANGES.txt",
|
21
|
-
"LICENSE.txt",
|
22
|
-
"README.rdoc",
|
23
|
-
"Rakefile",
|
24
|
-
"VERSION.yml",
|
25
|
-
"gibbler.gemspec",
|
26
|
-
"lib/gibbler.rb",
|
27
|
-
"lib/gibbler/aliases.rb",
|
28
|
-
"lib/gibbler/history.rb",
|
29
|
-
"lib/gibbler/mixins.rb",
|
30
|
-
"try/01_core_ext_try.rb",
|
31
|
-
"try/02_compat_try.rb",
|
32
|
-
"try/05_gibbler_digest_try.rb",
|
33
|
-
"try/10_standalone_try.rb",
|
34
|
-
"try/11_basic_try.rb",
|
35
|
-
"try/12_basic_sha256_try.rb",
|
36
|
-
"try/14_extended_try.rb",
|
37
|
-
"try/15_file_try.rb",
|
38
|
-
"try/16_uri_try.rb",
|
39
|
-
"try/17_complex_object_try.rb",
|
40
|
-
"try/18_proc_try.rb",
|
41
|
-
"try/20_time_try.rb",
|
42
|
-
"try/30_secret_try.rb",
|
43
|
-
"try/50_history_try.rb",
|
44
|
-
"try/51_hash_history_try.rb",
|
45
|
-
"try/52_array_history_try.rb",
|
46
|
-
"try/53_string_history_try.rb",
|
47
|
-
"try/57_arbitrary_history_try.rb",
|
48
|
-
"try/59_history_exceptions_try.rb",
|
49
|
-
"try/80_performance_try.rb",
|
50
|
-
"try/90_alias_try.rb"
|
51
|
-
]
|
52
|
-
s.homepage = "http://github.com/delano/gibbler"
|
53
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
54
|
-
s.require_paths = ["lib"]
|
55
|
-
s.rubyforge_project = "gibbler"
|
56
|
-
s.rubygems_version = "1.8.22"
|
57
|
-
s.summary = "Gibbler: Git-like hashes for Ruby objects"
|
11
|
+
s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
12
|
+
s.bindir = "exe"
|
13
|
+
s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
14
|
+
s.require_paths = ["lib"]
|
58
15
|
|
59
|
-
|
60
|
-
s.specification_version = 3
|
16
|
+
s.required_ruby_version = Gem::Requirement.new(">= 2.6.8")
|
61
17
|
|
62
|
-
|
63
|
-
s.add_runtime_dependency(%q<attic>, [">= 0.4.0"])
|
64
|
-
else
|
65
|
-
s.add_dependency(%q<attic>, [">= 0.4.0"])
|
66
|
-
end
|
67
|
-
else
|
68
|
-
s.add_dependency(%q<attic>, [">= 0.4.0"])
|
69
|
-
end
|
70
|
-
end
|
18
|
+
s.add_dependency "rake", "~> 13.0"
|
71
19
|
|
20
|
+
s.add_development_dependency "rubocop", "~> 1.0"
|
21
|
+
s.add_development_dependency "tryouts", "~> 2.2"
|
22
|
+
end
|
data/img/whoababy.gif
ADDED
Binary file
|
data/lib/gibbler/history.rb
CHANGED
@@ -11,17 +11,17 @@ class Gibbler < String
|
|
11
11
|
class BadDigest < Gibbler::Error
|
12
12
|
def message; "Unknown digest: #{@obj}"; end
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
module History
|
16
16
|
extend Attic
|
17
|
-
|
17
|
+
|
18
18
|
attic :__gibbler_history
|
19
|
-
|
19
|
+
|
20
20
|
@@mutex = Mutex.new
|
21
|
-
|
21
|
+
|
22
22
|
def self.mutex; @@mutex; end
|
23
|
-
|
24
|
-
# Returns an Array of digests in the order they were committed.
|
23
|
+
|
24
|
+
# Returns an Array of digests in the order they were committed.
|
25
25
|
# If +short+ is anything but false, the digests will be converted
|
26
26
|
# to the short 8 character digests.
|
27
27
|
def gibbler_history(short=false)
|
@@ -37,104 +37,104 @@ class Gibbler < String
|
|
37
37
|
self.__gibbler_history[:history].collect { |g| g.short }
|
38
38
|
end
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
# Returns the object stored under the given digest +g+.
|
42
|
-
# If +g+ is not a valid digest, returns nil.
|
43
|
-
def gibbler_object(g=nil)
|
42
|
+
# If +g+ is not a valid digest, returns nil.
|
43
|
+
def gibbler_object(g=nil)
|
44
44
|
g = gibbler_find_long g
|
45
45
|
g = self.gibbler_history.last if g.nil?
|
46
46
|
|
47
47
|
return unless gibbler_valid? g
|
48
48
|
self.__gibbler_history[:objects][ g ]
|
49
49
|
end
|
50
|
-
|
51
|
-
# Returns the timestamp (a Time object) when the digest +g+ was committed.
|
52
|
-
# If +g+ is not a valid gibble, returns nil.
|
50
|
+
|
51
|
+
# Returns the timestamp (a Time object) when the digest +g+ was committed.
|
52
|
+
# If +g+ is not a valid gibble, returns nil.
|
53
53
|
def gibbler_stamp(g=nil)
|
54
54
|
g = gibbler_find_long g
|
55
55
|
g = self.gibbler_history.last if g.nil?
|
56
56
|
return unless gibbler_valid? g
|
57
57
|
self.__gibbler_history[:stamp][ g ]
|
58
58
|
end
|
59
|
-
|
59
|
+
|
60
60
|
# Stores a clone of the current object instance using the current
|
61
61
|
# digest value. If the object was not changed, this method does
|
62
|
-
# nothing but return the gibble.
|
62
|
+
# nothing but return the gibble.
|
63
63
|
#
|
64
64
|
# NOTE: This method is not fully thread safe. It uses a Mutex.synchronize
|
65
|
-
# but there's a race condition where two threads can attempt to commit at
|
66
|
-
# near the same time. The first will get the lock and create the commit.
|
67
|
-
# The second will get the lock and create another commit immediately
|
68
|
-
# after. What we probably want is for the second thread to return the
|
65
|
+
# but there's a race condition where two threads can attempt to commit at
|
66
|
+
# near the same time. The first will get the lock and create the commit.
|
67
|
+
# The second will get the lock and create another commit immediately
|
68
|
+
# after. What we probably want is for the second thread to return the
|
69
69
|
# digest for that first snapshot, but how do we know this was a result
|
70
70
|
# of the race conditioon rather than two legitimate calls for a snapshot?
|
71
71
|
def gibbler_commit
|
72
72
|
now, digest, point = nil,nil,nil
|
73
|
-
|
73
|
+
|
74
74
|
if self.__gibbler_history.nil?
|
75
75
|
@@mutex.synchronize {
|
76
76
|
self.__gibbler_history ||= { :history => [], :objects => {}, :stamp => {} }
|
77
77
|
}
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
80
|
@@mutex.synchronize {
|
81
81
|
now, digest, point = ::Time.now, self.gibbler, self.clone
|
82
82
|
self.__gibbler_history[:history] << digest
|
83
83
|
self.__gibbler_history[:stamp][digest] = now
|
84
84
|
self.__gibbler_history[:objects][digest] = point
|
85
85
|
}
|
86
|
-
|
86
|
+
|
87
87
|
digest
|
88
88
|
end
|
89
|
-
|
89
|
+
|
90
90
|
# Revert this object to a previously known state. If called without arguments
|
91
91
|
# it will revert to the most recent commit. If a digest is specified +g+, it
|
92
|
-
# will revert to that point.
|
92
|
+
# will revert to that point.
|
93
93
|
#
|
94
|
-
# Ruby does not support replacing self (
|
94
|
+
# Ruby does not support replacing self (`self = previous_self`) so each
|
95
95
|
# object type needs to implement its own __gibbler_revert! method. This default
|
96
|
-
# run some common checks and then defers to self.__gibbler_revert!.
|
97
|
-
#
|
96
|
+
# run some common checks and then defers to self.__gibbler_revert!.
|
97
|
+
#
|
98
98
|
# Raise the following exceptions:
|
99
99
|
# * NoRevert: if this object doesn't have a __gibbler_revert! method
|
100
100
|
# * NoHistory: This object has no commits
|
101
101
|
# * BadDigest: The given digest is not in the history for this object
|
102
102
|
#
|
103
|
-
# If +g+ matches the current digest value this method does nothing.
|
103
|
+
# If +g+ matches the current digest value this method does nothing.
|
104
104
|
#
|
105
|
-
# Returns the new digest (+g+).
|
105
|
+
# Returns the new digest (+g+).
|
106
106
|
def gibbler_revert!(g=nil)
|
107
107
|
raise NoRevert unless self.respond_to? :__gibbler_revert!
|
108
108
|
raise NoHistory, self.class unless gibbler_history?
|
109
109
|
raise BadDigest, g if !g.nil? && !gibbler_valid?(g)
|
110
|
-
|
110
|
+
|
111
111
|
g = self.gibbler_history.last if g.nil?
|
112
|
-
g = gibbler_find_long g
|
113
|
-
|
114
|
-
# Do nothing if the given digest matches the current gibble.
|
112
|
+
g = gibbler_find_long g
|
113
|
+
|
114
|
+
# Do nothing if the given digest matches the current gibble.
|
115
115
|
# NOTE: We use __gibbler b/c it doesn't update self.gibbler_cache.
|
116
116
|
unless self.__gibbler == g
|
117
117
|
@@mutex.synchronize {
|
118
|
-
# Always make sure self.gibbler_digest is a Gibbler::Digest
|
118
|
+
# Always make sure self.gibbler_digest is a Gibbler::Digest
|
119
119
|
self.gibbler_cache = g.is_a?(Gibbler::Digest) ? g : Gibbler::Digest.new(g)
|
120
120
|
self.__gibbler_revert!
|
121
121
|
}
|
122
122
|
end
|
123
|
-
|
123
|
+
|
124
124
|
self.gibbler_cache
|
125
125
|
end
|
126
|
-
|
126
|
+
|
127
127
|
# Is the given digest +g+ contained in the history for this object?
|
128
128
|
def gibbler_valid?(g)
|
129
129
|
return false unless gibbler_history?
|
130
130
|
gibbler_history.member? gibbler_find_long(g)
|
131
131
|
end
|
132
|
-
|
132
|
+
|
133
133
|
# Does the current object have any history?
|
134
134
|
def gibbler_history?
|
135
135
|
!gibbler_history.empty?
|
136
136
|
end
|
137
|
-
|
137
|
+
|
138
138
|
# Returns the long digest associated to the short digest +g+.
|
139
139
|
# If g is longer than 8 characters it returns the value of +g+.
|
140
140
|
def gibbler_find_long(g)
|
@@ -143,7 +143,7 @@ class Gibbler < String
|
|
143
143
|
gibbler_history.select { |d| d.match /\A#{g}/ }.first
|
144
144
|
end
|
145
145
|
end
|
146
|
-
|
146
|
+
|
147
147
|
end
|
148
148
|
|
149
149
|
class Hash
|
@@ -161,7 +161,7 @@ class Array
|
|
161
161
|
self.push *(self.gibbler_object self.gibbler_cache)
|
162
162
|
end
|
163
163
|
end
|
164
|
-
|
164
|
+
|
165
165
|
class String
|
166
166
|
include Gibbler::History
|
167
167
|
def __gibbler_revert!
|
@@ -169,5 +169,3 @@ class String
|
|
169
169
|
self << (self.gibbler_object self.gibbler_cache)
|
170
170
|
end
|
171
171
|
end
|
172
|
-
|
173
|
-
|
data/lib/gibbler/mixins.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
+
# rubocop:disable all
|
1
2
|
require 'gibbler'
|
2
3
|
|
3
4
|
class NilClass; include Gibbler::Nil; end
|
4
5
|
class String; include Gibbler::String; end
|
5
6
|
class Symbol; include Gibbler::String; end
|
6
|
-
class
|
7
|
-
class Bignum; include Gibbler::String; end
|
7
|
+
class Integer; include Gibbler::String; end
|
8
8
|
class TrueClass; include Gibbler::String; end
|
9
9
|
class FalseClass; include Gibbler::String; end
|
10
10
|
class Class; include Gibbler::Object; end
|
@@ -23,7 +23,7 @@ class TempFile; include Gibbler::File; end
|
|
23
23
|
class MatchData; include Gibbler::String; end
|
24
24
|
class OpenStruct; include Gibbler::Object; end
|
25
25
|
|
26
|
-
# URI::Generic must be included towards the
|
26
|
+
# URI::Generic must be included towards the
|
27
27
|
# end b/c it runs Object#freeze statically.
|
28
28
|
module URI; class Generic; include Gibbler::String; end; end
|
29
29
|
|
@@ -31,4 +31,3 @@ module URI; class Generic; include Gibbler::String; end; end
|
|
31
31
|
module Gem; class Platform; include Gibbler::Complex; end; end
|
32
32
|
|
33
33
|
module Addressable; class URI; include Gibbler::String; end; end
|
34
|
-
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gibbler
|
4
|
+
|
5
|
+
module VERSION
|
6
|
+
def self.to_s
|
7
|
+
load_config
|
8
|
+
[@version[:MAJOR], @version[:MINOR], @version[:PATCH]].join('.')
|
9
|
+
end
|
10
|
+
alias_method :inspect, :to_s
|
11
|
+
def self.load_config
|
12
|
+
require 'yaml'
|
13
|
+
@version ||= YAML.load_file(::File.join(GIBBLER_LIB_HOME, '..', 'VERSION.yml'))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|