rubyexts 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/CHANGELOG +2 -0
  2. data/LICENSE +20 -0
  3. data/README.textile +0 -0
  4. data/Rakefile +79 -0
  5. data/TODO.txt +6 -0
  6. data/lib/rubyexts.rb +102 -0
  7. data/lib/rubyexts/array.rb +20 -0
  8. data/lib/rubyexts/attribute.rb +151 -0
  9. data/lib/rubyexts/capture_io.rb +32 -0
  10. data/lib/rubyexts/class.rb +177 -0
  11. data/lib/rubyexts/colored_string.rb +103 -0
  12. data/lib/rubyexts/enumerable.rb +30 -0
  13. data/lib/rubyexts/file.rb +84 -0
  14. data/lib/rubyexts/hash.rb +180 -0
  15. data/lib/rubyexts/kernel.rb +91 -0
  16. data/lib/rubyexts/module.rb +53 -0
  17. data/lib/rubyexts/object.rb +56 -0
  18. data/lib/rubyexts/object_space.rb +16 -0
  19. data/lib/rubyexts/os.rb +89 -0
  20. data/lib/rubyexts/platform.rb +27 -0
  21. data/lib/rubyexts/random.rb +25 -0
  22. data/lib/rubyexts/string.rb +92 -0
  23. data/lib/rubyexts/time.rb +15 -0
  24. data/lib/rubyexts/time_dsl.rb +62 -0
  25. data/lib/rubyexts/try_dup.rb +43 -0
  26. data/lib/rubyexts/unique_array.rb +16 -0
  27. data/rubyexts.gemspec +36 -0
  28. data/script/spec +12 -0
  29. data/spec/rubyexts/array_spec.rb +19 -0
  30. data/spec/rubyexts/attribute_spec.rb +37 -0
  31. data/spec/rubyexts/class_spec.rb +1 -0
  32. data/spec/rubyexts/cli_spec.rb +0 -0
  33. data/spec/rubyexts/colored_string_spec.rb +9 -0
  34. data/spec/rubyexts/enumerable_spec.rb +52 -0
  35. data/spec/rubyexts/file_spec.rb +78 -0
  36. data/spec/rubyexts/hash_spec.rb +91 -0
  37. data/spec/rubyexts/kernel_spec.rb +9 -0
  38. data/spec/rubyexts/module_spec.rb +0 -0
  39. data/spec/rubyexts/object_space_spec.rb +17 -0
  40. data/spec/rubyexts/object_spec.rb +57 -0
  41. data/spec/rubyexts/os_spec.rb +121 -0
  42. data/spec/rubyexts/platform_spec.rb +0 -0
  43. data/spec/rubyexts/random_spec.rb +31 -0
  44. data/spec/rubyexts/string_spec.rb +23 -0
  45. data/spec/rubyexts/time_dsl_spec.rb +88 -0
  46. data/spec/rubyexts/time_spec.rb +11 -0
  47. data/spec/rubyexts/unique_array_spec.rb +0 -0
  48. data/spec/rubyexts_spec.rb +182 -0
  49. data/spec/spec.opts +5 -0
  50. data/spec/spec_helper.rb +16 -0
  51. metadata +104 -0
