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
4 changes: 1 addition & 3 deletions .github/workflows/ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@ jobs:
test:
strategy:
matrix:
ruby-version: ['2.6', '2.7', '3.0', '3.1', '3.2', '3.3', 'jruby', 'truffleruby']
ruby-version: ['2.7', '3.0', '3.1', '3.2', '3.3', '3.4', 'jruby']
platform: [ubuntu-latest, macos-latest, windows-latest]
exclude:
- ruby-version: truffleruby
platform: windows-latest
- ruby-version: jruby
platform: windows-latest
runs-on: ${{ matrix.platform }}
Expand Down
5 changes: 5 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 1.2.0 - 8-Jul-2025
* Updated to use deferred method declaration validation. This means you
no longer have to define methods before including the interface.
* Additional specs and example added.

## 1.1.0 - 4-May-2022
* Switch from test-unit to rspec.

Expand Down
22 changes: 15 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
[![Ruby](https://github.com/djberg96/interface/actions/workflows/ruby.yml/badge.svg)](https://github.com/djberg96/interface/actions/workflows/ruby.yml)

## Description
This module provides Java style interfaces for Ruby, including a somewhat
similar syntax. This is largely a proof of concept library.
This library provides Java style interfaces for Ruby.

## Installation
`gem install interface`
Expand All @@ -14,12 +13,14 @@ similar syntax. This is largely a proof of concept library.
```ruby
require 'interface'

MyInterface = interface{
MyInterface = interface do
required_methods :foo, :bar, :baz
}
end

# Raises an error until 'baz' is defined
class MyClass
implements MyInterface

def foo
puts "foo"
end
Expand All @@ -28,7 +29,8 @@ class MyClass
puts "bar"
end

implements MyInterface
# Uncomment to work
# def baz; end
end
```

Expand Down Expand Up @@ -58,17 +60,23 @@ I should note that, although I am listed as the author, this was mostly the
combined work of David Naseby and Mauricio Fernandez. I just happened to be
the guy that put it all together.

In more recent versions this code was enhanced with the help of AI.
Specifically, it was updated to use the TracePoint interface so that an
interface could be declared at the top of the class, with the method
validations deferred.

## Acknowledgements
This module was largely inspired and somewhat copied from a post by
David Naseby (see URL above). It was subsequently modified almost entirely
by Mauricio Fernandez through a series of discussions on IRC.
by Mauricio Fernandez through a series of discussions on IRC, and later
by evil AI bots that will eventually kill us all.

## See Also
The Crystal programming language, which has syntax very similar to Ruby's,
effectively implements interfaces via the `abstract` keyword.

## Copyright
(C) 2004-2023 Daniel J. Berger
(C) 2004-2025 Daniel J. Berger
All rights reserved.

## Warranty
Expand Down
110 changes: 110 additions & 0 deletions examples/enhanced_features_demo.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# frozen_string_literal: true

require_relative '../lib/interface'

puts "=== Interface Library Improvements Demo ==="
puts

# 1. Enhanced Error Messages
puts "1. Enhanced Error Messages:"
puts "-" * 40

PaymentInterface = interface do
required_methods :process_payment, :validate_payment, :send_receipt
end

class IncompletePaymentProcessor
def process_payment
puts "Processing payment..."
end
# Missing validate_payment and send_receipt methods
end

begin
IncompletePaymentProcessor.include PaymentInterface
rescue Interface::MethodMissing => e
puts "Error caught with enhanced details:"
puts " Missing methods: #{e.missing_methods.join(', ')}"
puts " Target class: #{e.target_name}"
puts " Interface: #{e.interface_name}"
puts " Full message: #{e.message}"
end

puts

# 2. Interface Introspection
puts "2. Interface Introspection:"
puts "-" * 40

StorageInterface = interface do
required_methods :save, :load, :delete
end

puts "Required methods: #{StorageInterface.get_required_methods}"

# 3. Sub-interface with unrequired methods
puts "3. Sub-interface with selective requirements:"
puts "-" * 40

ReadOnlyStorageInterface = interface do
extend StorageInterface
unrequired_methods :save, :delete
end

puts "Original interface requires: #{StorageInterface.get_required_methods}"
puts "Read-only interface requires: #{ReadOnlyStorageInterface.get_required_methods}"
puts "Read-only interface unrequires: #{ReadOnlyStorageInterface.get_unrequired_methods}"

# 4. Interface Satisfaction Checking
puts "4. Interface Satisfaction Checking:"
puts "-" * 40

class FileStorage
def save; puts "Saving to file"; end
def load; puts "Loading from file"; end
def delete; puts "Deleting file"; end
end

class ReadOnlyFileStorage
def load; puts "Loading from file"; end
end

puts "FileStorage satisfies StorageInterface: #{StorageInterface.satisfied_by?(FileStorage)}"
puts "ReadOnlyFileStorage satisfies StorageInterface: #{StorageInterface.satisfied_by?(ReadOnlyFileStorage)}"
puts "ReadOnlyFileStorage satisfies ReadOnlyStorageInterface: #{ReadOnlyStorageInterface.satisfied_by?(ReadOnlyFileStorage)}"

# 5. Complex inheritance hierarchy
puts "5. Complex Interface Inheritance:"
puts "-" * 40

BaseInterface = interface do
required_methods :initialize_connection, :close_connection
end

AuthenticatedInterface = interface do
extend BaseInterface
required_methods :authenticate, :authorize
end

DatabaseInterface = interface do
extend AuthenticatedInterface
required_methods :query, :transaction
unrequired_methods :authorize # Don't require authorization for this specific interface
end

puts "BaseInterface requires: #{BaseInterface.get_required_methods}"
puts "AuthenticatedInterface requires: #{AuthenticatedInterface.get_required_methods}"
puts "DatabaseInterface requires: #{DatabaseInterface.get_required_methods}"

# 6. String to symbol conversion
puts "6. Automatic String to Symbol Conversion:"
puts "-" * 40

StringInterface = interface do
required_methods 'string_method', 'another_string_method'
end

puts "Methods defined with strings: #{StringInterface.get_required_methods}"

puts
puts "=== Demo Complete ==="
4 changes: 2 additions & 2 deletions examples/example_interface.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@
# run this example via the 'rake example:interface' task. Modify this
# code as you see fit.
#######################################################################
require 'interface'
require_relative '../lib/interface'

MyInterface = interface{
required_methods :foo, :bar
}

class MyClass
include MyInterface
def foo; end
def bar; end
include MyInterface
end

=begin
Expand Down
4 changes: 2 additions & 2 deletions interface.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ require 'rubygems'

Gem::Specification.new do |spec|
spec.name = 'interface'
spec.version = '1.1.0'
spec.version = '1.2.0'
spec.author = 'Daniel J. Berger'
spec.license = 'Artistic-2.0'
spec.email = 'djberg96@gmail.com'
spec.homepage = 'http://github.com/djberg96/interface'
spec.summary = 'Java style interfaces for Ruby'
spec.test_file = 'spec/interface_spec.rb'
spec.files = Dir['**/*'].reject{ |f| f.include?('git') }
spec.files = Dir['**/*'].reject{ |f| f.include?('git') || f.include?('misc') }
spec.cert_chain = Dir['certs/*']

spec.add_development_dependency('rake')
Expand Down
Loading