attic 1.0.0.pre.RC2 → 1.0.0

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: 65eaa51d7ee74a1e97bef40a3cdb0c6cb8c78ca29e0cd8f8bb55e0cba641f664
4
- data.tar.gz: fbc75d2a1063522b54edfc6d608a2f4599ac2b34c39a92aa400b138138f19cd8
3
+ metadata.gz: ba2c5122967296a766b52ac046951567c4daf3845596533f297936bb094155d4
4
+ data.tar.gz: ad4d86d50d03b6b48c8f05f5cf06c27ed2df348a2adf7ef3b038b4efed3c9337
5
5
  SHA512:
6
- metadata.gz: 2a54f4d47b2aa70d9a8d6ee30578d5c06360e9e5c2ffd3c5dfa196086b40122dfee3d84b079cbabd9a24076262b8d8b4521d50a649fe111c3f8f3b80d20945da
7
- data.tar.gz: b06ab0a4a824c1536046f4d36ee0a42ce0e5315e214ef398955cf45f7046c56937d93333102044a1a4607fbe4780dd176d0377569126320e81f623c220527c16
6
+ metadata.gz: d884ee454dd855cb09315b995c0705bbc4c327dc5ea397589e3b8e6143ed54d7cfb36bf01f62ba99404a62243523a1ee4c35c3d95ca53b319a5acbecab64d9b5
7
+ data.tar.gz: 6e2391ebb5e6ff55b1c27c475bacadbb9c67f5d1e770b79e762dfdf2cb9bc8a4c982b370cacb2ca2436d9f291101eef733a2af11b9727436f51415876f8af33f
data/.gitignore CHANGED
@@ -11,8 +11,8 @@
11
11
  !LICENSE.txt
12
12
  .ruby-version
13
13
  appendonlydir
14
- Gemfile.lock
15
14
  etc/config
16
15
  log
17
16
  tmp
18
17
  vendor
18
+ *.gem
data/Gemfile CHANGED
@@ -2,9 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- ruby '>= 3.1'
6
-
7
- git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
5
+ ruby '>= 2.6.8', '< 4.0'
8
6
 
9
7
  # bundle install --without production
10
8
  # bundle install --with development
@@ -14,5 +12,5 @@ group "development" do
14
12
  gem "pry"
15
13
  gem "pry-doc"
16
14
  gem "rubocop"
17
- gem "tryouts", '2.2.0.pre.RC1'
15
+ gem "tryouts", '~> 2.2.0'
18
16
  end
data/Gemfile.lock CHANGED
@@ -47,6 +47,7 @@ GEM
47
47
 
48
48
  PLATFORMS
49
49
  arm64-darwin-22
50
+ ruby
50
51
 
51
52
  DEPENDENCIES
52
53
  byebug
@@ -59,4 +60,4 @@ RUBY VERSION
59
60
  ruby 3.2.0p0
60
61
 
61
62
  BUNDLED WITH
62
- 2.4.12
63
+ 2.5.7
data/attic.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "attic"
3
- s.version = "1.0.0-RC2"
3
+ s.version = "1.0.0"
4
4
  s.summary = "When in doubt, store it in the attic"
5
5
  s.description = "Attic: a place to hide metadata about the class or variable itself (e.g. SHA hash summaries)."
6
6
  s.authors = ["Delano Mandelbaum"]
@@ -15,9 +15,8 @@ Gem::Specification.new do |s|
15
15
 
16
16
  s.required_ruby_version = Gem::Requirement.new(">= 2.6.0")
17
17
 
18
- s.add_dependency "rake", ">= 13.0.6"
18
+ s.add_dependency "rake", "~> 13.0.6"
19
19
 
20
- # Add development dependencies
21
20
  s.add_development_dependency "rspec", "~> 3.0"
22
21
  s.add_development_dependency "rubocop", "~> 1.0"
23
22
  end
@@ -40,6 +40,8 @@ module Attic
40
40
 
41
41
  safe_name = "@_attic_#{name}"
42
42
  instance_variable_set(safe_name, name)
43
+ rescue TypeError => e
44
+ raise NoSingletonError, name, caller
43
45
  end
44
46
 
45
47
  def attic_variable?(name)
@@ -8,12 +8,39 @@ module Attic
8
8
  module InstanceMethods
9
9
 
10
10
  def attic
11
- raise NoSingleton, self, caller unless attic?
11
+ raise NoSingletonError, self, caller unless attic?
12
12
 
13
13
  singleton_class
14
14
 
15
15
  rescue TypeError
