Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve naming and testing #6

Open
wants to merge 2 commits into
base: modernize
Choose a base branch
from
Open
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
23 changes: 23 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,29 @@
name: CI
on: [push, pull_request]
jobs:
test:
strategy:
matrix:
ruby-version:
- "2.4"
- "2.7"
- "3.0"
- "3.1"
name: ${{ format('Tests (Ruby {0})', matrix.ruby-version) }}
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v1

- name: Install Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
bundler-cache: true

- name: Run Unit Tests
run: |
bundle exec rake spec:unit
lint:
strategy:
matrix:
Expand Down
6 changes: 6 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ begin
require 'rspec/core/rake_task'

RSpec::Core::RakeTask.new(:spec)
RSpec::Core::RakeTask.new('spec:unit') do |t|
t.pattern = 'spec/unit/*_spec.rb'
end
RSpec::Core::RakeTask.new('spec:integration') do |t|
t.pattern = 'spec/integration/*_spec.rb'
end

task default: :spec
rescue LoadError
Expand Down
3 changes: 1 addition & 2 deletions lib/midi-eye.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@
require 'forwardable'
require 'midi-message'
require 'nibbler'
require 'unimidi'

# classes
require 'midi-eye/event'
require 'midi-eye/event_handlers'
require 'midi-eye/listener'
require 'midi-eye/source'

Expand Down
74 changes: 35 additions & 39 deletions lib/midi-eye/event.rb → lib/midi-eye/event_handlers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,30 @@

module MIDIEye
# User defined callbacks for input events
class Event
class EventHandlers
extend Forwardable

def_delegators :@event, :count
EventHandler = Struct.new(:conditions, :proc, :name)
EnqueuedEvent = Struct.new(:handler, :event)

def_delegators :@handlers, :count

def initialize
@event = []
@queue = Queue.new
@handlers = []
@event_queue = Queue.new
end

# Delete an event by name
# @param [String, Symbol] name
def delete(name)
@event.delete_if { |event| event[:listener_name].to_s == name.to_s }
@handlers.delete_if { |handler| handler.name.to_s == name.to_s }
end

# Clear the collection of events
# Clear the event handlers and their events
# @return [Boolean]
def clear
@event.clear
@queue.clear
@handlers.clear
@event_queue.clear
true
end

Expand All @@ -31,60 +34,53 @@ def clear
# @param [Proc] callback
# @return [Hash]
def add(options = {}, &callback)
name = options[:listener_name]
options.delete(:listener_name)
event = {
conditions: options,
proc: callback,
listener_name: name
}
@event << event
event
name = options[:name]
options.delete(:name)
handler = EventHandler.new(options, callback, name)
@handlers << handler
handler
end

# Trigger all enqueued events
# @return [Fixnum] The number of triggered events
def trigger_enqueued
# @return [Integer] The number of triggered events
def handle_enqueued
counter = 0
until @queue.empty?
until @event_queue.empty?
counter += 1
trigger_event(@queue.shift)
handle_event(@event_queue.shift)
end
counter
end

# Enqueue all events with the given message
# Enqueue the given event for all handlers
# @return [Array<Hash>]
def enqueue_all(message)
@event.map { |action| enqueue(action, message) }
def enqueue(event)
@handlers.map { |handler| enqueue_event_for_handler(handler, event) }
end

# Add an event to the trigger queue
private

# For the given handler, add an event to the queue
# @return [Hash]
def enqueue(action, message)
event = {
action: action,
message: message
}
@queue << event
event
def enqueue_event_for_handler(handler, event)
enqueued_event = EnqueuedEvent.new(handler, event)
@event_queue << enqueued_event
enqueued_event
end

private

# Does the given message meet the given conditions?
def meets_conditions?(conditions, message)
conditions.map { |key, value| condition_met?(message, key, value) }.all?
end

# Trigger an event
def trigger_event(event)
action = event[:action]
conditions = action[:conditions]
return unless conditions.nil? || meets_conditions?(conditions, event[:message][:message])
def handle_event(shifted_event)
handler = shifted_event.handler
conditions = handler.conditions
return unless conditions.nil? || meets_conditions?(conditions, shifted_event.event[:message])

