attic 0.6.pre.RC1 → 0.9.0.pre.rc2

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: cab1b37cf99fda2d7f47b898c998af908e2c8f4dcdde31ae66cd34d7c337a993
4
- data.tar.gz: 7d579f1320f3493b6639705d316aaf1197f77aa20894ecff84abd0a34a162124
3
+ metadata.gz: 5b8515c2812b3e4523d79d1fae98c348f041b4d6862d632e7a41ff4fb6555d7a
4
+ data.tar.gz: 29f3b9a07abb04e0d4611724a015ff8a0fcb704d7d8f3038d4ecd99ed6ae6768
5
5
  SHA512:
6
- metadata.gz: 920f145e1b3eb27151653a69be076fe3224c82e2781695209e2017ac4634708855dc0331ad254a9765bd185f25e566b1c6a712bc6118668c201ec0fa240239fc
7
- data.tar.gz: e0e4204bd76ae3f76c7c3a51166c278f69b2152db651a2ccd28ce8d559f281bfbf18e253713608fad2d9d488b96f52d3397070de7cc606842c3f47971db1276a
6
+ metadata.gz: 37911f6e23435f00b28f5d75643d2e250ee760c9ddee7049b6296dc3f5eaf6a00a48a9a6d3c484e9e9fd24e3feb15b57afaf7cc33d1e9456be80a8383840a360
7
+ data.tar.gz: bf9811b8008f3d292c0be8087202785ada8fda996ba352194d8d77652fc997ef0c2d9fe324f710493459f6aa79939472569d11bd68a25b6e4c7c31021b8c9c05
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ .DS_Store
2
+ .bundle
3
+ .byebug*
4
+ .history
5
+ .devcontainer
6
+ .vscode
7
+ *.env
8
+ *.log
9
+ *.md
10
+ *.txt
11
+ !LICENSE.txt
12
+ .ruby-version
13
+ appendonlydir
14
+ Gemfile.lock
15
+ etc/config
16
+ log
17
+ tmp
18
+ vendor
data/.rubocop.yml ADDED
@@ -0,0 +1,28 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ TargetRubyVersion: 3.1, 3.2
4
+ EnabledByDefault: false
5
+
6
+ Style/StringLiterals:
7
+ Enabled: true
8
+ EnforcedStyle: double_quotes
9
+
10
+ Style/StringLiteralsInInterpolation:
11
+ Enabled: true
12
+ EnforcedStyle: double_quotes
13
+
14
+ Layout/LineLength:
15
+ Enabled: true
16
+ Max: 120
17
+
18
+ Metrics/CyclomaticComplexity:
19
+ Enabled: false
20
+
21
+ Layout/HashAlignment:
22
+ Enabled: false
23
+
24
+ Lint/Void:
25
+ Enabled: false
26
+
27
+ Metrics/MethodLength:
28
+ Enabled: false
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ ruby '>= 3.1'
6
+
7
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
8
+
9
+ # bundle install --without production
10
+ # bundle install --with development
11
+ # bundle install --without development
12
+ group "development" do
13
+ gem "byebug"
14
+ gem "pry"
15
+ gem "pry-doc"
16
+ gem "rubocop"
17
+ gem "tryouts", '2.2.0.pre.RC1'
18
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,63 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ ast (2.4.2)
5
+ byebug (11.1.3)
6
+ coderay (1.1.3)
7
+ drydock (0.6.9)
8
+ json (2.7.1)
9
+ language_server-protocol (3.17.0.3)
10
+ method_source (1.0.0)
11
+ parallel (1.24.0)
12
+ parser (3.3.0.5)
13
+ ast (~> 2.4.1)
14
+ racc
15
+ pry (0.14.2)
16
+ coderay (~> 1.1)
17
+ method_source (~> 1.0)
18
+ pry-doc (1.5.0)
19
+ pry (~> 0.11)
20
+ yard (~> 0.9.11)
21
+ racc (1.7.3)
22
+ rainbow (3.1.1)
23
+ regexp_parser (2.9.0)
24
+ rexml (3.2.6)
25
+ rubocop (1.62.1)
26
+ json (~> 2.3)
27
+ language_server-protocol (>= 3.17.0)
28
+ parallel (~> 1.10)
29
+ parser (>= 3.3.0.2)
30
+ rainbow (>= 2.2.2, < 4.0)
31
+ regexp_parser (>= 1.8, < 3.0)
32
+ rexml (>= 3.2.5, < 4.0)
33
+ rubocop-ast (>= 1.31.1, < 2.0)
34
+ ruby-progressbar (~> 1.7)
35
+ unicode-display_width (>= 2.4.0, < 3.0)
36
+ rubocop-ast (1.31.2)
37
+ parser (>= 3.3.0.4)
38
+ ruby-progressbar (1.13.0)
39
+ storable (0.10.pre.RC1)
40
+ sysinfo (0.9.0.pre.RC1)
41
+ drydock (< 1.0)
42
+ storable (= 0.10.pre.RC1)
43
+ tryouts (2.2.0.pre.RC1)
44
+ sysinfo (= 0.9.0.pre.RC1)
45
+ unicode-display_width (2.5.0)
46
+ yard (0.9.36)
47
+
48
+ PLATFORMS
49
+ arm64-darwin-23
50
+ ruby
51
+
52
+ DEPENDENCIES
53
+ byebug
54
+ pry
55
+ pry-doc
56
+ rubocop
57
+ tryouts (= 2.2.0.pre.RC1)
58
+
59
+ RUBY VERSION
60
+ ruby 3.1.4p223
61
+
62
+ BUNDLED WITH
63
+ 2.5.7
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2009-2024 delano
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 CHANGED
@@ -1,54 +1,149 @@
1
- # Attic - v0.6-RC1 (2021-07-01)
1
+ # Attic - v1.0-RC1 (2023-03-15)
2
2
 
