bowline 0.4.6 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -0
- data/TODO +11 -0
- data/VERSION +1 -1
- data/assets/bowline.js +194 -0
- data/assets/json2.js +479 -0
- data/assets/osx/Info.plist.erb +45 -0
- data/assets/osx/bowline.png +0 -0
- data/assets/osx/makeicns +0 -0
- data/bowline.gemspec +47 -11
- data/lib/bowline.rb +35 -15
- data/lib/bowline/binders.rb +168 -131
- data/lib/bowline/commands/build.rb +3 -0
- data/lib/bowline/commands/generate.rb +3 -1
- data/lib/bowline/commands/run.rb +7 -4
- data/lib/bowline/desktop.rb +26 -0
- data/lib/bowline/desktop/app.rb +9 -0
- data/lib/bowline/desktop/bridge.rb +97 -0
- data/lib/bowline/desktop/clipboard.rb +9 -0
- data/lib/bowline/desktop/dialog.rb +28 -0
- data/lib/bowline/desktop/dock.rb +9 -0
- data/lib/bowline/desktop/host.rb +10 -0
- data/lib/bowline/desktop/js.rb +92 -0
- data/lib/bowline/desktop/misc.rb +9 -0
- data/lib/bowline/desktop/network.rb +7 -0
- data/lib/bowline/desktop/proxy.rb +94 -0
- data/lib/bowline/desktop/sound.rb +8 -0
- data/lib/bowline/desktop/window.rb +31 -0
- data/lib/bowline/desktop/window_manager.rb +72 -0
- data/lib/bowline/desktop/window_methods.rb +70 -0
- data/lib/bowline/generators.rb +3 -2
- data/lib/bowline/generators/application.rb +8 -5
- data/lib/bowline/generators/binder.rb +10 -6
- data/lib/bowline/generators/model.rb +9 -0
- data/lib/bowline/generators/window.rb +28 -0
- data/lib/bowline/helpers.rb +0 -3
- data/lib/bowline/initializer.rb +65 -11
- data/lib/bowline/library.rb +31 -0
- data/lib/bowline/local_model.rb +116 -0
- data/lib/bowline/logging.rb +23 -0
- data/lib/bowline/platform.rb +73 -0
- data/lib/bowline/tasks/app.rake +80 -148
- data/lib/bowline/tasks/libs.rake +59 -0
- data/lib/bowline/version.rb +2 -2
- data/lib/bowline/watcher.rb +91 -0
- data/templates/binder.rb +2 -8
- data/templates/config/environment.rb +3 -2
- data/templates/main_window.rb +7 -0
- data/templates/model.rb +1 -1
- data/templates/public/index.html +2 -1
- data/templates/script/build +3 -0
- data/templates/script/generate +3 -0
- data/templates/script/init +0 -16
- data/templates/window.rb +3 -0
- data/vendor/pathname.rb +1099 -0
- data/vendor/progressbar.rb +236 -0
- metadata +48 -9
- data/assets/jquery.bowline.js +0 -156
- data/lib/bowline/async.rb +0 -29
- data/lib/bowline/binders/collection.rb +0 -59
- data/lib/bowline/binders/singleton.rb +0 -31
- data/lib/bowline/jquery.rb +0 -31
- data/lib/bowline/observer.rb +0 -66
- data/lib/bowline/window.rb +0 -19
data/lib/bowline/commands/run.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
|
2
|
-
require '
|
3
|
-
|
4
|
-
Rake::Task['
|
1
|
+
unless Bowline::Library.downloaded?
|
2
|
+
require 'rake'
|
3
|
+
require 'bowline/tasks/bowline'
|
4
|
+
Rake::Task['libs:download'].invoke
|
5
|
+
end
|
6
|
+
|
7
|
+
exec("#{Bowline::Library.desktop_path} #{APP_ROOT}")
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Bowline
|
2
|
+
module Desktop
|
3
|
+
include Bowline::Logging
|
4
|
+
extend Bowline::Watcher::Base
|
5
|
+
watch :on_tick, :on_idle
|
6
|
+
|
7
|
+
def enabled?
|
8
|
+
$0 == "bowline"
|
9
|
+
end
|
10
|
+
module_function :enabled?
|
11
|
+
|
12
|
+
def idle
|
13
|
+
watcher.call(:on_idle)
|
14
|
+
rescue => e
|
15
|
+
log_error e
|
16
|
+
end
|
17
|
+
module_function :idle
|
18
|
+
|
19
|
+
def tick
|
20
|
+
watcher.call(:on_tick)
|
21
|
+
rescue => e
|
22
|
+
log_error e
|
23
|
+
end
|
24
|
+
module_function :tick
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Bowline
|
2
|
+
module Desktop
|
3
|
+
module Bridge
|
4
|
+
module ClassMethods
|
5
|
+
# Extend you own classes with this
|
6
|
+
# module, and then call js_expose
|
7
|
+
# to expose your classes' class
|
8
|
+
# variables to JavaScript.
|
9
|
+
#
|
10
|
+
# Ruby Class:
|
11
|
+
# class FooClass
|
12
|
+
# extend Bowline::Desktop::Bridge::ClassMethods
|
13
|
+
# js_expose
|
14
|
+
#
|
15
|
+
# def self.foo
|
16
|
+
# return :bar
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# JavaScript:
|
21
|
+
# Bowline.invoke("FooClass", "foo", function(res){
|
22
|
+
# console.log(res);
|
23
|
+
# })
|
24
|
+
def js_expose(opts = {})
|
25
|
+
# TODO - implement options,
|
26
|
+
# like :except and :only
|
27
|
+
instance_eval <<-RUBY
|
28
|
+
def js_exposed?
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
def js_invoke(window, method, *args)
|
33
|
+
send(method, *args)
|
34
|
+
end
|
35
|
+
RUBY
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Message
|
40
|
+
include Bowline::Logging
|
41
|
+
|
42
|
+
def self.from_array(window, arr)
|
43
|
+
arr.map {|i| self.new(window, i) }
|
44
|
+
end
|
45
|
+
|
46
|
+
attr_reader :window, :id, :klass, :method, :args
|
47
|
+
|
48
|
+
def initialize(window, atts)
|
49
|
+
@window = window
|
50
|
+
atts = atts.with_indifferent_access
|
51
|
+
@id = atts[:id]
|
52
|
+
@klass = atts[:klass]
|
53
|
+
@method = atts[:method].to_sym
|
54
|
+
@args = atts[:args] || []
|
55
|
+
end
|
56
|
+
|
57
|
+
def invoke
|
58
|
+
if klass == "_window"
|
59
|
+
object = window
|
60
|
+
else
|
61
|
+
# TODO - security concerns with constantize
|
62
|
+
object = klass.constantize
|
63
|
+
end
|
64
|
+
trace "JS invoking: #{klass}.#{method}(#{args.join(',')})"
|
65
|
+
if object.respond_to?(:js_exposed?) && object.js_exposed?
|
66
|
+
result = object.js_invoke(window, method, *args)
|
67
|
+
proxy = Proxy.new(window)
|
68
|
+
proxy.Bowline.invokeCallback(id, result)
|
69
|
+
window.run_script(proxy.to_s)
|
70
|
+
else
|
71
|
+
raise "Method not allowed"
|
72
|
+
end
|
73
|
+
rescue => e
|
74
|
+
log_error e
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def setup
|
79
|
+
Desktop.on_tick(method(:poll))
|
80
|
+
end
|
81
|
+
module_function :setup
|
82
|
+
|
83
|
+
def poll
|
84
|
+
WindowManager.allocated_windows.each do |window|
|
85
|
+
result = window.run_script("try {Bowline.pollJS()} catch(e) {false}")
|
86
|
+
next if result.blank? || result == "false"
|
87
|
+
result = JSON.parse(result)
|
88
|
+
messages = Message.from_array(window, result)
|
89
|
+
messages.each(&:invoke)
|
90
|
+
end
|
91
|
+
rescue => e
|
92
|
+
Bowline::Logging.log_error(e)
|
93
|
+
end
|
94
|
+
module_function :poll
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Bowline
|
2
|
+
module Desktop
|
3
|
+
module Dialog
|
4
|
+
def message(msg, options = {})
|
5
|
+
style = 0
|
6
|
+
style |= YES_NO if options[:yes_no]
|
7
|
+
style |= OK if options[:ok]
|
8
|
+
style |= CANCEL if options[:cancel]
|
9
|
+
style |= ICON_EXCLAMATION if options[:icon_exclamation]
|
10
|
+
style |= ICON_HAND if options[:icon_hand]
|
11
|
+
style |= ICON_ERROR if options[:icon_error]
|
12
|
+
style |= QUESTION if options[:question]
|
13
|
+
style |= INFORMATION if options[:information]
|
14
|
+
caption = options[:caption] || "Message"
|
15
|
+
|
16
|
+
result = _message(msg, caption, style)
|
17
|
+
|
18
|
+
case result
|
19
|
+
when YES then :yes
|
20
|
+
when NO then :no
|
21
|
+
when OK then :ok
|
22
|
+
when CANCEL then :cancel
|
23
|
+
end
|
24
|
+
end
|
25
|
+
module_function :message
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Bowline
|
2
|
+
module Desktop
|
3
|
+
module JS
|
4
|
+
class Script
|
5
|
+
include Bowline::Logging
|
6
|
+
|
7
|
+
attr_reader :window, :script, :prok
|
8
|
+
def initialize(window, script, prok = nil)
|
9
|
+
@window = window
|
10
|
+
@script = script
|
11
|
+
@prok = prok
|
12
|
+
end
|
13
|
+
alias :windows :window
|
14
|
+
|
15
|
+
def ready?
|
16
|
+
multiple_windows? ?
|
17
|
+
windows.all?(&:loaded?) :
|
18
|
+
window.loaded?
|
19
|
+
end
|
20
|
+
|
21
|
+
def call
|
22
|
+
if Desktop.enabled?
|
23
|
+
trace "JS eval on #{window}: #{script}"
|
24
|
+
if multiple_windows?
|
25
|
+
windows.each {|w| w.run_script(script) }
|
26
|
+
raise "Can't return from multiple windows" if prok
|
27
|
+
else
|
28
|
+
result = parse(window.run_script(script))
|
29
|
+
Thread.new { prok.call(result) } if prok
|
30
|
+
end
|
31
|
+
result
|
32
|
+
else
|
33
|
+
trace "Pseudo JS eval on #{window}: #{script}"
|
34
|
+
prok.call(nil)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def multiple_windows?
|
40
|
+
windows.is_a?(Array)
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse(str)
|
44
|
+
# This is crazy! The JSON
|
45
|
+
# lib can't parse booleans
|
46
|
+
case str
|
47
|
+
when "true" then true
|
48
|
+
when "false" then false
|
49
|
+
when nil then nil
|
50
|
+
else
|
51
|
+
JSON.parse(str)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def poll
|
57
|
+
run_scripts
|
58
|
+
end
|
59
|
+
module_function :poll
|
60
|
+
|
61
|
+
def setup
|
62
|
+
Desktop.on_tick(method(:poll))
|
63
|
+
end
|
64
|
+
module_function :setup
|
65
|
+
|
66
|
+
def eval(win, str, method = nil, &block)
|
67
|
+
script = Script.new(win, str, method||block)
|
68
|
+
if Thread.current == Thread.main && script.ready?
|
69
|
+
script.call
|
70
|
+
else
|
71
|
+
scripts << script
|
72
|
+
end
|
73
|
+
end
|
74
|
+
module_function :eval
|
75
|
+
|
76
|
+
private
|
77
|
+
def run_scripts
|
78
|
+
ready_scripts = scripts.select(&:ready?)
|
79
|
+
while script = ready_scripts.shift
|
80
|
+
script.call
|
81
|
+
end
|
82
|
+
end
|
83
|
+
module_function :run_scripts
|
84
|
+
|
85
|
+
# TODO - thread safety, needs mutex
|
86
|
+
def scripts
|
87
|
+
Thread.main[:scripts] ||= []
|
88
|
+
end
|
89
|
+
module_function :scripts
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Bowline
|
2
|
+
module Desktop
|
3
|
+
class Proxy
|
4
|
+
# Use to call out to JavaScript.
|
5
|
+
#
|
6
|
+
# Use the method 'call' if you want
|
7
|
+
# to call a function, or the method 'res' if you want
|
8
|
+
# the result of a variable evaluation.
|
9
|
+
#
|
10
|
+
# You can pass a block as the last argument which will
|
11
|
+
# be called with the result of the evaluation.
|
12
|
+
#
|
13
|
+
# All arguments are serialized by JSON, so you can only pass
|
14
|
+
# the following objects:
|
15
|
+
# * Hash
|
16
|
+
# * Array
|
17
|
+
# * String
|
18
|
+
# * Integer
|
19
|
+
#
|
20
|
+
# Usage:
|
21
|
+
# proxy.FooObject.messages = [1,2,3] #=> "FooObject.messages = [1,2,3]"
|
22
|
+
# proxy.FooObject.hi.call #=> "FooObject.hi()"
|
23
|
+
# proxy.FooObject.hi(1,2,3).bye.call #=> "FooObject.hi(1,2,3).bye()"
|
24
|
+
# proxy.FooObject.messages.res #=> "FooObject.messages"
|
25
|
+
# proxy.FooObject.messages.res {|result|
|
26
|
+
# puts "Messages are: #{result}"
|
27
|
+
# }
|
28
|
+
#
|
29
|
+
# Reasoning behind this class:
|
30
|
+
# * JavaScript needs to be called all at once
|
31
|
+
# * We don't know if it's a method call, or a variable
|
32
|
+
|
33
|
+
def initialize(win)
|
34
|
+
@window = win
|
35
|
+
@crumbs = []
|
36
|
+
end
|
37
|
+
|
38
|
+
# Call a JavaScript function:
|
39
|
+
# proxy.myFunction('arg1').call
|
40
|
+
def call(method = nil, &block)
|
41
|
+
if @crumbs.empty?
|
42
|
+
raise "No method provided"
|
43
|
+
end
|
44
|
+
string = to_s
|
45
|
+
string << "()" unless string.last == ")"
|
46
|
+
Bowline::Desktop::JS.eval(
|
47
|
+
@window,
|
48
|
+
"Bowline.invokeJS(#{string.inspect});",
|
49
|
+
method,
|
50
|
+
&block
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Evaluate a JavaScript variable:
|
55
|
+
# proxy.my_variable.res {|result| p result }
|
56
|
+
def res(method = nil, &block)
|
57
|
+
if @crumbs.empty?
|
58
|
+
raise "No attribute provided"
|
59
|
+
end
|
60
|
+
Bowline::Desktop::JS.eval(
|
61
|
+
@window,
|
62
|
+
"Bowline.invokeJS(#{to_s.inspect});",
|
63
|
+
method,
|
64
|
+
&block
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
def method_missing(sym, *args) #:nodoc:
|
69
|
+
method_name = sym.to_s
|
70
|
+
@crumbs << [method_name, args]
|
71
|
+
if method_name.last == "="
|
72
|
+
call
|
73
|
+
end
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
# Return the JavaScript that is to be evaluated
|
78
|
+
def to_s
|
79
|
+
(@crumbs || []).inject([]) do |arr, (method, args)|
|
80
|
+
str = method
|
81
|
+
if args.any?
|
82
|
+
str << "(" + args.to_json[1..-2] + ")"
|
83
|
+
end
|
84
|
+
arr << str
|
85
|
+
end.join(".")
|
86
|
+
end
|
87
|
+
alias :to_js :to_s
|
88
|
+
|
89
|
+
def inspect
|
90
|
+
"<#{self.class.name}:#{@window} #{to_s}>"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Bowline
|
2
|
+
module Desktop
|
3
|
+
class Window
|
4
|
+
include WindowMethods
|
5
|
+
# Methods:
|
6
|
+
# new()
|
7
|
+
# center(direction = :both)
|
8
|
+
# close
|
9
|
+
# chrome=
|
10
|
+
# disable
|
11
|
+
# enable
|
12
|
+
# file=
|
13
|
+
# id
|
14
|
+
# modal(flag = true)
|
15
|
+
# name=
|
16
|
+
# run_script(str) #=> str
|
17
|
+
# raise
|
18
|
+
# show
|
19
|
+
# hide
|
20
|
+
# set_size(width, height)
|
21
|
+
# set_position(x, y)
|
22
|
+
# select_dir(
|
23
|
+
# )
|
24
|
+
# shown?
|
25
|
+
end
|
26
|
+
|
27
|
+
class MainWindow
|
28
|
+
include WindowMethods
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|