Using Ruby coreaudio gem - objective-c

I'm creating an audio project as an academic exercise - I want to use Ruby for a non-rails project. I came across the coreaudio gem here: https://github.com/nagachika/ruby-coreaudio and thought it would be useful in playing back the audio I'm creating. However, there are some basic things I'm just not getting about this gem.
1) Is there any documentation? I would be happy to volunteer to contribute to the project by writing documentation but I'm afraid I lack the basic understanding to do so. I looked at the generated documentation here: http://rubydoc.info/gems/coreaudio/ but that's only more mysterious. I would think that would at least show the method prototypes but I only see one method CoreAudio::AudioFile:read. The examples have calls to other methods that don't show up here.
2) What are the available methods / is there an API? The examples have things like CoreAudio.default_input_device.input_buffer and CoreAudio.default_output_device.output_stream, and by copying the examples I have been able to get some sound out through the CoreAudio.default_output_device.output_buffer. default_input_device and default_output_device are instances of CoreAudio::AudioDevice. This is awesome, but I don't know what CoreAudio::AudioDevice is.
3) What is CoreAudio::AudioDevice? Having no more sophisticated tools at my disposal I grepped for the string 'AudioDevice' in the installed gem files. I found a number of instances in the file /ext/coreaudio.m. The code looks like C and I think .m is a file suffix for a certain type of file in an Objective C project. The only place in that file where I see the string 'AudioDevice' where it isn't part of a larger variable name (eg: rb_cAudioDevice) is in the comments, but I'm guessing that this objective C code gets compiled into... I don't know what...
4) What are these Objective C files doing here? Where's the Ruby? Shouldn't there be a file or files written in Ruby somewhere in this project that define the class CoreAudio::AudioDevice? The only .rb file on any appreciable size is lib/coreaudio/audiofile.rb (where the read method is defined, the only method shown in rdoc).
5) What is coreaudio_ext.bundle? The gem installation process created a binary file called coreaudio_ext.bundle which, if I had to guess, would be the result of compiling the objective C files in the project. This file is required in lib/coreaudio/coreaudio.rb, so must be significant to Ruby, but it's a black box to me. I assume it is a black box containing the definition of the ephemeral CoreAudio::AudioDevice. How would I know how to use this other than looking at the examples?
Please help me understand what I'm missing.

