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.
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