bowline 0.4.6 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|