Since I asked, I'll attempt to document my answer for any future archaeologists who may puzzle with the same questions. Thanks to Neil Slater, for pointing me in the direction of important resources needed to answer my question(s).
1) Is there any documentation? - not really. The CoreAudio::AudioFile:read function is the only one documented by rdoc because everything else is implemented in a Ruby extension in C, by the mechanism described in detail here: http://media.pragprog.com/titles/ruby3/ext_ruby.pdf. Turns out that this process has been done for a great number of existing C libraries to add their functionality to Ruby.
2) What are the available methods / is there an API? - The extension created is the equivalent of the following: (corrections welcome)
Module CoreAudio
class AudioDevice
attr_reader :devid, :name, :available_sample_rate, :nominal_rate, :input_stream, :output_stream
def initialize(devIdVal, options)
...
end
def actual_rate
...
end
def output_loop(frame)
...
end
def output_buffer(frame)
...
end
def input_buffer(frame)
...
end
end
class AudioStream
attr_reader :channels, :buffer_frame_size
def initialize(devid_val, is_input)
...
end
end
class OutLoop
def []=(index, val)
...
end
def start
...
end
def stop
...
end
end
class AudioBuffer
def start
...
end
def stop
...
end
def dropped_frame
...
end
def reset_dropped_frame
...
end
def space
...
end
end
class OutputBuffer
def <<(nary)
...
end
end
class InputBuffer
def read(num)
...
end
end
def devices
...
end
def default_input_device
...
end
def default_output_device
...
end
def set_default_output_device
...
end
class AudioFile
def initialize
...
end
def close
...
end
def write(data)
...
end
def read_frames(frame_val)
...
end
def rate
...
end
def channels
...
end
def inner_rate
...
end
def inner_channels
...
end
end
end
3) What is CoreAudio::AudioDevice? - I was mistaken about the string "AudioDevice" not appearing on its own as an identifier - it appears in the C code as follows:
rb_cAudioDevice = rb_define_class_under(rb_mCoreAudio, "AudioDevice", rb_cObject);
this is declaring the class AudioDevice inside of module CoreAudio.
4) Where's the Ruby? - this gem is extending Ruby with coreaudio, written in C.
5) What is coreaudio_ext.bundle? this is the compiled C code. It's required by the gem in coreaudio.rb
require "coreaudio/coreaudio_ext"
A .bundle file is the Mac equivalent of a .dll file in Windows. (I don't believe there is a Windows implementation of this gem, as CoreAudio is a Mac-specific technology)

Related

Forbid mocking methods that are undefined on original object

I want to ensure that my EventNotifier object catches and logs an exception to the Rails logger, so I writed the following rspec test using rspec-mocks:
describe EventNotifier do
before(:all) do
RSpec::Mocks::setup(self)
end
it 'logs exception on error' do
error = RuntimeError.new('simulated for tests')
EventDispatcher.should_receive(:dispatch).with('a message').and_raise(error)
Rails.logger.should_receive(:warning).with(error)
subject.notify('a message')
end
end
class EventDispatcher
def dispatch(message)
# stuff
end
end
class EventNotifier
def notify(message)
EventDispatcher.dispatch(message)
rescue RuntimeError => e
Rails.logger.warning(e)
end
end
The test passes with bells and whistles, but I did a big mistake. Can you spot it? Indeed, the Rails method to log a warning is warn, not warning. So this code will fail when used for real.
Is there a way to make rspec-mock forbid mocking methods that do not exist on the original object?
Another example where it can be useful: if I decide to rename EventDispatcher#dispatch as EventDispatcher#route, then the test still passes. If it was java, it would have failed at compilation because the interface has changed. With rspec-mock, I do not know how to make it fail automatically.
It will be possible with rspec-mocks 3.0 which introduces verifying doubles:
Verifying doubles are a stricter alternative to normal doubles that provide
guarantees about what is being verified. When using verifying doubles, RSpec
will check that the methods being stubbed are actually present on the
underlying object if it is available. Prefer using verifying doubles over
normal doubles.
So given these classes (examples taken from the spec):
class ConsoleNotifier
def notify(msg)
puts message
end
end
class User < Struct.new(:notifier)
def suspend!
notifier.notify("suspended as")
end
end
This example would pass because the method notify exists for a real ConsoleNotifier instance:
describe User, '#suspend!' do
it 'notifies the console' do
notifier = instance_double("ConsoleNotifier")
expect(notifier).to receive(:notify).with("suspended as")
user = User.new(notifier)
user.suspend!
end
end
If the notify method is renamed as publish, the example would fail because expectations are on the method notify and that method does not exist on target object.
# would make example fail
class ConsoleNotifier
def publish(msg)
puts message
end
end
If we add a color argument to the notify method, it will fail too because the method arity is incorrect in the expectation (wrong number of arguments).
class ConsoleNotifier
def notify(msg, color)
puts color + message
end
end
When putting expectations on class, like EventDispatcher.should_receive(:dispatch) or expect(EventDispatcher).to receive(:dispatch), the created double is named a partial double in rspec-mocks vocabulary.
To make partial doubles act like verifying doubles, the verify_partial_doubles configuration option must be set:
RSpec.configure do |config|
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
end
And it will automagically ensure that the EventDispatcher class has a dispatch method. So if this method is renamed, the test will fail, as expected!

Rails 3 ActionMailer class loaded but methods not available in controller

Many simmilar Q/A on this topic here and there, but I was unable to find exact solution for my problem. Using Rails 3.0.9 now, and trying to upgrade existing older application(not Rails).
The goal is to send simple email to new clients created by admins.
Have been following this oficial guide (and many others), but with no success.
The issue is, that method(s) defined in this controller, from class 'UserMailer', aren`t recognised from another controller, while class 'UserMailer' itself recognised is(how do I know this, will be explained below):
/app/mailers/user_mailer.rb
class UserMailer < ActionMailer::Base
default :from => "info#xxxxx.sk"
def kokotina # << this is just a dummy method for testing
caf = "ssss"
end
def regMailUsr(nazov, priezvisko, email, pass)
#nazov = nazov
#priezvisko = priezvisko
#email = email
#pass = pass
#url = "http://loyalty2.xxxx.sk"
mail(to: email, subject: 'Vaša registrácia bola dokončená.')
end
end
I have also created View for this mail controller but that is not important right now.
The fragments from clients controller are here:
/app/controllers/clients_controller.rb
# encoding: UTF-8
class ClientsController < ApplicationController
load_and_authorize_resource
.......
def new
#noveHeslo = genHeslo(10) # << I defined this in application_controller.rb and it works
UserMailer.kokotina # << just a dummy method from UserMailer
#client = Client.new(params[:client])
.......
end
.......
def create
.......
if #client.save
#send email to new client:
UserMailer.regMailUsr(params[:client][:fname], params[:client][:lname], params[:client][:email], params[:client][:password]).deliver
.....
end ......
Now how do I know that my class is loaded? If in client controller, I change 'UserMailer' to 'xUserMailer', I will get 'no class or method in ...' error, but without 'x', I get only:
'undefined method `kokotina' for UserMailer:Class'
I also tried to define my methods in UserMailer:Class like this:
def self.kokotina # << this is just a dummy method for testing
caf = "ssss"
end
#or even like this
def self <<
def kokotina # << this is just a dummy method for testing
caf = "ssss"
end
end
#and then tried to invoke this method(s) like this:
UserMailer.new.kokotina
#or simply
kokotina
Strange is, that when I put contents of file '/app/mailers/user_mailer.rb' at the end of 'application_helper.rb' file, just after the end of 'module ApplicationHelper', I get no errors but of course, it won`t work.
Please keep in mind that I have no problem coding in another languages, but this mystic/kryptic rules of Ruby on Rails are still a complete mistery to me and unfortunatelly, I don`t have time or even motivation to read time extensive quides or even books for RoR beginners. I have been coding much more difficult applications and implementations, but this heavily discriminating system is driving me nuts.
Thank you all!
Problem solved!
The trick was, that in '/app/mailers/user_mailer.rb', I had multibyte characters. In mail subject.
So I added:
# encoding: UTF-8
at the very first line of '/app/mailers/user_mailer.rb'
I found this by total accident: later my rails app could not start, and server was simply throwing HTTP 500 error. So no trace, error defining etc.
I found out that multibyte string in:
mail(to: email, subject: 'Vaša registrácia bola dokončená.')
Was responsible for crash. When I removed that string, I noticed one important side effect: my methods became magicaly available for another controller!!!!
So if someone could give me at least one reason to lowe Rails...

Resque and Resque_mailer with Devise

I am implementing background email processing with Resque using the resque_mailer gem (https://github.com/zapnap/resque_mailer). I was able to get it to work for all my emails except the ones sent by Devise.
I went through a bunch of SO questions, and blog posts (for instance http://teeparham.posterous.com/send-devise-emails-with-resque) but could not find a way to get it to work.
What are the precise steps to follow to get resque_mailer to work with Devise?
I went through tee's answer and several resources online, but couldn't find a working solution.
After a few days of reading through resque-mailer and devise code, a solution that worked for me. Thanks to tee for gist which put me in right direction.
Assuming your app/mailers/application_mailer.rb looks similar to
class ApplicationMailer < ActionMailer::Base
include Resque::Mailer # This will add a `self.perform` class method, which we will overwrite in DeviseResqueMailer
end
In config/initializers/devise.rb
Devise.parent_mailer = "ApplicationMailer"
Devise.setup do |config|
config.mailer = 'DeviseResqueMailer'
end
In the resource class which uses devise, overwrite the send_devise_notification method to send resource class and id instead of object to prevent marshalling
# app/models/user.rb
protected
def send_devise_notification(notification, *args)
# Based on https://github.com/zapnap/resque_mailer/blob/64d2be9687e320de4295c1bd1b645f42bd547743/lib/resque_mailer.rb#L81
# Mailer may completely skip Resque::Mailer in certain cases - and will fail as we write custom handle in DeviseResqueMailer assuming mails are handled via resque
# So in those cases, don't retain original devise_mailer so things work properly
if ActionMailer::Base.perform_deliveries && Resque::Mailer.excluded_environments.exclude?(Rails.env.to_sym)
# Originally devise_mailer.send(notification, self, *args).deliver
# Modified to ensure devise mails are safely sent via resque
resource_id, resource_class = self.id, self.class.name
devise_mailer.send(notification, {resource_id: resource_id, resource_class: resource_class}, *args).deliver
else
super
end
end
Finally, in app/mailers/devise_resque_mailer.rb, fetch the record again from the database and continue
class DeviseResqueMailer < Devise::Mailer
def self.perform(action, *args)
# Hack to prevent RuntimeError - Could not find a valid mapping for admin.attributes
record_hash = args.shift
record = record_hash["resource_class"].constantize.find(record_hash["resource_id"])
args.unshift(record)
super # From resque-mailer
end
end
I feel this approach is a better than using devise-async as all the mails go through same code path. Its easier to control and overwrite if needed.
I'd take a look at devise-async. Looks like it fits your use case. Devise Async

Stub method called from constructor when using Fabrication

I've got the following model
class User < ActiveRecord::Base
before_create :set_some_values
private
def set_some_values
#do something
end
end
In specs I'm using Fabrication gem to create objects but I can't find a way to stub the set_some_values method. I tried
User.any_instance.stub!(:set_some_values).and_return(nil)
but Fabrication seems to ignore this. Is it possible to do?
This is why I don't like ActiveRecord callbacks -- because if you want to have nothing to do with a callback (because, say, you're making a call to an external service inside the callback) you still have to be concerned about stubbing it out. Yes you could stub out methods inside the callback, but it's the same problem, and actually it's a bit worse because now you are concerned about something inside a method you want nothing to do with.
As usual there are multiple options here.
One option which I've used a lot in the past is, add a condition to your callback that turns it off by default. So your Post class could look like:
class Post
before_save :sync_with_store, :if => :syncing_with_store?
def syncing_with_store?; #syncing_with_store; end
attr_writer :syncing_with_store
def sync_with_store
# make an HTTP request or something
end
end
Now wherever you really want to call the callback (perhaps in your controller or wherever), you can set post.syncing_with_store = true before you call post.save.
The downside to this approach is, it's something that you (and other devs working with you) have to keep in mind, and it's not really obvious that you have to do this. On the other hand, if you forget to do this, nothing bad happens.
Another option is to use a fake class. Say you have a Post that pushes its data to an external data store on save. You could extract the code that does the pushing to a separate class (e.g. Pusher) which would be accessible at Post.pusher_service. By default, though, this would be set to a fake Pusher class that responds to the same interface but does nothing. So like:
class Post
class << self
attr_accessor :pusher_service
end
self.pusher_service = FakePostPusher
before_save :sync_with_store
def sync_with_store
self.class.pusher_service.run(self)
end
end
class FakePostPusher
def self.run(post)
new(post).run
end
def initialize(post)
#post = post
end
def run
# do nothing
end
end
class PostPusher < FakePostPusher
def run
# actually make the HTTP request or whatever
end
end
In your production environment file, you'd set Post.pusher_service = Pusher. In individual tests or test cases, you'd make a subclass of Post -- let(:klass) { Class.new(Post) } -- and set klass.pusher_service = Pusher (that way you don't permanently set it and affect future tests).
The third approach, which I have been experimenting with, is this: simply don't use ActiveRecord callbacks. This is something I picked up from Gary Bernhardt's screencasts (which, by the way, are pretty amazing). Instead, define a service class that wraps the act of creating a post. Something like:
class PostCreator
def self.run(attrs={})
new(attrs).run
end
def initialize(attrs={})
#post = Post.new(attrs)
end
def run
if #post.save
make_http_request
return true
else
return false
end
end
def make_http_request
# ...
end
end
This way PostCreator.run(attrs) is the de facto way of creating a post instead of going through Post. Now to test saves within Post, there's no need to stub out callbacks. If you want to test the PostCreator process, there's no magic going on, you can easily stub out whichever methods you want or test them independently. (You could argue that stubbing out methods here is the same as stubbing out AR callbacks, but I think it's more explicit what's going on.) Obviously this only handles post creation, but you could do the same for post updating too.
Anyway, different ideas, pick your poison.
The #set_some_values method here is called when you call #save on the record. So it has nothing to do with the constructor and therefore you don't need to stub User.any_instance -- just make your record and then do a partial stub, as in:
record.stub(:set_some_values)
record.save

Extend a module in Rails 3

I want to define a function available_translations which lists the translations I have made for my application into the I18n module.
I tried putting the following into the file lib/i18n.rb, but it doesn't work when I try to use it from the rails console:
module I18n
# Return the translations available for this application.
def self.available_translations
languages = []
Dir.glob(Rails.root.to_s + '/config/locales/*.yml') do |filename|
if md = filename.match #^.+/(\w+).yml$#
languages << md[1]
end
end
languages
end
end
Console:
ruby-1.9.2-p290 :003 > require Rails.root.to_s + '/lib/i18n.rb'
=> false
ruby-1.9.2-p290 :004 > I18n.available_translations
NoMethodError: undefined method `available_translations' for I18n:Module
...
Besides solving my concrete problem, I would be very pleased to learn how this whole module thing in Ruby on Rails works because it still confuses me, so I would appreciate links to the docs or source code very much.
Either of these will solve your problem:
move the code to config/initializers/i18n.rb, or
require your file from config/application.rb, or
name your class otherwise (to trigger autoload)
The code in lib/i18n.rb wil not be loaded by autoload since I18n name will be already loaded, so either you load it yourself or change the class name (and file name) so the new name will trigger autoload behavior.
BTW, the I18n.available_locales() method is presented in rails.