eva 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +234 -0
- data/Rakefile +1 -0
- data/benchmark/area.rb +24 -0
- data/benchmark/fib.rb +12 -0
- data/bin/eva +13 -0
- data/eva.gemspec +21 -0
- data/example/basic.rb +140 -0
- data/example/delegate.rb +0 -0
- data/example/emitter.rb +5 -0
- data/example/timers.rb +18 -0
- data/lib/eva.rb +17 -0
- data/lib/eva/emitter.rb +68 -0
- data/lib/eva/ext/object.rb +17 -0
- data/lib/eva/space.rb +91 -0
- data/lib/eva/version.rb +3 -0
- metadata +81 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Davide D'Agostino
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,234 @@
|
|
1
|
+
# Eva
|
2
|
+
|
3
|
+
Eva is an effortless event driven micro framework that runs on Ruby (cruby and jruby) through
|
4
|
+
[eventmachine](http://eventmachine.rubyforge.org) with a different **syntax**.
|
5
|
+
|
6
|
+
The common problem with *EventMachine* is to deal with callbacks, fortunately thanks to
|
7
|
+
people like [Ilya Grigorik](https://github.com/igrigorik) and `things`
|
8
|
+
like [em-synchrony](https://github.com/igrigorik/em-synchrony) make this job is a little simpler but
|
9
|
+
a bit far to be _native_ like in `javascript`.
|
10
|
+
|
11
|
+
So here my idea to make **experiment** and try to figure out a new way to write better our applications
|
12
|
+
following the nature of the reactor pattern with a special focus on **simplicity** and **clearity**.
|
13
|
+
|
14
|
+
|
15
|
+
## Overview
|
16
|
+
|
17
|
+
It's **pure ruby**, no lexers and parsers. If you want a fast tast on how it looks like,
|
18
|
+
please see [emitter](https://github.com/DAddYE/eva/blob/master/lib/eva/emitter.rb)
|
19
|
+
|
20
|
+
The _idea_ behind it is to build all our apps only using two ruby objects: `namespaces` and `objectspace`
|
21
|
+
|
22
|
+
Like in languages like `javascript` or `lua` if we have:
|
23
|
+
|
24
|
+
```js
|
25
|
+
var hello = function(){ console.log('Hello!') }
|
26
|
+
```
|
27
|
+
|
28
|
+
in order to invoke our function we should use `hello()` otherwise using only `hello` we will get
|
29
|
+
the `Function` object.
|
30
|
+
|
31
|
+
In that way is simple to chain functions and do things like:
|
32
|
+
|
33
|
+
```js
|
34
|
+
var callback = function(obj){ Notifier.sendEmail(obj) }
|
35
|
+
doExpensiveTask(var1, var2, callback)
|
36
|
+
```
|
37
|
+
|
38
|
+
So I applied the same rule on Ruby:
|
39
|
+
|
40
|
+
_unless you don't provide method arguments, invoking a method will get a Method Object instead of the result of the method itself_
|
41
|
+
|
42
|
+
Here an example:
|
43
|
+
|
44
|
+
```rb
|
45
|
+
namespace :Greater,
|
46
|
+
say: ->(text) { p text }
|
47
|
+
|
48
|
+
Greater.say 'Hello World!'
|
49
|
+
=> "Hello World!"
|
50
|
+
|
51
|
+
Greater.say
|
52
|
+
=> #<Method: Module(Greater)#say!>
|
53
|
+
```
|
54
|
+
|
55
|
+
The main difference is that using `objectspace` and `namespace` we create
|
56
|
+
*unbounded* methods, we can still call directly these methods but appending ! (bang) prefix
|
57
|
+
or using [] or using .call
|
58
|
+
|
59
|
+
```rb
|
60
|
+
Greater.say[]
|
61
|
+
Greater.say!
|
62
|
+
Greater.say.call
|
63
|
+
```
|
64
|
+
|
65
|
+
So where this can help us?
|
66
|
+
|
67
|
+
Look a the following examples:
|
68
|
+
|
69
|
+
```rb
|
70
|
+
namespace :Circle,
|
71
|
+
area: ->(n) do
|
72
|
+
Math::PI * n * n
|
73
|
+
end,
|
74
|
+
|
75
|
+
big_calc: -> do
|
76
|
+
perform_a_big_calc
|
77
|
+
end
|
78
|
+
|
79
|
+
LotOfNumbers.each &Circle.area
|
80
|
+
defer Circle.big_calc
|
81
|
+
set_timeout 10, Circle.big_calc
|
82
|
+
```
|
83
|
+
|
84
|
+
## Namespaces
|
85
|
+
|
86
|
+
Namespaces are simply module **functions** a namespace looks like:
|
87
|
+
|
88
|
+
```rb
|
89
|
+
namespace :Greater,
|
90
|
+
say: ->(text) { p [:say, text] }
|
91
|
+
```
|
92
|
+
|
93
|
+
You can extend it from another place simply invoking it:
|
94
|
+
|
95
|
+
```rb
|
96
|
+
namespace :Greater,
|
97
|
+
say_hello: -> do
|
98
|
+
say 'Hello There!'
|
99
|
+
end
|
100
|
+
```
|
101
|
+
|
102
|
+
Remember that it's ruby so you can include extend other objects like:
|
103
|
+
|
104
|
+
```rb
|
105
|
+
module OldModule
|
106
|
+
include Greater
|
107
|
+
end
|
108
|
+
```
|
109
|
+
|
110
|
+
Since is a module **function** remember you can invoke methods directly:
|
111
|
+
|
112
|
+
```rb
|
113
|
+
Greater.say 'Hello World'
|
114
|
+
Greater.say_hello! # <<<<< REMBEMBER THAT IF WE DON'T HAVE ARGUMENT TO ADD A ! or []
|
115
|
+
```
|
116
|
+
|
117
|
+
## Objectspace
|
118
|
+
|
119
|
+
Are exactly ruby classes with no exception and the structure is identical to the module
|
120
|
+
|
121
|
+
```rb
|
122
|
+
objectspace :Greater,
|
123
|
+
say: ->(text){ p [:say, text] }
|
124
|
+
```
|
125
|
+
|
126
|
+
Also here we can add other methods whenever and wherever we want:
|
127
|
+
|
128
|
+
```rb
|
129
|
+
namespace :Greater,
|
130
|
+
say_hello: -> do
|
131
|
+
say 'Hello There!'
|
132
|
+
end
|
133
|
+
```
|
134
|
+
|
135
|
+
So now we can create our Object:
|
136
|
+
|
137
|
+
```rb
|
138
|
+
greater = Greater.new
|
139
|
+
greater.say 'Hello world'
|
140
|
+
greater.say_hello! # <<<<< REMBEMBER THAT IF WE DON'T HAVE ARGUMENT TO ADD A ! or []
|
141
|
+
```
|
142
|
+
|
143
|
+
## Constructors, Private Methods, Attributes ...
|
144
|
+
|
145
|
+
In **eva** there are some special _keys_:
|
146
|
+
|
147
|
+
* `initialize`: used only for **objects**
|
148
|
+
* `include`: to include a module
|
149
|
+
* `extend`: to extend an object with the given module
|
150
|
+
* `attr_reader, attr_writer, attr_accessor, attr`: to create attributes for **objects** and **spaces**
|
151
|
+
* `alias_method`
|
152
|
+
|
153
|
+
Finally if you need to create `private` methods you can do that only prefixing with `_`
|
154
|
+
|
155
|
+
Example:
|
156
|
+
|
157
|
+
```rb
|
158
|
+
objectspace :Tester,
|
159
|
+
attr_reader: [:a, :b, :c],
|
160
|
+
attr_writer: [:d, :e, :f],
|
161
|
+
attr_accessor: [:g, :h, :i],
|
162
|
+
initialize: -> do
|
163
|
+
@a, @b, @c = 1, 2, 3
|
164
|
+
f = foo
|
165
|
+
say
|
166
|
+
end,
|
167
|
+
foo: -> { p [:alias, :bar] },
|
168
|
+
alias_method: { :bar => :foo },
|
169
|
+
say: MyMod.say, # this is a delegate
|
170
|
+
|
171
|
+
Tester.new
|
172
|
+
```
|
173
|
+
|
174
|
+
## Evented
|
175
|
+
|
176
|
+
As mentioned before, the aim of this project is _play nice_ with [eventmachine](http://eventmachine.rubyforge.org),
|
177
|
+
so in _eva_ we extended the main **Object** with few and useful `delegator`:
|
178
|
+
|
179
|
+
```rb
|
180
|
+
:add_periodic_timer, :add_timer, :add_shutdown_hook, :cancel_timer,
|
181
|
+
:defer, :defers_finished?, :fork_reactor, :next_tick,
|
182
|
+
:popen, :reactor_running?, :reactor_thread?,
|
183
|
+
:schedule, :spawn, :system, :tick_loop
|
184
|
+
```
|
185
|
+
|
186
|
+
and few aliases:
|
187
|
+
|
188
|
+
```rb
|
189
|
+
alias :set_interval :add_periodic_timer
|
190
|
+
alias :set_timeout :add_timer
|
191
|
+
alias :clear_interval :cancel_timer
|
192
|
+
alias :clear_timeout :cancel_timer
|
193
|
+
```
|
194
|
+
|
195
|
+
## Installation
|
196
|
+
|
197
|
+
Add this line to your application's Gemfile:
|
198
|
+
|
199
|
+
gem 'eva'
|
200
|
+
|
201
|
+
And then execute:
|
202
|
+
|
203
|
+
$ bundle
|
204
|
+
|
205
|
+
Or install it yourself as:
|
206
|
+
|
207
|
+
$ gem install eva
|
208
|
+
|
209
|
+
## Usage
|
210
|
+
|
211
|
+
You can use `eva` syntax in all your projects simply requiring it, if you plan to use `evented` code
|
212
|
+
so `set_timeout, defer ...` you should use `eva` executable:
|
213
|
+
|
214
|
+
```sh
|
215
|
+
eva examples/timers.rb
|
216
|
+
```
|
217
|
+
|
218
|
+
## Contributing
|
219
|
+
|
220
|
+
1. Fork it
|
221
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
222
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
223
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
224
|
+
5. Create new Pull Request
|
225
|
+
|
226
|
+
# Copyright
|
227
|
+
|
228
|
+
Copyright (C) 2013 Davide D'Agostino - @DAddYE
|
229
|
+
|
230
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
231
|
+
|
232
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
233
|
+
|
234
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/benchmark/area.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'benchmark'
|
3
|
+
|
4
|
+
namespace :Circle,
|
5
|
+
area: ->(n) do
|
6
|
+
Math::PI * n * n
|
7
|
+
end,
|
8
|
+
|
9
|
+
circumference: ->(n) do
|
10
|
+
Math::PI * n * 2
|
11
|
+
end
|
12
|
+
|
13
|
+
TEST = 10_000_000.times.map { rand(100_000..1_000_000) }
|
14
|
+
|
15
|
+
Benchmark.bm(15) do |x|
|
16
|
+
x.report('area normal:') { TEST.each { |n| Circle.area(n) } }
|
17
|
+
x.report('area eva:') { TEST.each(&Circle.area) }
|
18
|
+
end
|
19
|
+
|
20
|
+
# Results:
|
21
|
+
#
|
22
|
+
# user system total real
|
23
|
+
# area normal: 16.940000 0.000000 16.940000 ( 16.947472)
|
24
|
+
# area eva: 6.300000 0.000000 6.300000 ( 6.306686)
|
data/benchmark/fib.rb
ADDED
data/bin/eva
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$:.push File.expand_path('../../lib', __FILE__)
|
3
|
+
require 'eva'
|
4
|
+
|
5
|
+
abort 'Please provide a program to run' unless ARGV[0]
|
6
|
+
|
7
|
+
Eva.run do
|
8
|
+
require File.expand_path ARGV[0]
|
9
|
+
checker = set_interval 0.123 do
|
10
|
+
timers = schedule { Eva.timers.reject { |k,v| v == checker } }
|
11
|
+
Eva.stop if timers.size.zero? && Eva.defers_finished?
|
12
|
+
end
|
13
|
+
end
|
data/eva.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'eva/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = 'eva'
|
8
|
+
gem.version = Eva::VERSION
|
9
|
+
gem.authors = ['DAddYE']
|
10
|
+
gem.email = ['info@daddye.it']
|
11
|
+
gem.summary = 'Effortless event driven micro framework with a tasty syntax'
|
12
|
+
gem.description = gem.summary
|
13
|
+
gem.homepage = 'https://github.com/DAddYE/eva'
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency 'eventmachine', '~>1.0.0'
|
21
|
+
end
|
data/example/basic.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'eva'
|
3
|
+
|
4
|
+
# The first difference is that namespace returns
|
5
|
+
# a module function.
|
6
|
+
#
|
7
|
+
# The second main difference is that using
|
8
|
+
# objectspace and namespace we create
|
9
|
+
# *unbounded* methods, we can still call
|
10
|
+
# directly these methods but appending ! (bang)
|
11
|
+
# or using [] or using .call
|
12
|
+
#
|
13
|
+
# # Examples:
|
14
|
+
# Greater.say[]
|
15
|
+
# Greater.say!
|
16
|
+
# Greater.say.call
|
17
|
+
# Greater.say # => method(:say!)
|
18
|
+
#
|
19
|
+
# But if we provide arguments this is not necessary
|
20
|
+
#
|
21
|
+
# Greater.say 'Foo'
|
22
|
+
# # is equal to
|
23
|
+
# Greater.say! 'Foo'
|
24
|
+
#
|
25
|
+
# This patternis well know in javascript where having:
|
26
|
+
#
|
27
|
+
# function myFunction(){ doSome }
|
28
|
+
#
|
29
|
+
# you should *always* call it with parenthesis if not:
|
30
|
+
#
|
31
|
+
# a = myFunction
|
32
|
+
# # will returns: [Function: MyFunction]
|
33
|
+
#
|
34
|
+
# This pattern, applied with eventmachine, enumerator, callbacks
|
35
|
+
# etc opens the doors to method chainability and fun stuff ;)
|
36
|
+
#
|
37
|
+
# Private methods starts with underscore:
|
38
|
+
#
|
39
|
+
# namespace :Foo,
|
40
|
+
# im_public: -> {}
|
41
|
+
# _im_private: -> {}
|
42
|
+
#
|
43
|
+
# You can also use module methods like:
|
44
|
+
#
|
45
|
+
# * attr
|
46
|
+
# * attr_reader
|
47
|
+
# * attr_writer
|
48
|
+
# * attr_accessor
|
49
|
+
# * alias_method
|
50
|
+
# * alias
|
51
|
+
#
|
52
|
+
|
53
|
+
##
|
54
|
+
# Namespaces
|
55
|
+
#
|
56
|
+
namespace :Greater,
|
57
|
+
say: ->(text) { p [:say, text] }
|
58
|
+
|
59
|
+
# If we have params
|
60
|
+
Greater.say 'Hey'
|
61
|
+
|
62
|
+
# if we want we can add methods
|
63
|
+
# to that module space ... also **private**
|
64
|
+
namespace :Greater,
|
65
|
+
hello: -> { say! 'Hello from Greater!!' },
|
66
|
+
_not_public: -> {}
|
67
|
+
|
68
|
+
Greater.hello!
|
69
|
+
Greater.not_public! rescue p([:not_public, 'is private'])
|
70
|
+
|
71
|
+
##
|
72
|
+
# Classes
|
73
|
+
#
|
74
|
+
objectspace :Foo,
|
75
|
+
say: ->(text){ p [:say, text] }
|
76
|
+
|
77
|
+
# we can add methods whenever we want
|
78
|
+
objectspace :Foo,
|
79
|
+
initialize: -> { say! 'Hello from Foo!!' }
|
80
|
+
# initialize, include and extend ATM are reserved words so (no-bang)
|
81
|
+
# include: Mod # works too
|
82
|
+
# extend: Mod # will works too (if we were in a module)
|
83
|
+
|
84
|
+
Foo.new
|
85
|
+
|
86
|
+
# we can inherit classes
|
87
|
+
objectspace :Bar => Foo,
|
88
|
+
hello: -> { say! 'Hello from Bar!!' }
|
89
|
+
|
90
|
+
Bar.new.hello!
|
91
|
+
#.new will greet Foo and then Bar
|
92
|
+
|
93
|
+
# We can include namespace in other modules
|
94
|
+
module MyMod
|
95
|
+
include Greater
|
96
|
+
end
|
97
|
+
|
98
|
+
MyMod.say! 'Hello from MyMod!!'
|
99
|
+
|
100
|
+
# And so also in classes
|
101
|
+
class MyClass
|
102
|
+
include Greater
|
103
|
+
end
|
104
|
+
|
105
|
+
MyClass.new.say! 'Hello from MyClass!!'
|
106
|
+
|
107
|
+
##
|
108
|
+
# Aliases, Delegate && Attributes
|
109
|
+
#
|
110
|
+
namespace :Aliases,
|
111
|
+
attr_reader: [:a, :b, :c],
|
112
|
+
attr_writer: [:d, :e, :f],
|
113
|
+
attr_accessor: [:g, :h, :i],
|
114
|
+
foo: -> { p [:alias, :bar] },
|
115
|
+
alias_method: { :bar => :foo },
|
116
|
+
say: MyMod.say, # this is a delegate
|
117
|
+
assert: -> {
|
118
|
+
@a, @b, @c = 1, 2, 3
|
119
|
+
say a
|
120
|
+
say b
|
121
|
+
say c
|
122
|
+
self.d, self.e, self.f = 4, 5, 6
|
123
|
+
say @d
|
124
|
+
say @e
|
125
|
+
say @f
|
126
|
+
@g, @h, @i = 7, 8, 9
|
127
|
+
say g
|
128
|
+
say h
|
129
|
+
say i
|
130
|
+
self.g, self.h, self.i = 10, 11, 12
|
131
|
+
say @g
|
132
|
+
say @h
|
133
|
+
say @i
|
134
|
+
say :finish
|
135
|
+
}
|
136
|
+
|
137
|
+
Aliases.say 'Hello Aliases !!!'
|
138
|
+
Aliases.foo!
|
139
|
+
Aliases.assert!
|
140
|
+
p [:attr, :are_public, Aliases.i]
|
data/example/delegate.rb
ADDED
File without changes
|
data/example/emitter.rb
ADDED
data/example/timers.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'eva'
|
3
|
+
|
4
|
+
namespace :Timer,
|
5
|
+
init: -> {
|
6
|
+
@sid = set_interval(1, say_hello)
|
7
|
+
set_timeout(4, quit)
|
8
|
+
},
|
9
|
+
say_hello: -> {
|
10
|
+
puts 'Hello World'
|
11
|
+
},
|
12
|
+
quit: -> {
|
13
|
+
puts 'Bye...'
|
14
|
+
clear_interval @sid
|
15
|
+
}
|
16
|
+
|
17
|
+
puts 'Starting ...'
|
18
|
+
Timer.init!
|
data/lib/eva.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
|
3
|
+
# Bad things here, but EM doesn't support include/extend
|
4
|
+
# in any way know to me
|
5
|
+
Object.send :remove_const, :Eva if defined?(Eva)
|
6
|
+
Eva = EventMachine
|
7
|
+
Eva.send :remove_const, :VERSION
|
8
|
+
|
9
|
+
require 'eva/version'
|
10
|
+
require 'eva/space'
|
11
|
+
require 'eva/ext/object'
|
12
|
+
require 'eva/emitter'
|
13
|
+
|
14
|
+
module Eva
|
15
|
+
extend self
|
16
|
+
attr_reader :timers
|
17
|
+
end
|
data/lib/eva/emitter.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
##
|
2
|
+
# Rif.: http://nodejs.org/api/events.html
|
3
|
+
#
|
4
|
+
module Eva
|
5
|
+
#
|
6
|
+
# A convenience class for object that needs to
|
7
|
+
# emit or subscribe events, like servers or files
|
8
|
+
#
|
9
|
+
objectspace :Emitter,
|
10
|
+
#
|
11
|
+
# @example:
|
12
|
+
#
|
13
|
+
# emitter = Emitter.new
|
14
|
+
# emitter.emit 'connected', user
|
15
|
+
# client.on 'connected', ->(user) { p [:connected, user] }
|
16
|
+
#
|
17
|
+
initialize: -> do
|
18
|
+
@channels = Hash.new { |h, k| h[k] = [] }
|
19
|
+
@uid = 0
|
20
|
+
end,
|
21
|
+
#
|
22
|
+
# @example:
|
23
|
+
#
|
24
|
+
# callback = ->(stream) { puts 'someone connected' }
|
25
|
+
# server.on 'data', cb
|
26
|
+
# # or
|
27
|
+
# server.on('data') do |stream|
|
28
|
+
# puts 'Someone connected'
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
on: ->(event, &block) do
|
32
|
+
id = gen_id
|
33
|
+
@channel[event] << { id => block }
|
34
|
+
id
|
35
|
+
end,
|
36
|
+
#
|
37
|
+
# @example:
|
38
|
+
#
|
39
|
+
# callback = -> (stream) { puts 'someone connected' }
|
40
|
+
# sid = server.on 'connection', callback
|
41
|
+
# server.off 'connection', callback
|
42
|
+
# # or
|
43
|
+
# server.off 'connection', sid
|
44
|
+
#
|
45
|
+
off: ->(event, id) do
|
46
|
+
@channel[event].delete_if { |k, v| [k, v].include?(id) }
|
47
|
+
end,
|
48
|
+
#
|
49
|
+
# Removes all subscribers of a given event
|
50
|
+
#
|
51
|
+
off_all: ->(id) do
|
52
|
+
@channel[event] = []
|
53
|
+
end,
|
54
|
+
#
|
55
|
+
# @example:
|
56
|
+
#
|
57
|
+
# emitter = Emitter.new
|
58
|
+
# emitter.emit 'connected', user
|
59
|
+
#
|
60
|
+
emit: ->(event, data) do
|
61
|
+
next_tick do
|
62
|
+
@channel[event].each { |id, block| block[data] }
|
63
|
+
end
|
64
|
+
end,
|
65
|
+
#
|
66
|
+
# @private
|
67
|
+
_gen_id: -> { @uid += 1 }
|
68
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Object
|
2
|
+
include Eva::Space
|
3
|
+
|
4
|
+
[
|
5
|
+
:add_periodic_timer, :add_timer, :add_shutdown_hook, :cancel_timer,
|
6
|
+
:defer, :defers_finished?, :fork_reactor, :next_tick,
|
7
|
+
:popen, :reactor_running?, :reactor_thread?,
|
8
|
+
:schedule, :spawn, :system, :tick_loop
|
9
|
+
].each do |name|
|
10
|
+
define_method(name) { |*args, &block| Eva.send(name, *args, &block) }
|
11
|
+
end
|
12
|
+
|
13
|
+
alias :set_interval :add_periodic_timer
|
14
|
+
alias :set_timeout :add_timer
|
15
|
+
alias :clear_interval :cancel_timer
|
16
|
+
alias :clear_timeout :cancel_timer
|
17
|
+
end
|
data/lib/eva/space.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
module Eva
|
2
|
+
|
3
|
+
class EvaObject
|
4
|
+
end
|
5
|
+
|
6
|
+
module Space
|
7
|
+
|
8
|
+
def namespace(name, methods={})
|
9
|
+
mod = gen_const(name, Module.new)
|
10
|
+
mod.extend mod
|
11
|
+
mod.extend ClassMethods
|
12
|
+
methods.each { |name, block| gen_methods(mod, name, block) }
|
13
|
+
mod
|
14
|
+
end
|
15
|
+
|
16
|
+
def objectspace(name, methods={})
|
17
|
+
if Hash === name
|
18
|
+
(name, superclass), *methods = *name.to_a
|
19
|
+
else
|
20
|
+
superclass = EvaObject
|
21
|
+
end
|
22
|
+
|
23
|
+
klass = gen_const(name, Class.new(superclass))
|
24
|
+
methods.each { |name, block| gen_methods(klass, name, block) }
|
25
|
+
klass
|
26
|
+
end
|
27
|
+
|
28
|
+
module ClassMethods
|
29
|
+
def included(base)
|
30
|
+
base.extend(base) if base.class == Module
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def gen_methods(obj, name, block)
|
37
|
+
name = name.to_s
|
38
|
+
|
39
|
+
case name
|
40
|
+
when 'attr'
|
41
|
+
obj.send(:attr, *block)
|
42
|
+
when 'attr_reader'
|
43
|
+
obj.send(:attr_reader, *block)
|
44
|
+
when 'attr_writer'
|
45
|
+
obj.send(:attr_writer, *block)
|
46
|
+
when 'attr_accessor'
|
47
|
+
obj.send(:attr_accessor, *block)
|
48
|
+
when 'alias', 'alias_method'
|
49
|
+
block.each { |k,v| obj.send(:alias_method, k, v) }
|
50
|
+
else
|
51
|
+
is_private = name.start_with?('_')
|
52
|
+
name.sub!(/^_/, '') if is_private
|
53
|
+
|
54
|
+
bang = %w[include extend initialize].include?(name.to_s) ? name : "#{name}!"
|
55
|
+
|
56
|
+
block.respond_to?(:to_proc) ?
|
57
|
+
obj.send(:define_method, bang, &block) :
|
58
|
+
obj.send(:define_method, bang){ block }
|
59
|
+
|
60
|
+
return if bang == name
|
61
|
+
|
62
|
+
if Class === obj
|
63
|
+
method = obj.instance_method(bang)
|
64
|
+
obj.send(:define_method, name) do |*a, &b|
|
65
|
+
return send(bang, *a, &b) if b || a.any?
|
66
|
+
method.bind(self)
|
67
|
+
end
|
68
|
+
else
|
69
|
+
method = obj.method(bang)
|
70
|
+
obj.send(:define_method, name) do |*a, &b|
|
71
|
+
return send(bang, *a, &b) if b || a.any?
|
72
|
+
method
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
if is_private
|
77
|
+
obj.send :private, bang
|
78
|
+
obj.send :private, name
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def gen_const(name, object_type)
|
84
|
+
context = respond_to?(:const_set) ? self : Object
|
85
|
+
obj = context.const_defined?(name) ?
|
86
|
+
context.const_get(name) :
|
87
|
+
context.const_set(name, object_type)
|
88
|
+
obj
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/lib/eva/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: eva
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- DAddYE
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-12 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: eventmachine
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.0.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.0.0
|
30
|
+
description: Effortless event driven micro framework with a tasty syntax
|
31
|
+
email:
|
32
|
+
- info@daddye.it
|
33
|
+
executables:
|
34
|
+
- eva
|
35
|
+
extensions: []
|
36
|
+
extra_rdoc_files: []
|
37
|
+
files:
|
38
|
+
- .gitignore
|
39
|
+
- Gemfile
|
40
|
+
- LICENSE.txt
|
41
|
+
- README.md
|
42
|
+
- Rakefile
|
43
|
+
- benchmark/area.rb
|
44
|
+
- benchmark/fib.rb
|
45
|
+
- bin/eva
|
46
|
+
- eva.gemspec
|
47
|
+
- example/basic.rb
|
48
|
+
- example/delegate.rb
|
49
|
+
- example/emitter.rb
|
50
|
+
- example/timers.rb
|
51
|
+
- lib/eva.rb
|
52
|
+
- lib/eva/emitter.rb
|
53
|
+
- lib/eva/ext/object.rb
|
54
|
+
- lib/eva/space.rb
|
55
|
+
- lib/eva/version.rb
|
56
|
+
homepage: https://github.com/DAddYE/eva
|
57
|
+
licenses: []
|
58
|
+
post_install_message:
|
59
|
+
rdoc_options: []
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ! '>='
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ! '>='
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubyforge_project:
|
76
|
+
rubygems_version: 1.8.23
|
77
|
+
signing_key:
|
78
|
+
specification_version: 3
|
79
|
+
summary: Effortless event driven micro framework with a tasty syntax
|
80
|
+
test_files: []
|
81
|
+
has_rdoc:
|