nyoibo 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +6 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +11 -0
- data/app/helpers/nyoibo_helper.rb +15 -0
- data/lib/generators/nyoibo/install_generator.rb +20 -0
- data/lib/generators/nyoibo/templates/nyoibo.js.coffee +65 -0
- data/lib/generators/nyoibo/templates/nyoibo.rb.erb +4 -0
- data/lib/generators/nyoibo/templates/nyoibo_en.yml +3 -0
- data/lib/nyoibo.rb +17 -0
- data/lib/nyoibo/callback.rb +28 -0
- data/lib/nyoibo/configure.rb +19 -0
- data/lib/nyoibo/daemon.rb +58 -0
- data/lib/nyoibo/rails.rb +4 -0
- data/lib/nyoibo/railtie.rb +8 -0
- data/lib/nyoibo/version.rb +3 -0
- data/nyoibo.gemspec +26 -0
- data/test/fixtures/test.jpg +0 -0
- data/test/test_helper.rb +19 -0
- data/test/unit/callback_test.rb +17 -0
- data/test/unit/configure_test.rb +15 -0
- data/test/unit/daemon_test.rb +60 -0
- data/vendor/html5jp/progress.js +929 -0
- data/vendor/web-socket-js/README.md +157 -0
- data/vendor/web-socket-js/WebSocketMain.swf +0 -0
- data/vendor/web-socket-js/swfobject.js +4 -0
- data/vendor/web-socket-js/web_socket.js +361 -0
- metadata +124 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
gem "shoulda-matchers", :git => "https://github.com/thoughtbot/shoulda-matchers.git"
|
4
|
+
gem "shoulda-context", :git => "https://github.com/thoughtbot/shoulda-context.git"
|
5
|
+
gem "turn", :git => "https://github.com/yalab/turn.git"
|
6
|
+
gemspec
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2011 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
require 'rake/testtask'
|
4
|
+
desc "Default Task"
|
5
|
+
Rake::TestTask.new do |t|
|
6
|
+
files = FileList['test/test_helper.rb', 'test/**/*test.rb']
|
7
|
+
t.test_files = files
|
8
|
+
t.libs << "test"
|
9
|
+
# t.warning = true
|
10
|
+
end
|
11
|
+
task :default => :test
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module NyoiboHelper
|
2
|
+
def ws_form_for(model, options={}, &block)
|
3
|
+
options[:html] ||= {}
|
4
|
+
options[:html][:id] ||= 'ws-form'
|
5
|
+
output = ''
|
6
|
+
output << javascript_tag(<<EOS)
|
7
|
+
WEB_SOCKET_DEBUG = true;
|
8
|
+
WEB_SOCKET_SWF_LOCATION = '/WebSocketMain.swf';
|
9
|
+
var nyoibo = #{I18n.t('nyoibo').to_json};
|
10
|
+
EOS
|
11
|
+
output << form_for(model, options, &block)
|
12
|
+
output << content_tag(:div, '', :id => 'ws-progress')
|
13
|
+
output.html_safe
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rails'
|
2
|
+
require 'rails/generators'
|
3
|
+
module Nyoibo
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path("../templates", __FILE__)
|
7
|
+
|
8
|
+
desc "Creates a Nyoibo initializer and copy javascript and locale files to your application."
|
9
|
+
def copy_initializer
|
10
|
+
template "nyoibo.rb.erb", "config/initializers/nyoibo.rb"
|
11
|
+
template "nyoibo_en.yml", "config/locales/nyoibo_en.yml"
|
12
|
+
template "nyoibo.js.coffee", "app/assets/javascripts/nyoibo.js.coffee"
|
13
|
+
template "../../../../vendor/html5jp/progress.js", "app/assets/javascripts/progress.js"
|
14
|
+
template "../../../../vendor/web-socket-js/web_socket.js", "app/assets/javascripts/web_socket.js"
|
15
|
+
template "../../../../vendor/web-socket-js/swfobject.js", "app/assets/javascripts/swfobject.js"
|
16
|
+
template "../../../../vendor/web-socket-js/WebSocketMain.swf", "public/WebSocketMain.swf"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
jQuery ($)->
|
2
|
+
progress = new html5jp.progress("ws-progress")
|
3
|
+
progress.draw()
|
4
|
+
|
5
|
+
form = $('#ws-form')
|
6
|
+
progress_bar = $("#ws-progress")
|
7
|
+
|
8
|
+
fire = (obj, name, data) ->
|
9
|
+
event = $.Event(name)
|
10
|
+
obj.trigger(event, data)
|
11
|
+
return event.result != false
|
12
|
+
|
13
|
+
form.bind 'ws:upload', (event) ->
|
14
|
+
file = form.find('input[type=file]').get(0).files[0]
|
15
|
+
try
|
16
|
+
max_length = file.size
|
17
|
+
catch e
|
18
|
+
alert nyoibo.file_not_found
|
19
|
+
return
|
20
|
+
if max_length == 0
|
21
|
+
alert nyoibo.file_not_found
|
22
|
+
return
|
23
|
+
chunk = 102400
|
24
|
+
start = 0
|
25
|
+
ws = new WebSocket("ws://localhost:3030/")
|
26
|
+
|
27
|
+
fire(form, 'ws:before_upload')
|
28
|
+
ws.onclose = ->
|
29
|
+
progress.set_val(100)
|
30
|
+
fire(form, 'ws:after_upload')
|
31
|
+
progress.reset()
|
32
|
+
ws = null
|
33
|
+
|
34
|
+
ws.onmessage = (evt) ->
|
35
|
+
switch evt.data
|
36
|
+
when 'OK Ready'
|
37
|
+
params = {filename: file.name, comment: $('#post_comment').val(), size: max_length, session_string: $('#session_string').val()}
|
38
|
+
ws.send("JSON: " + JSON.stringify(params))
|
39
|
+
when 'OK Bye'
|
40
|
+
ws.close()
|
41
|
+
when 'EMPTY'
|
42
|
+
ws.send("QUIT")
|
43
|
+
when 'NEXT'
|
44
|
+
val = Math.floor(start / max_length * 100)
|
45
|
+
progress.set_val(val)
|
46
|
+
stop = start + chunk - 1
|
47
|
+
if stop >= max_length
|
48
|
+
stop = max_length
|
49
|
+
|
50
|
+
blob = if typeof(file.mozSlice) == "function"
|
51
|
+
file.mozSlice(start, stop)
|
52
|
+
else if typeof(file.webkitSlice) == "function"
|
53
|
+
file.webkitSlice(start, stop)
|
54
|
+
start = stop
|
55
|
+
reader = new FileReader()
|
56
|
+
reader.onloadend = (e) ->
|
57
|
+
ws.send(e.target.result)
|
58
|
+
|
59
|
+
setTimeout ->
|
60
|
+
reader.readAsBinaryString(blob)
|
61
|
+
, 300
|
62
|
+
|
63
|
+
$('#ws-form input[type=submit]').click ->
|
64
|
+
fire(form, 'ws:upload') if fire(form, 'ws:prepare_upload')
|
65
|
+
return false
|
data/lib/nyoibo.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'em-websocket'
|
2
|
+
require 'json'
|
3
|
+
require 'nyoibo/configure'
|
4
|
+
require 'nyoibo/daemon'
|
5
|
+
require 'nyoibo/callback'
|
6
|
+
|
7
|
+
module Nyoibo
|
8
|
+
extend Configure
|
9
|
+
extend Daemon
|
10
|
+
extend Callback::Runner
|
11
|
+
end
|
12
|
+
|
13
|
+
if defined?(Rails)
|
14
|
+
require 'rails'
|
15
|
+
require 'nyoibo/rails'
|
16
|
+
require 'nyoibo/railtie'
|
17
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Nyoibo
|
2
|
+
module Callback
|
3
|
+
def self.callbacks
|
4
|
+
@callbacks ||= {}
|
5
|
+
end
|
6
|
+
def self.included(base)
|
7
|
+
base.extend ClassMethod
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethod
|
11
|
+
def uploaded(path, &block)
|
12
|
+
if ENV["NYOIBO_ENV"] == "production" && Nyoibo::Callback.callbacks[path]
|
13
|
+
raise "Already defined '#{path}' updated callback."
|
14
|
+
end
|
15
|
+
Nyoibo::Callback.callbacks[path] = block
|
16
|
+
|
17
|
+
Process.kill(:TERM, Nyoibo.pid) && Nyoibo.run if ENV["NYOIBO_ENV"] == "development"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Runner
|
22
|
+
def run_callback(path="/", *args)
|
23
|
+
block = Nyoibo::Callback.callbacks[path]
|
24
|
+
block.call(*args)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Nyoibo
|
2
|
+
module Configure
|
3
|
+
attr_accessor :config
|
4
|
+
def configure(&block)
|
5
|
+
@config = Config.new
|
6
|
+
@config.instance_exec(&block)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Config
|
11
|
+
[:host, :port].each do |method|
|
12
|
+
module_eval <<-EOS, __FILE__, __LINE__
|
13
|
+
def #{method}(value = nil)
|
14
|
+
(value) ? (@#{method} = value) : @#{method}
|
15
|
+
end
|
16
|
+
EOS
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Nyoibo
|
2
|
+
module Daemon
|
3
|
+
attr_reader :pid
|
4
|
+
CMD_QUIT = /^QUIT$/
|
5
|
+
CMD_JSON = /^JSON: /
|
6
|
+
TYPE_BASE64 = %r|^data:application/octet-stream;base64,|
|
7
|
+
def run
|
8
|
+
return if defined?(IRB)
|
9
|
+
daemon = lambda{
|
10
|
+
EventMachine::WebSocket.start(:host => config.host, :port => config.port) do |ws|
|
11
|
+
ws.onopen{
|
12
|
+
ws.send "OK Ready"
|
13
|
+
}
|
14
|
+
ws.onclose{
|
15
|
+
ws.send "OK Bye"
|
16
|
+
}
|
17
|
+
ws.onmessage{|msg|
|
18
|
+
@binary ||= ""
|
19
|
+
@binary_type ||= 'binary'
|
20
|
+
case msg
|
21
|
+
when CMD_QUIT
|
22
|
+
if @binary_type == 'base64'
|
23
|
+
@binary = Base64.decode64(@binary)
|
24
|
+
elsif @binary.encoding == Encoding::UTF_8
|
25
|
+
@binary = @binary.unpack('U*').pack('c*')
|
26
|
+
end
|
27
|
+
Nyoibo.run_callback(ws.request["path"], @json, @binary)
|
28
|
+
ws.close_connection
|
29
|
+
when CMD_JSON
|
30
|
+
msg.gsub!(CMD_JSON, '')
|
31
|
+
@json = JSON.parse(msg)
|
32
|
+
@json['size'] = @json['size'].to_i
|
33
|
+
ws.send("NEXT")
|
34
|
+
else
|
35
|
+
|
36
|
+
if msg =~ TYPE_BASE64
|
37
|
+
msg.gsub!(TYPE_BASE64, '')
|
38
|
+
@binary_type = 'base64'
|
39
|
+
end
|
40
|
+
|
41
|
+
if msg.length > 0
|
42
|
+
@binary << msg
|
43
|
+
ws.send("NEXT")
|
44
|
+
else
|
45
|
+
ws.send("EMPTY")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
}
|
49
|
+
end
|
50
|
+
}
|
51
|
+
if ENV["NYOIBO_ENV"] == "test"
|
52
|
+
daemon.call
|
53
|
+
else
|
54
|
+
@pid = Process.fork &daemon
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/nyoibo/rails.rb
ADDED
data/nyoibo.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "nyoibo/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "nyoibo"
|
7
|
+
s.version = Nyoibo::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["yalab"]
|
10
|
+
s.email = ["rudeboyjet@gmail.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Websocket uploader with progressbar.}
|
13
|
+
s.description = %q{}
|
14
|
+
s.license = 'MIT'
|
15
|
+
|
16
|
+
s.rubyforge_project = "nyoibo"
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
s.add_dependency("em-websocket", "~>0.3.0")
|
23
|
+
s.add_dependency("json", "~>1.5.3")
|
24
|
+
s.add_development_dependency('em-http-request', '~> 0.2.6')
|
25
|
+
s.add_development_dependency('railties')
|
26
|
+
end
|
Binary file
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'socket'
|
3
|
+
require 'bundler'
|
4
|
+
ENV['BUNDLE_GEMFILE'] = File.expand_path("../../Gemfile", __FILE__)
|
5
|
+
require 'bundler/setup'
|
6
|
+
require 'shoulda/context'
|
7
|
+
require 'em-http'
|
8
|
+
require 'turn'
|
9
|
+
|
10
|
+
Dir.glob(File.expand_path("../../lib/**/*.rb", __FILE__)).each {|f| require f }
|
11
|
+
|
12
|
+
ENV["NYOIBO_ENV"] = "test"
|
13
|
+
|
14
|
+
Test::Unit::TestCase.module_eval do
|
15
|
+
def failed
|
16
|
+
EventMachine.stop
|
17
|
+
fail
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
class Nyoibo::CallbackTest < Test::Unit::TestCase
|
3
|
+
class TestApp
|
4
|
+
include Nyoibo::Callback
|
5
|
+
uploaded "/" do
|
6
|
+
"uploaded!"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
context "Run callback" do
|
10
|
+
setup do
|
11
|
+
@response = Nyoibo.run_callback("/")
|
12
|
+
end
|
13
|
+
should "fetch response" do
|
14
|
+
assert_equal "uploaded!", @response
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
class Nyoibo::ConfigureTest < Test::Unit::TestCase
|
3
|
+
context ".configure" do
|
4
|
+
setup do
|
5
|
+
Nyoibo.configure do
|
6
|
+
host 'localhost'
|
7
|
+
port 3030
|
8
|
+
end
|
9
|
+
end
|
10
|
+
should "reflect config" do
|
11
|
+
assert_equal 'localhost', Nyoibo.config.host
|
12
|
+
assert_equal 3030, Nyoibo.config.port
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
class Nyoibo::DaemonTest < Test::Unit::TestCase
|
3
|
+
class TestApp < Test::Unit::TestCase
|
4
|
+
include Nyoibo::Callback
|
5
|
+
uploaded "/" do |json, binary|
|
6
|
+
File.open("/tmp/test.jpg", "w:binary"){|f|
|
7
|
+
f.write(binary)
|
8
|
+
}
|
9
|
+
raise "finame is not 'test.jpg'" if json["filename"] != "test.jpg"
|
10
|
+
raise "Binary size is not '3137' byte" if binary.length != 3137
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context "run" do
|
15
|
+
setup do
|
16
|
+
@file = File.new(File.expand_path("../../fixtures/test.jpg", __FILE__), "rb")
|
17
|
+
Nyoibo.configure do
|
18
|
+
host "localhost"
|
19
|
+
port 3030
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
should "listen port" do
|
24
|
+
EM.run do
|
25
|
+
EventMachine.add_timer(0.1) do
|
26
|
+
http = EventMachine::HttpRequest.new('ws://localhost:3030/').get(:timeout => 0)
|
27
|
+
http.errback { failed }
|
28
|
+
@start = 0
|
29
|
+
@sendsize = 1024
|
30
|
+
http.stream{|msg|
|
31
|
+
case msg
|
32
|
+
when "OK Ready"
|
33
|
+
http.send("JSON: " + {:filename => File.basename(@file.path), :size => @file.size}.to_json)
|
34
|
+
when "NEXT"
|
35
|
+
@end = @start + @sendsize
|
36
|
+
@encoded ||= Base64.encode64(@file.read)
|
37
|
+
|
38
|
+
@end = @encoded.size - 1 if @end >= @encoded.size
|
39
|
+
if @start == @encoded.size
|
40
|
+
http.send("QUIT")
|
41
|
+
else
|
42
|
+
chunk = @encoded.slice(@start..@end)
|
43
|
+
if @start == 0
|
44
|
+
chunk = "data:application/octet-stream;base64," + chunk
|
45
|
+
end
|
46
|
+
|
47
|
+
http.send(chunk)
|
48
|
+
end
|
49
|
+
@start = @end + 1
|
50
|
+
end
|
51
|
+
}
|
52
|
+
http.disconnect{
|
53
|
+
EventMachine.stop
|
54
|
+
}
|
55
|
+
end
|
56
|
+
Nyoibo.run
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|