16
- NoSingleton.add_member self
16
+ NoSingletonError.add_member self
17
+ end
18
+
19
+ # A quick way to check if this object instance already has a
20
+ # dedicated singleton class. We like to know this upfront
21
+ # because this is where our attic variables are to be stored.
22
+ def attic?
23
+ return false if NoSingletonError.member? self
24
+
25
+ # NOTE: Calling this on an object for the first time lazily
26
+ # creates a singleton class for itself. Another way of doing
27
+ # the same thing is to attempt defining a singleton method
28
+ # for the object. In either case, objects that cannot have
29
+ # cannot have a dedicated singleton class (e.g. nil, true,
30
+ # false) will raise a TypeError. We rescue this and add the
31
+ # object to the NoSingletonError list so we don't keep
32
+ # trying to access its singleton class over and over.
33
+ #
34
+ # NOTE 2: Module#singleton_class? is only available for
35
+ # modules and classes (which are also modules); it is not
36
+ # available for instances of classes.
37
+ #
38
+ !singleton_class.nil?
39
+
40
+ rescue TypeError
41
+ # Remember for next time.
42
+ NoSingletonError.add_member self
43
+ false
17
44
  end
18
45
 
19
46
  def attic_variables
@@ -25,6 +52,7 @@ module Attic
25
52
  end
26
53
 
27
54
  def attic_variable_set(name, val)
55
+ raise FrozenError, self, caller if frozen?
28
56
  attic_variables << name unless attic_variable? name
29
57
  attic.instance_variable_set("@___attic_#{name}", val)
30
58
  end
data/lib/attic.rb CHANGED
@@ -100,7 +100,7 @@ require_relative "attic/instance_methods"
100
100
  # nil.attic.object_id #=> 800
101
101
  #
102
102
  module Attic
103
- VERSION = '0.9.0-RC1'.freeze unless defined?(VERSION)
103
+ VERSION = '1.0.0'.freeze unless defined?(VERSION)
104
104
 
105
105
  # A convenince method at the class level for including
106
106
  # ConstructMethods in the given object specifically.
@@ -146,20 +146,20 @@ module Attic
146
146
  attic_vars = self.attic_variables.clone
147
147
  klass.attic.instance_variable_set("@attic_variables", attic_vars)
148
148
  end
149
- if method_defined? :instance_variables
150
- instance_variables_orig = instance_method(:instance_variables)
151
- define_method :instance_variables do
152
- ret = _instance_variables_orig.bind(self).call.clone
149
+ if obj.method_defined? :instance_variables
150
+ instance_variables_orig = obj.instance_method(:instance_variables)
151
+ obj.define_method :instance_variables do
152
+ ret = instance_variables_orig.bind(self).call.clone
153
153
  ret.reject! { |v| v.to_s =~ /^@___?attic/ } # match 2 or 3 underscores
154
154
  ret
155
155
  end
156
- define_method :all_instance_variables do
157
- _instance_variables_orig.bind(self).call
156
+ obj.define_method :all_instance_variables do
157
+ instance_variables_orig.bind(self).call
158
158
  end
159
159
  end
160
160
 
161
161
  rescue TypeError => e
162
- raise NoSingleton, obj, caller
162
+ raise NoSingletonError, obj, caller
163
163
  end
164
164
 
165
165
  # A class method for defining variables to store in the attic.
@@ -203,6 +203,16 @@ module Attic
203
203
  attic_variables # only after defining new attic vars
204
204
  end
205
205
 
206
+ def attic?
207
+ return false if NoSingletonError.member? self
208
+
209
+ singleton_class?
210
+
211
+ rescue TypeError
212
+ NoSingletonError.add_member self
213
+ false
214
+ end
215
+
206
216
  # Returns an Array of attic variables for the current class.
207
217
  # e.g.
208
218
  #
@@ -49,3 +49,14 @@ ExampleClass.attic :kind
49
49
  a = ExampleClass.new
50
50
  a.kind
51
51
  #=> :unlikely_value
52
+
53
+ # If we try to add an attic variable to an instance,
54
+ # we get an ArgumentError (we can only add attic vars
55
+ # at the class level).
56
+ a = ExampleClass.new
57
+ begin
58
+ a.attic :colour
59
+ rescue => e
60
+ e.class
61
+ end
62
+ #=> ArgumentError
@@ -0,0 +1,67 @@
1
+ require_relative '../lib/attic'
2
+
3
+ # Tests for the frozen objects
4
+
5
+ # A class we define can extend Attic
6
+ class ::ExampleClass
7
+ extend Attic
8
+ end
9
+
10
+ ExampleClass.methods.member?(:attic)
11
+ #=> true
12
+
13
+ # Classes that extend Attic don't spawn frozen instances
14
+ a = ExampleClass.new
15
+ a.frozen?
16
+ #=> false
17
+
18
+ # Classes that extend Attic and have attic variables
19
+ # also don't spawn frozen instances
20
+ ExampleClass.attic :size
21
+ a = ExampleClass.new
22
+ a.frozen?
23
+ #=> false
24
+
25
+ # Instances can still freeze normally
26
+ a = ExampleClass.new
27
+ a.freeze
28
+ a.frozen?
29
+ #=> true
30
+
31
+ # Instances can still freeze normally even if they
32
+ # have attic with values
33
+ ExampleClass.attic :size
34
+ a = ExampleClass.new
35
+ a.size = 2
36
+ a.freeze
37
+ [a.frozen?, a.size]
38
+ #=> [true, 2]
39
+
40
+ # Frozen instances can't have their attic vars changed either
41
+ a = ExampleClass.new
42
+ a.size = 2
43
+ a.freeze
44
+ begin
45
+ a.size = 3
46
+ rescue => e
47
+ e.class
48
+ end
49
+ #=> FrozenError
50
+
51
+ # Regardless of the behaviour of the instance itself, once
52
+ # it's frozen, its singleton class is frozen too!
53
+ a = ExampleClass.new
54
+ a.freeze
55
+ a.singleton_class.frozen?
56
+ #=> true
57
+
58
+ # Frozen instances can't have their attic vars changed either
59
+ a = ExampleClass.new
60
+ a.size = 2
61
+ a.freeze
62
+ begin
63
+ a.size = 3
64
+ rescue => e
65
+ e.class
66
+ end
67
+ #=> FrozenError
@@ -1,29 +1,30 @@
1
- require 'attic'
1
+ require_relative '../lib/attic'
2
+
2
3
  class ::Worker
