Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Metrics/BlockLength:
Exclude:
- 'spec/**/*_spec.rb'

Metrics/ModuleLength:
Enabled: false

RSpec/SpecFilePathFormat:
Enabled: false

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,8 @@ gem "i18n"
Benchmark scripts are defined in the [`benchmarks`](benchmarks) folder and can be run with Rake:

```console
rake benchmarks:case
rake benchmark:case
rake benchmark:inheritance
```

## Contributing
Expand Down
5 changes: 5 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,9 @@ namespace :benchmark do
task :case do
require_relative 'benchmarks/case'
end

desc 'Run benchmark for enum lookups on classes with inheritance'
task :inheritance do
require_relative 'benchmarks/inheritance'
end
end
53 changes: 53 additions & 0 deletions benchmarks/inheritance.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# frozen_string_literal: true

$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))

require 'benchmark'
require 'ruby-enum'

##
# Base enum, no inheritance.
class Colors
include Ruby::Enum

define :RED, 'red'
define :GREEN, 'green'
end

##
# Subclass, adds its own enum on top of an inherited one (1 level).
class SubColors < Colors
define :BLUE, 'blue'
end

##
# Sub-subclass, adds its own enum on top of 2 inherited levels.
class SubSubColors < SubColors
define :YELLOW, 'yellow'
end

n = 1_000_000

def benchmark(label, iterations, &block)
time = Benchmark.realtime { iterations.times(&block) }
puts "#{label}: #{time.round(4)}"
time
end

puts "Running #{n} iterations of each scenario below\n\n"

puts '--- .value lookups, by depth of inheritance ---'
base_value = benchmark('base class (no inheritance)', n) { Colors.value(:RED) }
sub_value = benchmark('subclass (1 level)', n) { SubColors.value(:RED) }
sub_sub_value = benchmark('sub-subclass (2 levels)', n) { SubSubColors.value(:RED) }

puts "\n--- Other methods on a subclass (1 level) ---"
benchmark('.keys', n) { SubColors.keys }
benchmark('.key?', n) { SubColors.key?(:RED) }
benchmark('.to_h', n) { SubColors.to_h }
count = 0
benchmark('.each', n) { SubColors.each { |_k, _v| count += 1 } }

puts "\n--- Comparison ---"
puts "subclass value is #{(sub_value / base_value).round(2)}x base class value"
puts "sub-subclass value is #{(sub_sub_value / base_value).round(2)}x base class value"
23 changes: 13 additions & 10 deletions lib/ruby-enum/enum.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ def store_new_instance(key, value)
new_instance = new(key, value)
_own_enum_hash[key] = new_instance
_own_enums_by_value[value] = new_instance

# Invalidate memoized, merged hashes since this class' own enums changed.
@_enum_hash = @_enums_by_value = nil
end

def const_missing(key)
Expand Down Expand Up @@ -173,22 +176,22 @@ def _own_enums_by_value
# with keys defined in this class taking precedence over those inherited
# from a superclass.
def _enum_hash
if superclass < Ruby::Enum
superclass.send(:_enum_hash).merge(_own_enum_hash)
else
_own_enum_hash
end
@_enum_hash ||= if superclass < Ruby::Enum
superclass.send(:_enum_hash).merge(_own_enum_hash)
else
_own_enum_hash
end
end

# Returns the enums-by-value hash for this class merged with all of its
# superclasses, with values defined in this class taking precedence over
# those inherited from a superclass.
def _enums_by_value
if superclass < Ruby::Enum
superclass.send(:_enums_by_value).merge(_own_enums_by_value)
else
_own_enums_by_value
end
@_enums_by_value ||= if superclass < Ruby::Enum
superclass.send(:_enums_by_value).merge(_own_enums_by_value)
else
_own_enums_by_value
end
end

def upper?(s)
Expand Down
Loading