begin
action[:proc].call(event[:message])
handler.proc.call(shifted_event.event)
rescue StandardError => e
Thread.main.raise(e)
end
Expand Down
32 changes: 14 additions & 18 deletions lib/midi-eye/listener.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ module MIDIEye
class Listener
LISTEN_INTERVAL = 1.0 / 10_000

attr_reader :event
attr_reader :event_handlers
attr_accessor :sources

# @param [Array<UniMIDI::Input>, UniMIDI::Input] inputs Input(s) to add to the list of sources for this listener
def initialize(inputs)
@sources = []
@event = Event.new
@event_handlers = EventHandlers.new

add_input(inputs)
end
Expand Down Expand Up @@ -48,12 +48,11 @@ def remove_input(inputs)
alias remove_inputs remove_input

# Start listening for MIDI messages
# @params [Hash] options
# @option options [Boolean] :background Run in a background thread
# @params [Boolean] background Run in a background thread (default: true)
# @return [MIDIEye::Listener] self
def run(options = {})
def run(background: true)
listen
join if options[:background].nil?
join unless background
self
end
alias start run
Expand All @@ -62,7 +61,7 @@ def run(options = {})
# @return [MIDIEye::Listener] self
def close
@listener.kill if running?
@event.clear
@event_handlers.clear
@sources.clear
self
end
Expand All @@ -86,20 +85,13 @@ def join
self
end

# Deletes the event with the given name (for backwards compat)
# @param [String, Symbol] event_name
# @return [Boolean]
def delete_event(event_name)
!@event.delete(event_name).nil?
end

# Add an event to listen for
# @param [Hash] options
# @return [MIDIEye::Listener] self
def listen_for(options = {}, &callback)
raise 'Listener must have a block' if callback.nil?

@event.add(options, &callback)
@event_handlers.add(options, &callback)
self
end
alias on_message listen_for
Expand All @@ -108,29 +100,33 @@ def listen_for(options = {}, &callback)
def poll
@sources.each do |input|
input.poll do |objs|
objs.each { |batch| input_to_messages(batch) }
handle_new_input(objs)
end
end
end

private

def handle_new_input(objs)
objs.each { |batch| input_to_messages(batch) }
end

def input_to_messages(batch)
messages = [batch[:messages]].flatten.compact
messages.each do |message|
data = {
message: message,
timestamp: batch[:timestamp]
}
@event.enqueue_all(data)
@event_handlers.enqueue(data)
end
end

# A loop that runs while the listener is active
def listen_loop
loop do
poll
@event.trigger_enqueued
@event_handlers.handle_enqueued
sleep(LISTEN_INTERVAL)
end
end
Expand Down
5 changes: 3 additions & 2 deletions lib/midi-eye/source.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def initialize(input)
def poll(&block)
messages = @device.buffer.slice(@pointer, @device.buffer.length - @pointer)
@pointer = @device.buffer.length
messages.compact.each { |raw_message| handle_message(raw_message, &block) }
messages.compact.map { |raw_message| handle_message(raw_message, &block) }
end

# If this source was created from the given input
Expand All @@ -42,7 +42,8 @@ def handle_message(raw_message)
nil
end
objects = [parsed_messages].flatten.compact
yield(objects)
yield(objects) if block_given?
objects
end
end
end
14 changes: 0 additions & 14 deletions spec/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,3 @@

require 'rspec'
require 'midi-eye'

module SpecHelper
module_function

def devices
if @devices.nil?
@devices = {}
{ input: UniMIDI::Input, output: UniMIDI::Output }.each do |type, klass|
@devices[type] = klass.gets
end
end
@devices
end
end
17 changes: 17 additions & 0 deletions spec/integration/helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true

require 'unimidi'

module IntegrationSpecHelper
module_function

def devices
if @devices.nil?
@devices = {}
{ input: UniMIDI::Input, output: UniMIDI::Output }.each do |type, klass|
@devices[type] = klass.gets
end
end
@devices
end
end
Loading