gibbler 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ ![](https://delanotes.com/gibbler/img/whoababy.gif)
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
@@ -1,4 +1,4 @@
1
- ---
1
+ ---
2
2
  :MAJOR: 0
3
- :MINOR: 9
3
+ :MINOR: 10
4
4
  :PATCH: 0
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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
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
- s.name = "gibbler"
8
- s.version = "0.9.0"
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
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Delano Mandelbaum"]
12
- s.date = "2012-04-20"
13
- s.description = "Gibbler: Git-like hashes for Ruby objects"
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
- if s.respond_to? :specification_version then
60
- s.specification_version = 3
16
+ s.required_ruby_version = Gem::Requirement.new(">= 2.6.8")
61
17
 
62
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
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
@@ -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 (<tt>self = previous_self</tt>) so each
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
-
@@ -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 Fixnum; include Gibbler::String; end
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