3
4
  extend Attic
4
5
  attic :size
5
6
  end
6
7
 
7
-
8
+
8
9
  ## save an instance variable the long way
9
10
  w = Worker.new
10
- w.metametaclass.instance_variable_set '@mattress', 'S&F'
11
- w.metametaclass.instance_variable_get '@mattress'
11
+ w.attic.instance_variable_set '@mattress', 'S&F'
12
+ w.attic.instance_variable_get '@mattress'
12
13
  #=> 'S&F'
13
-
14
+
14
15
  ## save an instance variable the short way
15
16
  w = Worker.new
16
17
  w.size = :california_king
17
18
  w.size
18
19
  #=> :california_king
19
-
20
+
20
21
  ## new instances don't cross streams
21
22
  w = Worker.new
22
23
  w.size
23
24
  #=> nil
24
-
25
+
25
26
  ## instance variables are hidden
26
27
  w = Worker.new
27
- w.metametaclass.instance_variable_set '@mattress', 'S&F'
28
+ w.attic.instance_variable_set '@mattress', 'S&F'
28
29
  w.instance_variables
29
30
  ## []
@@ -4,25 +4,25 @@ require 'attic'
4
4
  String.extend Attic
5
5
  String.respond_to? :attic
6
6
  #=> true
7
-
7
+
8
8
  ## save an instance variable the long way
9
9
  s = ""
10
- s.metametaclass.instance_variable_set '@mattress', 'S&F'
11
- s.metametaclass.instance_variable_get '@mattress'
10
+ s.attic.instance_variable_set '@mattress', 'S&F'
11
+ s.attic.instance_variable_get '@mattress'
12
12
  #=> 'S&F'
13
13
 
14
14
  ## can create attributes
15
15
  String.attic :goodies
16
16
  #=> [:goodies]
17
-
17
+
18
18
  ## save an instance variable the short way
19
19
  s = ""
20
20
  s.goodies = :california_king
21
21
  p s.instance_variables
22
- p s.attic_vars
22
+ p s.attic_variables
23
23
  s.goodies
24
24
  #=> :california_king
25
-
25
+
26
26
  ## String instances don't cross streams
27
27
  String.extend Attic
28
28
  String.attic :name
@@ -39,6 +39,14 @@ Symbol.extend Attic
39
39
  :any.attic?
40
40
  #=> false
41
41
 
42
+ ## A String's attic vars appear in `all_instance_variables` do
43
+ String.extend Attic
44
+ String.attic :_name
45
+ a, b = 'String1', 'String2'
46
+ a._name = :roger
47
+ a.all_instance_variables
48
+ #=> [:@___attic_name]
49
+
42
50
  ## A Symbol's attic vars appear in `all_instance_variables` do
43
51
  Symbol.extend Attic
44
52
  Symbol.attic :_name
metadata CHANGED
@@ -1,27 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attic
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre.RC2
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Delano Mandelbaum
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-04-04 00:00:00.000000000 Z
11
+ date: 2024-04-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: 13.0.6
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 13.0.6
27
27
  - !ruby/object:Gem::Dependency
@@ -69,11 +69,11 @@ files:
69
69
  - attic.gemspec
70
70
  - lib/attic.rb
71
71
  - lib/attic/class_methods.rb
72
- - lib/attic/core_ext.rb
73
72
  - lib/attic/errors.rb
74
73
  - lib/attic/instance_methods.rb
75
74
  - try/01_mixins_tryouts.rb
76
75
  - try/10_attic_tryouts.rb
76
+ - try/15_frozen_attic_tryouts.rb
77
77
  - try/20_accessing_tryouts.rb
78
78
  - try/25_string_tryouts.rb
79
79
  - try/30_nometaclass_tryouts.rb
@@ -93,9 +93,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
93
93
  version: 2.6.0
94
94
  required_rubygems_version: !ruby/object:Gem::Requirement
95
95
  requirements:
96
- - - ">"
96
+ - - ">="
97
97
  - !ruby/object:Gem::Version
98
- version: 1.3.1
98
+ version: '0'
99
99
  requirements: []
100
100
  rubygems_version: 3.4.12
101
101
  signing_key:
@@ -1,70 +0,0 @@
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, Integer].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