3
- A place to hide private instance variables in your Ruby objects.
3
+ A place to hide private instance variables in your Ruby objects: in the attic.
4
4
 
5
- ## Example
5
+ ## Description
6
+
7
+ Attic is a Ruby library that provides a place to hide private instance variables in your Ruby objects: in the attic.
8
+
9
+ Like, _why though_? Well sometimes you want to hide thing from the public interface of your object. You could use the `@@` prefix to place them at the class level but then you can't use them on a per-instance basis without creating a new data structure to store them. Attic does this for you in a transparent way using the Ruby singleton classes that are already there**.
10
+
11
+
12
+ ### Example
6
13
 
7
14
  ```ruby
8
15
  require 'attic'
9
16
 
17
+ # Extend a class with Attic
10
18
  class String
11
19
  extend Attic
20
+
21
+ # Add an instance variable to the attic. All instances
22
+ # of this class will have this variable available.
12
23
  attic :timestamp
13
24
  end
14
25
 
15
- a = "anything"
16
- a.timestamp = "1980-11-18"
26
+ # Instantiate a new String object
27
+ a = "A lovely example of a string"
28
+
29
+ # Set and get the timestamp
30
+ a.timestamp = "1990-11-18"
31
+ a.timestamp # => "1990-11-18"
32
+
33
+ # The timestamp is not visible in the public interface
17
34
  a.instance_variables # => []
18
- a.timestamp # 1980-11-18
19
35
 
36
+ # But it is available in the attic
20
37
  a.attic_variables # => [:timestamp]
21
38
 
39
+ # If you prefer getting your hands dirty, you can also
40
+ # interact with the attic at a lower level.
22
41
  a.attic_variable_set :tags, [:a, :b, :c]
23
- a.attic_variable_get :tags # [:a, :b, :c]
42
+ a.attic_variable_get :tags # => [:a, :b, :c]
24
43
 
44
+ # Looking at the attic again shows that the timestamp
45
+ # is still there too.
25
46
  a.attic_variables # => [:timestamp, :tags]
26
47
  ```
27
48
 
28
- ## Some objects have no metaclasses
29
49
 
30
- Symbol objects do not have metaclasses so instance variables are hidden in the object itself.
50
+ ### **Objects without singleton classes
31
51
 
52
+ Symbol, Integer, Float, TrueClass, FalseClass, NilClass, and Fixnum are all objects that do not have singleton classes. TrueClass, FalseClass, and NilClass are all singletons themselves. Fixnum is a singleton of Integer.
32
53
 
33
- ## Installation
54
+ These objects do not have metaclasses so the attic is hidden in the object itself.
34
55
 
35
- Via Rubygems, one of:
56
+ ### Explore in irb
57
+
58
+ ```shell
59
+ $ irb -r attic
60
+ ```
61
+
62
+ ---
63
+
64
+ ## Installation
36
65
 
37
66
  ```shell
38
67
  $ gem install attic
39
68
  ```
40
69
 
70
+ ```shell
71
+ $ bundle install attic
72
+ ```
73
+
41
74
  or via download:
42
- * attic-latest.tar.gz[http://github.com/delano/attic/tarball/latest]
43
- * attic-latest.zip[http://github.com/delano/attic/zipball/latest]
75
+ * [attic-latest.tar.gz](https://github.com/delano/attic/tarball/latest)
76
+ * [attic-latest.zip](https://github.com/delano/attic/zipball/latest)
77
+
78
+
79
+ ## Proofs
80
+
81
+ Tested the following code in IRB for Ruby 2.6.8 and 3.0.2:
82
+
83
+ ```ruby
84
+ rquire 'pp'
85
+
86
+ test_values = [:sym, 1, 1.01, Symbol, Integer, Float, String, TrueClass, FalseClass, NilClass, '', true, false, nil]
87
+
88
+ results = test_values.map do |value|
89
+ { value: value,
90
+ class: value.class,
91
+ attic: [value.attic?, value.attic? && value.attic.object_id] }
92
+ end
93
+ ```
44
94
 
95
+ which produced the same results for both.
96
+
97
+ ```ruby
98
+ attic> RUBY_VERSION
99
+ => "3.2.0"
100
+
101
+ pp results
102
+ [
103
+ {:value=>:sym, :class=>Symbol, :attic=>[false, false]},
104
+ {:value=>1, :class=>Integer, :attic=>[false, false]},
105
+ {:value=>1.01, :class=>Float, :attic=>[false, false]},
106
+ {:value=>Symbol, :class=>Class, :attic=>[true, 564680]},
107
+ {:value=>Integer, :class=>Class, :attic=>[true, 564700]},
108
+ {:value=>Float, :class=>Class, :attic=>[true, 564720]},
109
+ {:value=>String, :class=>Class, :attic=>[true, 564740]},
110
+ {:value=>TrueClass, :class=>Class, :attic=>[true, 564760]},
111
+ {:value=>FalseClass, :class=>Class, :attic=>[true, 564780]},
112
+ {:value=>NilClass, :class=>Class, :attic=>[true, 564800]},
113
+ {:value=>"", :class=>String, :attic=>[true, 602880]}
114
+ {:value=>true, :class=>TrueClass, :attic=>[true, 13840]},
115
+ {:value=>false, :class=>FalseClass, :attic=>[true, 13860]},
116
+ {:value=>nil, :class=>NilClass, :attic=>[true, 40]}
117
+ ]
118
+ ```
119
+ ```ruby
120
+ attic> RUBY_VERSION
121
+ => "2.6.8"
122
+
123
+ pp results
124
+ [
125
+ {:value=>:sym, :class=>Symbol, :attic=>[false, false]},
126
+ {:value=>1, :class=>Integer, :attic=>[false, false]},
127
+ {:value=>1.01, :class=>Float, :attic=>[false, false]},
128
+ {:value=>Symbol, :class=>Class, :attic=>[true, 2844115920]},
129
+ {:value=>Integer, :class=>Class, :attic=>[true, 2844089400]},
130
+ {:value=>Float, :class=>Class, :attic=>[true, 2844087700]},
131
+ {:value=>String, :class=>Class, :attic=>[true, 2844122580]},
132
+ {:value=>TrueClass, :class=>Class, :attic=>[true, 2844136260]},
133
+ {:value=>FalseClass, :class=>Class, :attic=>[true, 2844136000]},
134
+ {:value=>NilClass, :class=>Class, :attic=>[true, 2844139060]},
135
+ {:value=>"", :class=>String, :attic=>[true, 2845261220]},
136
+ {:value=>true, :class=>TrueClass, :attic=>[true, 2844136280]},
137
+ {:value=>false, :class=>FalseClass, :attic=>[true, 2844136020]},
138
+ {:value=>nil, :class=>NilClass, :attic=>[true, 2844139080]}
139
+ ]
140
+ ```
45
141
 
46
142
  ## Credits
47
143
 
48
- * `gems@solutious.com` (@delano)
144
+ * (@delano) Delano Mandelbaum
49
145
 
50
146
 
51
147
  ## License
52
148
 
53
149
  MIT
54
-
data/attic.gemspec CHANGED
@@ -1,20 +1,23 @@
1
- @spec = Gem::Specification.new do |s|
2
- s.name = "attic"
3
- s.version = "0.6-RC1"
4
- s.summary = "When in doubt, store it in the attic"
5
- s.description = "Attic: a place to hide metadata about the class or variable itself (e.g. SHA hash summaries)."
6
- s.author = "Delano Mandelbaum"
7
- s.email = "gems@solutious.com"
8
- s.homepage = "http://github.com/delano/attic"
9
- s.executables = %w[]
10
- s.require_paths = %w[lib]
11
- s.extra_rdoc_files = %w[README.md]
12
- s.licenses = ["MIT"] # https://spdx.org/licenses/MIT-Modern-Variant.html
13
- s.rdoc_options = ["--line-numbers", "--title", s.summary, "--main", "README.md"]
14
- s.files = %w(
15
- README.md
16
- Rakefile
17
- attic.gemspec
18
- lib/attic.rb
19
- )
1
+ Gem::Specification.new do |s|
2
+ s.name = "attic"
3
+ s.version = "0.9.0-rc2"
4
+ s.summary = "When in doubt, store it in the attic"
5
+ s.description = "Attic: a place to hide metadata about the class or variable itself (e.g. SHA hash summaries)."
6
+ s.authors = ["Delano Mandelbaum"]
7
+ s.email = "gems@solutious.com"
8
+ s.homepage = "https://github.com/delano/attic"
9
+ s.license = "MIT"
10
+
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"]
15
+
16
+ s.required_ruby_version = Gem::Requirement.new(">= 2.6.0")
17
+
18
+ s.add_dependency "rake", ">= 13.0.6"
19
+
20
+ # Add development dependencies
21
+ s.add_development_dependency "rspec", "~> 3.0"
22
+ s.add_development_dependency "rubocop", "~> 1.0"
20
23
  end
@@ -0,0 +1,66 @@
1
+ #
2
+ # = Attic::ClassMethods
3
+ #
4
+ module Attic
5
+ # Adds a few methods for accessing the metaclass of an
6
+ # object. We do this with great caution since the Object
7
+ # class is as global as it gets in Ruby.
8
+ module ClassMethods
9
+ # A list of all the attic variables defined for this class.
10
+ attr_reader :attic_variables
11
+
12
+ # A quick way to check if the current object already has a
13
+ # dedicated singleton class. We want to know this because
14
+ # this is where our attic variables will be stored.
15
+ def attic?
16
+ return false if NoSingletonError.member? self
17
+
18
+ # NOTE: Calling this on an object for the first time lazily
19
+ # creates a singleton class for itself. Another way of doing
20
+ # the same thing is to attempt defining a singleton method
21
+ # for the object. In either case, objects that cannot have
22
+ # cannot have a dedicated singleton class (e.g. nil, true,
23
+ # false) will raise a TypeError. We rescue this and add the
24
+ # object to the NoSingletonError list so we don't have to
25
+ # keep trying to access its singleton class.
26
+ !singleton_class.nil?
27
+
28
+ rescue TypeError
29
+ # Remember for next time.
30
+ NoSingletonError.add_member self
31
+ false
32
+ end
33
+
34
+ def attic(name=nil)
35
+ return singleton_class if name.nil?
36
+
37
+ name = name.normalize
38
+
39
+ self.attic_variables << name unless attic_variable? name
40
+
41
+ safe_name = "@_attic_#{name}"
42
+ instance_variable_set(safe_name, name)
43
+ end
44
+
45
+ def attic_variable?(name)
46
+ attic_variables.include? name.normalize
47
+ end
48
+
49
+ def attic_variable_set(name, val)
50
+ unless attic_variable? name
51
+ attic_variables << name.normalize
52
+ end
53
+ attic.instance_variable_set("@_attic_#{name}", val)
54
+ end
55
+
56
+ def attic_variable_get(name)
57
+ instance_variable_get("@_attic_#{name}")
58
+ end
59
+
60
+ protected
61
+
62
+ def normalize(name)
63
+ name.to_s.gsub(/\@[\?\!\=]$/, '_').to_sym
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,70 @@
1
+
2
+ class NoMetaClass < RuntimeError
3
+ end
4
+
5
+ # = Object
6
+ #
7
+ # These methods are copied directly from _why's metaid.rb.
8
+ # See: http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html
9
+ class Object
10
+
11
+ unless defined?(::Object::NOMETACLASS)
12
+ # An Array of classes which do not have metaclasses.
13
+ NOMETACLASS = [Symbol, Fixnum].freeze
14
+ end
15
+
16
+ def nometaclass?
17
+ NOMETACLASS.member?(self)
18
+ end
19
+
20
+ def metaclass?
21
+ !NOMETACLASS.member?(self.class)
22
+ end
23
+
24
+ # A convenient method for getting the metaclass of the current object.
25
+ # i.e.
26
+ #
27
+ # class << self; self; end;
28
+ #
29
+ # NOTE: Some Ruby class do not have meta classes (see: NOMETACLASS).
30
+ # For these classes, this method returns the class itself. That means
31
+ # the instance variables will stored in the class itself.
32
+ def metaclass
33
+ if !self.metaclass?
34
+ raise NoMetaClass, self
35
+ else
36
+ class << self; self; end;
37
+ end
38
+ end
39
+
40
+ # Execute a block +&blk+ within the metaclass of the current object.
41
+ def meta_eval &blk; metaclass.instance_eval &blk; end
42
+
43
+ # Add an instance method called +name+ to metaclass for the current object.
44
+ # This is useful because it will be available as a singleton method
45
+ # to all subclasses too.
46
+ def meta_def name, &blk
47
+ meta_eval { define_method name, &blk }
48
+ end
49
+
50
+ # Add a class method called +name+ for the current object's class. This
51
+ # isn't so special but it maintains consistency with meta_def.
52
+ def class_def name, &blk
53
+ class_eval { define_method name, &blk }
54
+ end
55
+
56
+
57
+ # A convenient method for getting the metaclass of the metaclass
58
+ # i.e.
59
+ #
60
+ # self.metaclass.metaclass
61
+ #
62
+ def metametaclass; self.metaclass.metaclass; end
63
+
64
+ def metameta_eval &blk; metametaclass.instance_eval &blk; end
65
+
66
+ def metameta_def name, &blk
67
+ metameta_eval { define_method name, &blk }
68
+ end
69
+
70
+ end
@@ -0,0 +1,52 @@
1
+
2
+ # Attic: A special place to store instance variables.
3
+
4
+ class NoMetaClass < RuntimeError
5
+ end
6
+
7
+ # = NoSingletonError
8
+ #
9
+ # This error is raised when an attempt is made to access the
10
+ # attic of an object which does not have a singleton class.
11
+ #
12
+ # This is a TypeError because it is not an exceptional
13
+ # condition. It is simply a condition that is not supported
14
+ # by the Attic module.
15
+ #
16
+ # == Usage
17
+ #
18
+ # require 'attic'
19
+ # class MyClass
20
+ # include Attic
21
+ # attic :name, :age
22
+ # end
23
+ #
24
+ class NoSingletonError < TypeError
25
+ unless defined?(MEMBERS)
26
+ # A Set of classes which do not have singleton classes
27
+ # (i.e. meta classes). This is used to prevent an exception
28
+ # the first time an attic is accessed. It's populated
29
+ # dynamically at start time by simply checking whether
30
+ # the object has a singleton. This only needs to be
31
+ # done once per class.
32
+ #
33
+ # We use a set here to avoid having to deal with duplicate
34
+ # values. Realistically there are only a few classes that
35
+ # do not have singleton classes. We could hard code them
36
+ # here which is not lost on us.
37
+ #
38
+ MEMBERS = Set.new
39
+ end
40
+
41
+ # Check if the given object is a member of the NoSingleton
42
+ # members list. This checks for the object itself and all
43
+ # of its ancestors. See the docs for `Enumerable#===` for
44
+ # more details.
45
+ def self.member?(obj)
46
+ MEMBERS === obj
47
+ end
48
+
49
+ def self.add_member(obj)
50
+ MEMBERS.merge [self]
51
+ end
52
+ end
@@ -0,0 +1,36 @@
1
+ #
2
+
3
+ # = Attic::InstanceMethods
4
+ #
5
+ module Attic
6
+ # Adds a few methods for object instances to access the
7
+ # attic variables of their class.
8
+ module InstanceMethods
9
+
10
+ def attic
11
+ raise NoSingleton, self, caller unless attic?
12
+
13
+ singleton_class
14
+
15
+ rescue TypeError
16
+ NoSingleton.add_member self
17
+ end
18
+
19
+ def attic_variables
20
+ self.class.attic_variables
21
+ end
22
+
23
+ def attic_variable?(name)
24
+ self.class.attic_variable? name
25
+ end
26
+
27
+ def attic_variable_set(name, val)
28
+ attic_variables << name unless attic_variable? name
29
+ attic.instance_variable_set("@___attic_#{name}", val)
30
+ end
31
+
32
+ def attic_variable_get(name)
33
+ attic.instance_variable_get("@___attic_#{name}")
34
+ end
35
+ end
36
+ end