data/CHANGELOG ADDED
@@ -0,0 +1,2 @@
1
+ = Version 0.0.1
2
+ * Imported from Rango
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Jakub Šťastný aka Botanicus
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env rake1.9
2
+ # encoding: utf-8
3
+
4
+ # http://support.runcoderun.com/faqs/builds/how-do-i-run-rake-with-trace-enabled
5
+ Rake.application.options.trace = true
6
+
7
+ task :setup => ["submodules:init"]
8
+
9
+ namespace :submodules do
10
+ desc "Init submodules"
11
+ task :init do
12
+ sh "git submodule init"
13
+ end
14
+
15
+ desc "Update submodules"
16
+ task :update do
17
+ Dir["vendor/*"].each do |path|
18
+ if File.directory?(path) && File.directory?(File.join(path, ".git"))
19
+ Dir.chdir(path) do
20
+ puts "=> #{path}"
21
+ puts %x[git reset --hard]
22
+ puts %x[git fetch]
23
+ puts %x[git reset origin/master --hard]
24
+ puts
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ task :gem do
32
+ sh "gem build rubyexts.gemspec"
33
+ end
34
+
35
+ namespace :gem do
36
+ task :prerelease do
37
+ require_relative "lib/rubyexts"
38
+ gemspec = Dir["*.gemspec"].first
39
+ content = File.read(gemspec)
40
+ prename = "#{gemspec.split(".").first}.pre.gemspec"
41
+ version = RubyExts::VERSION.sub(/^(\d+)\.(\d+)\.\d+$/) { "#$1.#{$1.to_i + 1}" }
42
+ File.open(prename, "w") do |file|
43
+ file.puts(content.gsub(/(\w+::VERSION)/, "'#{version}.pre'"))
44
+ end
45
+ sh "gem build #{prename}"
46
+ rm prename
47
+ end
48
+ end
49
+
50
+ desc "Release new version of rubyexts"
51
+ task release: ["release:tag", "release:gemcutter"]
52
+
53
+ namespace :release do
54
+ desc "Create Git tag"
55
+ task :tag do
56
+ require_relative "lib/rubyexts"
57
+ puts "Creating new git tag #{RubyExts::VERSION} and pushing it online ..."
58
+ sh "git tag -a -m 'Version #{RubyExts::VERSION}' #{RubyExts::VERSION}"
59
+ sh "git push --tags"
60
+ puts "Tag #{RubyExts::VERSION} was created and pushed to GitHub."
61
+ end
62
+
63
+ desc "Push gem to Gemcutter"
64
+ task :gemcutter do
65
+ puts "Pushing to Gemcutter ..."
66
+ sh "gem push #{Dir["*.gem"].last}"
67
+ end
68
+
69
+ desc "Create and push prerelease gem"
70
+ task :pre => ["gem:prerelease", :gemcutter]
71
+ end
72
+
73
+ desc "Run specs"
74
+ task :default => :setup do
75
+ rubylib = (ENV["RUBYLIB"] || String.new).split(":")
76
+ libdirs = Dir["vendor/*/lib"]
77
+ ENV["RUBYLIB"] = (libdirs + rubylib).join(":")
78
+ exec "./script/spec --options spec/spec.opts spec"
79
+ end
data/TODO.txt ADDED
@@ -0,0 +1,6 @@
1
+ missing specs:
2
+ - attribute
3
+ - class (copy from extlib)
4
+
5
+ rewrite/refactor:
6
+ - colored string
data/lib/rubyexts.rb ADDED
@@ -0,0 +1,102 @@
1
+ # encoding: utf-8
2
+
3
+ module RubyExts
4
+ VERSION ||= "0.0.1"
5
+ end
6
+
7
+ module Kernel
8
+ # Require all files matching given glob relative to $:
9
+ #
10
+ # @author Botanicus
11
+ # @since 0.0.3
12
+ # @param [String] library Glob of files to require
13
+ # @param [Hash] params Optional parameters.
14
+ # @option params [String, Array<String>] :exclude File or list of files or globs relative to base directory
15
+ # @raise [LoadError] If base directory doesn't exist
16
+ # @raise [ArgumentError] If first argument isn't a glob
17
+ # @return [Array<String>] List of successfully loaded files
18
+ # @example
19
+ # acquire "lib/*"
20
+ # acquire "lib/**/*", exclude: "**/*_spec.rb"
21
+ # acquire "lib/**/*", exclude: ["**/*_spec.rb", "lib/init.rb"]
22
+ def acquire(glob, params = Hash.new)
23
+ base, glob = get_base_and_glob(glob)
24
+ $:.compact.find do |path|
25
+ fullpath = File.expand_path(File.join(path, base))
26
+ if File.directory?(fullpath)
27
+ return __acquire__(fullpath, glob, params.merge(soft: true))
28
+ end
29
+ end
30
+ raise LoadError, "Directory #{base} doesn't exist in $:"
31
+ end
32
+
33
+ def acquire!(glob, params = Hash.new)
34
+ self.acquire(glob, params.merge(soft: false))
35
+ end
36
+
37
+ # Require all files matching given glob relative to current file
38
+ #
39
+ # @author Botanicus
40
+ # @since 0.0.3
41
+ # @param [String] library Glob of files to require
42
+ # @param [Hash] params Optional parameters.
43
+ # @option params [String, Array<String>] :exclude File or list of files or globs relative to base directory
44
+ # @raise [LoadError] If base directory doesn't exist
45
+ # @raise [ArgumentError] If first argument isn't a glob
46
+ # @return [Array<String>] List of successfully loaded files
47
+ # @example
48
+ # acquire "lib/*"
49
+ # acquire "lib/**/*", exclude: "**/*_spec.rb"
50
+ # acquire "lib/**/*", exclude: ["**/*_spec.rb", "lib/init.rb"]
51
+ def acquire_relative(glob, params = Hash.new)
52
+ base, glob = get_base_and_glob(glob)
53
+ path = File.dirname(caller[0].split(":").first)
54
+ full = File.expand_path(File.join(path, base))
55
+ raise LoadError, "Directory #{base} doesn't exist in #{path}" unless File.directory?(full)
56
+ return __acquire__(full, glob, params.merge(soft: true))
57
+ end
58
+
59
+ def acquire_relative!(glob, params = Hash.new)
60
+ self.acquire_relative(glob, params.merge(soft: false))
61
+ end
62
+
63
+ def load_relative(file)
64
+ path = File.dirname(caller[0].split(":").first)
65
+ load File.expand_path(File.join(path, file))
66
+ end
67
+
68
+ private
69
+ def __acquire__(path, glob, params)
70
+ glob.replace("#{glob}.rb") if glob.eql?("*") || glob.end_with?("/*")
71
+ files = Dir[File.join(path, glob)]
72
+ excludes = [params[:exclude]].flatten.compact
73
+ excludes.map! do |glob|
74
+ fullpath = File.join(path, glob)
75
+ File.file?(fullpath) ? fullpath : Dir[fullpath]
76
+ end
77
+ excludes = excludes.flatten.compact
78
+ files.select do |path|
79
+ if File.file?(path) && !excludes.include?(path)
80
+ soft_not_loaded = params[:soft] && ! $LOADED_FEATURES.include?(path)
81
+ load(path) if soft_not_loaded || ! params[:soft]
82
+ $LOADED_FEATURES.push(path) if soft_not_loaded
83
+ end
84
+ end
85
+ end
86
+
87
+ def get_base_and_glob(glob)
88
+ base, glob = glob.match(/^([^*]+)(.*)$/)[1..2]
89
+ raise ArgumentError, "You have to provide glob" if glob.empty?
90
+ return [base, glob]
91
+ end
92
+ end
93
+
94
+ acquire_relative "rubyexts/*"
95
+
96
+ # run console if executed directly
97
+ if $0 == __FILE__
98
+ require "irb"
99
+ require "irb/completion"
100
+ include RubyExts
101
+ IRB.start(__FILE__)
102
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+
3
+ class Array
4
+ # Returns the _only_ element in the array or raise
5
+ # +IndexError+ if array hasn't exactly one element.
6
+ #
7
+ # @author Botanicus
8
+ # @from Extensions
9
+ # @since 0.0.3
10
+ # @raise [IndexError] If array hasn't exactly one element
11
+ # @return [Object] First (and only) item of the array
12
+ # @example
13
+ # [5].only # => 5
14
+ # [1, 2, 3].only # => IndexError
15
+ # [].only # => IndexError
16
+ def only
17
+ raise IndexError, "Array#only called on non-single-element array" unless self.size == 1
18
+ self.first
19
+ end
20
+ end
@@ -0,0 +1,151 @@
1
+ # encoding: utf-8
2
+
3
+ # TODO: refactor similar as attr_accessor_with_default from active support
4
+ # see http://noobkit.com/show/ruby/rails/rails-stable/activesupport/module/attr_accessor_with_default.html
5
+ require "rubyexts/try_dup"
6
+
7
+ # default value for attr
8
+ module AttributeMixin
9
+ # class Array
10
+ # private_alias :join
11
+ # def join(char)
12
+ # puts "New join!"
13
+ # __join__(char)
14
+ # end
15
+ # end
16
+ def private_alias(method)
17
+ alias_method "__#{method}__", method
18
+ private "__#{method}__"
19
+ end
20
+
21
+ # @since 0.0.1
22
+ # @example
23
+ # class Post
24
+ # attribute :title, "Rango rulez!"
25
+ # end
26
+ # Post.new.title
27
+ # # => "Rango rulez!"
28
+ # @param [Symbol] name Name of object variable which will be set. If you have <tt>attribute :title</tt>, then the +@title+ variable will be defined. It also create +#title+ and +#title=+ accessors.
29
+ # @param [Object, @optional] default_value Default value of the variable.
30
+ # @return [name] Returns given default value or if default value.
31
+ # @see #hattribute
32
+ def attribute(name, default_value = nil)
33
+ # define reader method
34
+ define_method(name) do
35
+ if instance_variable_get("@#{name}").nil?
36
+ # lazy loading
37
+ # TODO: why is it lazy loaded?
38
+ default_value = default_value.call if default_value.is_a?(Proc)
39
+ instance_variable_set("@#{name}", default_value.try_dup) # dup is terribly important, otherwise all the objects will points to one object. If it is for example array, all of the objects will push into one array.
40
+ end
41
+ instance_variable_get("@#{name}")
42
+ end
43
+
44
+ # define writer method
45
+ define_method("#{name}=") do |value|
46
+ instance_variable_set("@#{name}", value)
47
+ # TODO: here should be rewritten the reader for cases when user want to do foo.bar = nil because now it will still returns the default value
48
+ end
49
+
50
+ return default_value
51
+ end
52
+
53
+ # This will also define title and title= methods, but it doesn't define @title variable,
54
+ # but @__hattributes__ hash with all the attributes
55
+
56
+ # @since 0.0.1
57
+ # @example
58
+ # class Post
59
+ # hattribute :title, "Rango rulez!"
60
+ # end
61
+ # Post.new.title
62
+ # # => "Rango rulez!"
63
+ # @param [Symbol] name Name of attribute his accessor methods will be defined. It's similar as +#attribute+, but it doesn't define +@name+ variable, but it will be key in +@__hattributes__+ hash. It's useful when you don't like to mess the object namespace with many variables or if you like to separate the attributes from the instance variables.
64
+ # @param [Object, @optional] default_value Default value of the variable.
65
+ # @return [name] Returns given default value or if default value.
66
+ # @see #attribute
67
+ def default_hattribute_names
68
+ # this can't be hash because in case the value will be lambda, we can't call it,
69
+ # because lambda is evaluated in scope of instance
70
+ @default_hattribute_names ||= Array.new
71
+ end
72
+
73
+ def hattribute(name, default_value = nil)
74
+ self.default_hattribute_names.push(name)
75
+
76
+ define_method(:hattributes) do
77
+ @__hattributes__ ||= Hash.new
78
+ # this is importat for initialization @__hattributes__
79
+ self.class.default_hattribute_names.each do |hattribute|
80
+ self.send(hattribute)
81
+ end
82
+ return @__hattributes__
83
+ end
84
+
85
+ # define reader method
86
+ if default_value.is_a?(Proc)
87
+ define_method(name) do
88
+ properties = instance_variable_get("@__hattributes__") || Hash.new
89
+ if properties[name].nil?
90
+ # instance_variable_set("@#{name}", default_value)
91
+ # lazy loading
92
+ properties[name] = self.instance_eval(&default_value)
93
+ end
94
+ # instance_variable_get("@#{name}")
95
+ properties[name]
96
+ end
97
+ else
98
+ define_method(name) do
99
+ properties = instance_variable_get("@__hattributes__") || Hash.new
100
+ # properties = @__hattributes__
101
+ if properties[name].nil?
102
+ # instance_variable_set("@#{name}", default_value)
103
+ # lazy loading
104
+ properties[name] = default_value.try_dup
105
+ end
106
+ # instance_variable_get("@#{name}")
107
+ properties[name]
108
+ end
109
+ end
110
+
111
+ # define writer method
112
+ define_method("#{name}=") do |value|
113
+ instance_variable_set("@__hattributes__", Hash.new) unless instance_variable_get("@__hattributes__")
114
+ instance_variable_get("@__hattributes__")[name] = value
115
+ end
116
+
117
+ return default_value
118
+ end
119
+
120
+ # class << self.class
121
+ # def hattributes
122
+ # raise "Y"
123
+ # end
124
+ # end
125
+
126
+ # class Post
127
+ # questionable :updated, true
128
+ # end
129
+ # Post.new.updated?
130
+ # # => true
131
+ # @since 0.0.2
132
+ def questionable(name, default_value)
133
+ define_method("#{name}?") do
134
+ unless self.instance_variables.include?(name.to_sym)
135
+ self.instance_variable_set("@#{name}", default_value)
136
+ end
137
+ self.instance_variable_get("@#{name}")
138
+ end
139
+ end
140
+ end
141
+
142
+ Class.send(:include, AttributeMixin)
143
+ Module.send(:include, AttributeMixin)
144
+
145
+ # class Test
146
+ # hattribute :bar, -> { "value" }
147
+ # end
148
+ #
149
+ # t = Test.new
150
+ # p t.bar
151
+ # p t.hattributes
@@ -0,0 +1,32 @@
1
+ require "stringio"
2
+
3
+ def STDOUT.capture(&block)
4
+ before = self
5
+ $stdout = StringIO.new
6
+ block.call
7
+ $stdout.rewind
8
+ output = $stdout.read
9
+ $stdout = before
10
+ output
11
+ end
12
+
13
+ # @example STDOUT.capture { puts "hi" }
14
+ # # => "hi"
15
+ def STDERR.capture(&block)
16
+ before = self
17
+ $stderr = StringIO.new
18
+ block.call
19
+ $stderr.rewind
20
+ output = $stderr.read
21
+ $stderr = before
22
+ output
23
+ end
24
+
25
+ # @example STDIN.capture("yes") { ask("Do you hear me?") }
26
+ # # => "yes"
27
+ def STDIN.capture(default, &block)
28
+ STDIN.reopen "/dev/null" # so we don't get the fucking prompt
29
+ STDIN.ungetbyte(default) # so the default value can be get by STDIN.getc & similar methods
30
+ block.call
31
+ # TODO: how I can get back the original STDIN?
32
+ end
@@ -0,0 +1,177 @@
1
+ # Copyright (c) 2004-2008 David Heinemeier Hansson
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ # Allows attributes to be shared within an inheritance hierarchy, but where
23
+ # each descendant gets a copy of their parents' attributes, instead of just a
24
+ # pointer to the same. This means that the child can add elements to, for
25
+ # example, an array without those additions being shared with either their
26
+ # parent, siblings, or children, which is unlike the regular class-level
27
+ # attributes that are shared across the entire hierarchy.
28
+ class Class
29
+ # Defines class-level and instance-level attribute reader.
30
+ #
31
+ # @param *syms<Array> Array of attributes to define reader for.
32
+ # @return <Array[#to_s]> List of attributes that were made into cattr_readers
33
+ #
34
+ # @api public
35
+ #
36
+ # @todo Is this inconsistent in that it does not allow you to prevent
37
+ # an instance_reader via :instance_reader => false
38
+ def cattr_reader(*syms)
39
+ syms.flatten.each do |sym|
40
+ next if sym.is_a?(Hash)
41
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
42
+ unless defined? @@#{sym}
43
+ @@#{sym} = nil
44
+ end
45
+
46
+ def self.#{sym}
47
+ @@#{sym}
48
+ end
49
+
50
+ def #{sym}
51
+ @@#{sym}
52
+ end
53
+ RUBY
54
+ end
55
+ end
56
+
57
+ # Defines class-level (and optionally instance-level) attribute writer.
58
+ #
59
+ # @param <Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to define writer for.
60
+ # @option syms :instance_writer<Boolean> if true, instance-level attribute writer is defined.
61
+ # @return <Array[#to_s]> List of attributes that were made into cattr_writers
62
+ #
63
+ # @api public
64
+ def cattr_writer(*syms)
65
+ options = syms.last.is_a?(Hash) ? syms.pop : {}
66
+ syms.flatten.each do |sym|
67
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
68
+ unless defined? @@#{sym}
69
+ @@#{sym} = nil
70
+ end
71
+
72
+ def self.#{sym}=(obj)
73
+ @@#{sym} = obj
74
+ end
75
+ RUBY
76
+
77
+ unless options[:instance_writer] == false
78
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
79
+ def #{sym}=(obj)
80
+ @@#{sym} = obj
81
+ end
82
+ RUBY
83
+ end
84
+ end
85
+ end
86
+
87
+ # Defines class-level (and optionally instance-level) attribute accessor.
88
+ #
89
+ # @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to define accessor for.
90
+ # @option syms :instance_writer<Boolean> if true, instance-level attribute writer is defined.
91
+ # @return <Array[#to_s]> List of attributes that were made into accessors
92
+ #
93
+ # @api public
94
+ def cattr_accessor(*syms)
95
+ cattr_reader(*syms)
96
+ cattr_writer(*syms)
97
+ end
98
+
99
+ # Defines class-level inheritable attribute reader. Attributes are available to subclasses,
100
+ # each subclass has a copy of parent's attribute.
101
+ #
102
+ # @param *syms<Array[#to_s]> Array of attributes to define inheritable reader for.
103
+ # @return <Array[#to_s]> Array of attributes converted into inheritable_readers.
104
+ #
105
+ # @api public
106
+ #
107
+ # @todo Do we want to block instance_reader via :instance_reader => false
108
+ # @todo It would be preferable that we do something with a Hash passed in
109
+ # (error out or do the same as other methods above) instead of silently
110
+ # moving on). In particular, this makes the return value of this function
111
+ # less useful.
112
+ def class_inheritable_reader(*ivars)
113
+ instance_reader = ivars.pop[:reader] if ivars.last.is_a?(Hash)
114
+
115
+ ivars.each do |ivar|
116
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
117
+ def self.#{ivar}
118
+ return @#{ivar} if defined?(@#{ivar})
119
+ return nil if self.object_id == #{self.object_id}
120
+ ivar = superclass.#{ivar}
121
+ return nil if ivar.nil?
122
+ @#{ivar} = ivar.try_dup
123
+ end
124
+ RUBY
125
+
126
+ unless instance_reader == false
127
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
128
+ def #{ivar}
129
+ self.class.#{ivar}
130
+ end
131
+ RUBY
132
+ end
133
+ end
134
+ end
135
+
136
+ # Defines class-level inheritable attribute writer. Attributes are available to subclasses,
137
+ # each subclass has a copy of parent's attribute.
138
+ #
139
+ # @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
140
+ # define inheritable writer for.
141
+ # @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
142
+ # @return <Array[#to_s]> An Array of the attributes that were made into inheritable writers.
143
+ #
144
+ # @api public
145
+ #
146
+ # @todo We need a style for class_eval <<-HEREDOC. I'd like to make it
147
+ # class_eval(<<-RUBY, __FILE__, __LINE__), but we should codify it somewhere.
148
+ def class_inheritable_writer(*ivars)
149
+ instance_writer = ivars.pop[:instance_writer] if ivars.last.is_a?(Hash)
150
+ ivars.each do |ivar|
151
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
152
+ def self.#{ivar}=(obj)
153
+ @#{ivar} = obj
154
+ end
155
+ RUBY
156
+ unless instance_writer == false
157
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
158
+ def #{ivar}=(obj) self.class.#{ivar} = obj end
159
+ RUBY
160
+ end
161
+ end
162
+ end
163
+
164
+ # Defines class-level inheritable attribute accessor. Attributes are available to subclasses,
165
+ # each subclass has a copy of parent's attribute.
166
+ #
167
+ # @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
168
+ # define inheritable accessor for.
169
+ # @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
170
+ # @return <Array[#to_s]> An Array of attributes turned into inheritable accessors.
171
+ #
172
+ # @api public
173
+ def class_inheritable_accessor(*syms)
174
+ class_inheritable_reader(*syms)
175
+ class_inheritable_writer(*syms)
176
+ end
177
+ end