myna_eventmachine 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,16 +1,50 @@
1
1
  # Myna Ruby/EventMachine Client
2
2
 
3
- This is a Ruby client for the v1 Myna API, using EventMachine for asychronous goodness.
3
+ This is a Ruby client for the v1 [Myna](http://mynaweb.com) API, using EventMachine for asychronous goodness. Tested in Ruby 1.9.2
4
4
 
5
- Tested in Ruby 1.9.2
5
+ ## Installation
6
+
7
+ `gem install myna_eventmachine`
8
+
9
+ ## Usage
10
+
11
+ You can get a suggestion from Myna without authorizing:
12
+
13
+ ```ruby
14
+ expt = Myna.experiment('45923780-80ed-47c6-aa46-15e2ae7a0e8c')
15
+ suggestion = expt.suggest.get
16
+ # suggestion has two attributes: choice and token
17
+ # suggestion.choice is a string, the choice made by Myna
18
+ # suggestion.token is a string, the token you send back to Myna when you reward
19
+ puts("Choice is #{suggestion.choice}")
20
+ expt.reward(suggestion.token, 1.0)
21
+ ```
22
+
23
+ To create an experiment, add and delete variants, and so on, you must authorize first:
24
+
25
+ ```ruby
26
+ myna = Myna.authorize('email', 'password')
27
+ # Create an experiment
28
+ expt = myna.create('My funky new experiment').get
29
+ expt.create_variant('My new variant')
30
+ expt.create_variant('My other new variant')
31
+ ```
32
+
33
+ For more detail, see [the wiki](https://github.com/myna/ruby-eventmachine-client/wiki)
6
34
 
7
35
  ## Development Notes
8
36
 
9
37
  The easiest way to install the dependencies is to install [Bundler](http://gembundler.com/) and then run `bundle install`
10
38
 
11
- Run `rake test` to run the tests.
39
+ Rake commands:
40
+
41
+ - `test` to run the tests.
42
+ - `build` to build a Gem
43
+ - `install` to install the Gem locally
44
+ - `release` to push the Gem to RubyGems
12
45
 
13
46
 
14
47
  ## TODO
15
48
 
16
- Futures need to handle failures, and need to rigourously trap exceptions and propagate them
49
+ - Futures need to handle failures, and need to rigourously trap exceptions and propagate them.
50
+ - Some RDoc or equivalent might be useful
data/lib/myna/future.rb CHANGED
@@ -26,45 +26,83 @@ require 'thread'
26
26
 
27
27
 
28
28
  module Future
29
+
30
+ def Future.run
31
+ future = Future.new
32
+ future.run(yield)
33
+ future
34
+ end
35
+
29
36
  class Future
30
37
  def initialize()
31
38
  @lock = Mutex.new
32
39
  @signal = ConditionVariable.new
33
40
  @value = nil
34
- @delivered = false
41
+ @result = false # false, :success, or :failure
35
42
  @listeners = []
36
43
  end
37
44
 
38
- def deliver(value)
45
+ def set(value, result)
39
46
  @lock.synchronize {
40
- if @delivered
41
- raise ArgumentError, "This future has already been delivered a value"
47
+ if @result
48
+ raise ArgumentError, "This future has already been completed with a value"
42
49
  else
43
50
  @value = value
44
- @delivered = true
51
+ @result = result
45
52
  @signal.broadcast
46
53
  end
47
54
  }
48
- @listeners.each do |block|
49
- block.call(value)
55
+
56
+ if result == :success
57
+ @listeners.each do |block|
58
+ block.call(value)
59
+ end
60
+ end
61
+ end
62
+
63
+ def succeed(value)
64
+ self.set(value, :success)
65
+ end
66
+
67
+ def fail(value)
68
+ self.set(value, :failure)
69
+ end
70
+
71
+ def run
72
+ begin
73
+ self.succeed(yield)
74
+ rescue => exn
75
+ self.fail(exn)
50
76
  end
51
77
  end
52
78
 
53
79
  def get
80
+ def handle_value
81
+ case @result
82
+ when :success
83
+ @value
84
+ when :failure
85
+ raise @value
86
+ else
87
+ raise ArgumentError, "Attempted to handle_value when result #{@result} is not :success or :failure"
88
+ end
89
+ end
90
+
54
91
  @lock.synchronize {
55
- if !@delivered
92
+ if @result
93
+ handle_value
94
+ else
56
95
  @signal.wait(@lock)
57
96
  @signal.broadcast
97
+ handle_value
58
98
  end
59
-
60
- @value
61
99
  }
62
100
  end
63
101
 
64
102
  def on_complete(&block)
65
103
  return_value = false
66
104
  @lock.synchronize {
67
- if @delivered
105
+ if @result == :success
68
106
  return_value = true
69
107
  else
70
108
  @listeners.push(block)
@@ -82,7 +120,9 @@ module Future
82
120
  def map(&block)
83
121
  future = Future.new
84
122
  self.on_complete do |value|
85
- future.deliver(block.call(value))
123
+ future.run do
124
+ block.call(value)
125
+ end
86
126
  end
87
127
  future
88
128
  end
data/lib/myna/myna.rb CHANGED
@@ -35,14 +35,14 @@ module Myna
35
35
  future = Future::Future.new
36
36
 
37
37
  if EventMachine::reactor_running?
38
- future.deliver(true)
38
+ future.succeed(true)
39
39
  else
40
40
  Thread.new do
41
41
  EventMachine::run {}
42
42
  end
43
43
 
44
44
  EventMachine::next_tick do
45
- future.deliver(true)
45
+ future.succeed(true)
46
46
  end
47
47
  end
48
48
 
@@ -78,8 +78,8 @@ module Myna
78
78
  client = EventMachine::HttpRequest.new(uri)
79
79
 
80
80
  http = yield(client)
81
- http.errback { future.deliver(Response::NetworkError.new) }
82
- http.callback { future.deliver(Response.parse(http.response)) }
81
+ http.errback { future.succeed(Response::NetworkError.new) }
82
+ http.callback { future.succeed(Response.parse(http.response)) }
83
83
  end
84
84
 
85
85
  future
@@ -118,6 +118,10 @@ module Myna
118
118
  end
119
119
  end
120
120
 
121
+ def Myna.experiment(uuid)
122
+ Myna::Experiment.new(uuid)
123
+ end
124
+
121
125
  # The simple API for unauthorized access
122
126
  class Experiment
123
127
  def initialize(uuid, host = UnauthorizedHost)
@@ -143,11 +147,11 @@ module Myna
143
147
  end
144
148
 
145
149
  def Myna.authorize(email, password, host = AuthorizedHost)
146
- ExperimentFactory.new(email, password, host)
150
+ AuthorizedMyna.new(email, password, host)
147
151
  end
148
152
 
149
153
  # The more complex API for authorized access
150
- class ExperimentFactory
154
+ class AuthorizedMyna
151
155
  def initialize(email, password, host = AuthorizedHost)
152
156
  @email = email
153
157
  @password = password
@@ -0,0 +1,27 @@
1
+ # Copyright 2011 by Untyped Ltd. All Rights Reserved\
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # No portion of this Software shall be used in any application which does not
14
+ # use the ReportGrid platform to provide some subset of its functionality.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ # THE SOFTWARE.
23
+ #
24
+
25
+ module Myna
26
+ VERSION = "1.1.0"
27
+ end
data/lib/myna.rb ADDED
@@ -0,0 +1,26 @@
1
+ # Copyright 2011 by Untyped Ltd. All Rights Reserved\
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # No portion of this Software shall be used in any application which does not
14
+ # use the ReportGrid platform to provide some subset of its functionality.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ # THE SOFTWARE.
23
+ #
24
+
25
+ require 'myna/myna'
26
+ require 'myna/response'
data/test/test_future.rb CHANGED
@@ -33,7 +33,7 @@ describe Future do
33
33
  v + 3
34
34
  end
35
35
 
36
- f1.deliver(1)
36
+ f1.succeed(1)
37
37
  f2.get.must_equal 4
38
38
  end
39
39
 
@@ -45,10 +45,18 @@ describe Future do
45
45
 
46
46
  Thread.new do
47
47
  sleep(1)
48
- f1.deliver(1)
48
+ f1.succeed(1)
49
49
  end
50
50
 
51
51
  f2.get.must_equal 4
52
52
  end
53
53
  end
54
+
55
+ describe "Future.fail" do
56
+ it "must cause an exception to be raised on get" do
57
+ # Not implemented
58
+ # Also test Future.run and future.run
59
+ true.must_equal false
60
+ end
61
+ end
54
62
  end
data/test/test_suggest.rb CHANGED
@@ -36,13 +36,13 @@ describe Myna do
36
36
 
37
37
  describe "suggest" do
38
38
  it "must return a suggestion for a valid experiment" do
39
- expt = Myna::Experiment.new('45923780-80ed-47c6-aa46-15e2ae7a0e8c')
39
+ expt = Myna.experiment('45923780-80ed-47c6-aa46-15e2ae7a0e8c')
40
40
  ans = expt.suggest
41
41
  ans.get.must_be_kind_of Response::Suggestion
42
42
  end
43
43
 
44
44
  it "must return an error for an invalid experiment" do
45
- expt = Myna::Experiment.new('bogus')
45
+ expt = Myna.experiment('bogus')
46
46
  ans = expt.suggest
47
47
  ans.get.must_be_kind_of Response::ApiError
48
48
  end
@@ -50,7 +50,7 @@ describe Myna do
50
50
 
51
51
  describe "reward" do
52
52
  it "must succeed when given correct token and amount" do
53
- expt = Myna::Experiment.new('45923780-80ed-47c6-aa46-15e2ae7a0e8c')
53
+ expt = Myna.experiment('45923780-80ed-47c6-aa46-15e2ae7a0e8c')
54
54
  suggestion = expt.suggest.get
55
55
 
56
56
  ans = expt.reward(suggestion.token, 0.5)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: myna_eventmachine
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -22,9 +22,11 @@ files:
22
22
  - Gemfile.lock
23
23
  - README.md
24
24
  - Rakefile
25
+ - lib/myna.rb
25
26
  - lib/myna/future.rb
26
27
  - lib/myna/myna.rb
27
28
  - lib/myna/response.rb
29
+ - lib/myna/version.rb
28
30
  - myna_eventmachine.gemspec
29
31
  - test/test_authorized.rb
30
32
  - test/test_future.rb