Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Add Cellulate, a late init Celluloid mixin #126

Closed
wants to merge 2 commits into from

2 participants

RageLtMan Tony Arcieri
RageLtMan

This commit adds Cellulate, a mixin which can be used to convert
an existing object into a Celluloid Actor proxy.

Either mix in, or extend your object with Cellulate and assign it
the result of its own "cellulate" call. See cellulate.rb in
examples for code sample.

TODO:
testing
create Cellulate::IO and DCellulate for the corresponding
libraries.

RageLtMan added some commits
RageLtMan Add Cellulate, a late init Celluloid mixin
This commit adds Cellulate, a mixin which can be used to convert
an existing object into a Celluloid Actor proxy.

Either mix in, or extend your object with Cellulate and assign it
the result of its own "cellulate" call. See cellulate.rb in
examples for code sample.

TODO:
testing
create Cellulate::IO and DCellulate for the corresponding
libraries.
196d543
RageLtMan Namespace, safety switch, and big warning sign.
Add a warning with an explanation and example as to why this is
dangerous.
Move Cellulate into Celluloid namespace.
Make :cellulate private.
Update example.
ae84639
Tony Arcieri
Owner

I'm not sure this is a good idea... from your comments you seem to have gathered as much yourself ;)

RageLtMan

In principle, i wholeheartedly agree with your approach to making threaded operation as safe and grunt proof as possible. In the real world, i've been running into a lot of problems integrating Celluloid and its derivatives into existing code where we deal with conditional/staged object construction or a pre-existing threading model.

The first problem is solved outright here, and the button for this is bright red with clearly marked warning signs.
The second problem needs much more work, but is partially resolved here as this allows us to replace complicated thread logic (under the proper conditions) with an actor proxy via extension and a method call. Aside from simplification this approach allows for compatible behavior between new objects designed with Celluloid in mind, and legacy code which is expensive to rewrite from the ground up (the proper way).

I suppose the resolution to this PR depends on what we think is a "good idea." Do you favor principle and proper development methodology, or wider adoption and ease of integration for an existing codebase or users with edge cases requiring late init?

If the decision is a painful one, we could provide cellulate and its spawn (IO, dcell, etc) as a separate gem, and avoid my polluting your code. Though i'm not sure how much that would assist with adoption, those in real need should be able to find it.

Tony Arcieri
Owner

I think I'd probably prefer a separate gem. This isn't an approach I'd really like to encourage, but one that might make sense for people who are doing retrofits.

As far as steering people towards it, that can be done on the mailing list / IRC channel.

Tony Arcieri
Owner

Per our discussion on IRC, I think this is better served by a separate gem. We can put it under the Celluloid project.

Tony Arcieri tarcieri closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 27, 2012
  1. Add Cellulate, a late init Celluloid mixin

    RageLtMan authored
    This commit adds Cellulate, a mixin which can be used to convert
    an existing object into a Celluloid Actor proxy.
    
    Either mix in, or extend your object with Cellulate and assign it
    the result of its own "cellulate" call. See cellulate.rb in
    examples for code sample.
    
    TODO:
    testing
    create Cellulate::IO and DCellulate for the corresponding
    libraries.
Commits on Nov 29, 2012
  1. Namespace, safety switch, and big warning sign.

    RageLtMan authored
    Add a warning with an explanation and example as to why this is
    dangerous.
    Move Cellulate into Celluloid namespace.
    Make :cellulate private.
    Update example.
This page is out of date. Refresh to see the latest.
Showing with 103 additions and 0 deletions.
  1. +38 −0 examples/cellulate.rb
  2. +65 −0 lib/celluloid/cellulate.rb
38 examples/cellulate.rb
View
@@ -0,0 +1,38 @@
+#!/usr/bin/env ruby
+
+$:.push File.expand_path('../../lib', __FILE__)
+require 'celluloid/cellulate'
+
+class BaseFibber
+ def fib(n)
+ n < 2 ? n : fib(n-1) + fib(n-2)
+ end
+end
+
+class Fibber
+ include Celluloid
+
+ def fib(n)
+ n < 2 ? n : fib(n-1) + fib(n-2)
+ end
+end
+
+fibber = Fibber.new
+future = fibber.future(:fib,10)
+
+base_fibber = BaseFibber.new
+begin
+ base_future = base_fibber.future(:fib,11)
+rescue => e
+ puts "We failed because we dont know the future: #{e}"
+end
+puts "#{base_fibber.class} methods count before conversion: #{base_fibber.methods.count}"
+base_fibber.extend(Celluloid::Cellulate)
+# call protected method
+base_fibber = base_fibber.send(:cellulate)
+puts "#{base_fibber.class} methods count after conversion: #{base_fibber.methods.count}"
+
+base_future = base_fibber.future(:fib,11)
+
+puts "Everyone is now clairvoyant: future == #{future.value} and base_future == #{base_future.value}"
+
65 lib/celluloid/cellulate.rb
View
@@ -0,0 +1,65 @@
+require 'celluloid'
+
+module Celluloid::Cellulate
+
+ ##
+ # WARNING, DANGER, ATTENTION, ETC:
+ # USING THIS MODULE CAN LEAD TO AWFUL THINGS HAPPNING.
+ #
+ # This module is designed to perform late init for celluloid
+ # objects. It is intended for instantiated objects which are
+ # conditionally extended during construction, and not as a late
+ # backgrounding method for existing objects which exist elsewhere.
+ #
+ # Ruby 1.9 does not allow reassignment of self, or rather the
+ # value at the address of the object contextually defined as self.
+ # This means that if an object is instantiated, and is referenced
+ # by some other context, then the using this method will not replace
+ # the object in that context with the proxy created here.
+ # The result can lead to calls on the raw object...
+ #
+ # Example of how things go wrong:
+ # obj = Object.new; ar = []; ar << obj; obj.extend(Celluloid::Cellulate)
+ # obj = obj.send(:cellulate)
+ # ar[0] != obj
+ #
+ # YOU HAVE BEEN WARNED...
+ ##
+
+ # Required when building an Actor
+ def superclass
+ self.class
+ end
+
+ # Force the developer to try harder in hopes they read the warning.
+ private
+
+ # Use this method for converting to a celluloid object
+ # Ex: obj = obj.send(:cellulate)
+ # TODO: log this terrible event, log rescues
+ def cellulate
+ # Ensure there is no current actor
+ curr_act = nil
+ begin
+ curr_act = Celluloid::Actor.current
+ rescue => e
+ # TODO: log attempt
+ end
+ return self if curr_act
+
+ # Add celluloid and its methods
+ self.extend(Celluloid)
+ self.extend(Celluloid::ClassMethods)
+ self.class.send(:include, Celluloid::InstanceMethods)
+ # Expose all methods as singleton
+ begin
+ self.extend
+ rescue => e
+ # TODO: log failure
+ end
+ proxy = Celluloid::Actor.new(self, actor_options).proxy
+ proxy._send_(:initialize)
+ return proxy
+ end
+
+end
Something went wrong with that request. Please try again.