Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/handlino/FireApp
Browse files Browse the repository at this point in the history
Conflicts:
	src/compile_version.rb
  • Loading branch information
tka committed Dec 16, 2014
2 parents bb89569 + 6eda046 commit a1d52f3
Show file tree
Hide file tree
Showing 10 changed files with 275 additions and 11 deletions.
53 changes: 53 additions & 0 deletions RemoteControl-Readme.md
@@ -0,0 +1,53 @@

# Fire.app Remote Control (Beta)

Fire.app 在啟動後,會自動 listen 13425 port。

我們可以用 `nc localhost 13425` 來連上控制界面。並且鍵入 help 來查看支援的指令。

![starting-client](RemoteControl-Readme/starting-client.png)

而在 Fire.app 那邊則可以看到:

![starting-fireapp](RemoteControl-Readme/starting-fireapp.png)

以下簡單說明目前有支援的指令


## Watch Project

- 使用 `watch path [project_path]` 控制 Fire.app watch 此 project。

*注意: project_path 指的是與 Fire.app 同一臺電腦下的 path,而目前尚無法讓 Fire.app watch 其他電腦上的 project。 若是 client 與 Fire.app 不使用同一個 host 時請小心。*

- 使用 `watch lastest` 直接 watch 最近一個使用過的 project 。
- 使用 `watch status` 可確認目前是否有 watch project 。若是有的話,一併回傳目前的 project path。

## Stop Watching

- 使用 `watch stop` 終止目前的工作


## Others

- 使用 `extension list` 列出 Fire.app 有支援的 extension 清單
- 使用 `quit` 關閉 Fire.app
- 使用 `echo [msg]` 讓 Fire.app 回傳 msg (測試功能) 。

---

## Motivation

需要有 remote interface 的原因有幾個,其中最主要的想法是希望可以結合 rspec ,方便做 unit test 。另外一點是, Fire.app 也算是一個吃記憶體的 app ,如果許多電腦可以共用某一檯電腦內的 Fire.app 來 compiler 自己的檔案,感覺也不錯。
*註:要達成 unit test 我目前還有另一個想法是使用 applescript 來控制 interface ,有機會可以研究。*


## TODO

1. 讓 remote interface 能回傳一致的資料格式
2. Fire.app 有很多動作必須選擇資料夾/檔案,如 create project / watch project 。之前寫 rspec 有解決這個問題,需要把它整合進來
3. 需要有 nc 的 ruby implement version ,除了不是所有作業系統都有 nc 指令這個原因之外,我們也需要讓 rspec 可以控制 nc
4. 像是 Preference / Change Option 等功能,會開啟一個新視窗。新開的那個視窗的控制方法必須要研究一下。

*本功能的主要模組在 src/remote_control_server.rb*

Binary file added RemoteControl-Readme/starting-client.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added RemoteControl-Readme/starting-fireapp.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion lib/ruby/common/serve-patched/lib/serve/router.rb
Expand Up @@ -14,7 +14,7 @@ def self.resolve(root, path)
path
when File.directory?(full_path)
# It's a directory? Try a directory index.
resolve(root, File.join(path, 'index'))
resolve(root, File.join(path, 'index.')) || resolve(root, File.join(path, 'index'))
else
# Still no luck? Check to see if a file with an extension exists by that name.
# TODO: Return a path with an extension based on priority, not just the first found.
Expand Down
4 changes: 3 additions & 1 deletion src/compiler/base_compiler.rb
Expand Up @@ -87,7 +87,9 @@ def self.get_dst_file_path(src_dir, src_file_path, dst_dir)
src_file_path = File.expand_path(src_file_path)

new_dir = File.dirname(src_file_path.to_s.sub(src_dir, ""))
new_file = File.basename(src_file_path).gsub(/\.#{self.src_file_ext}/,".#{self.dst_file_ext}").gsub(/\.#{self.dst_file_ext}\.#{self.dst_file_ext}/,"#{self.dst_file_ext}")
new_file = File.basename(src_file_path)
.gsub(/\.#{self.src_file_ext}$/,".#{self.dst_file_ext}")
.gsub(/\.#{self.dst_file_ext}\.#{self.dst_file_ext}$/,".#{self.dst_file_ext}")

return File.join(dst_dir, new_dir, new_file)
end
Expand Down
6 changes: 4 additions & 2 deletions src/main.rb
Expand Up @@ -39,8 +39,7 @@
%w{alert notification quit_window tray preference_panel report welcome_window change_options_panel progress_window}.each do | f |
require "ui/#{f}"
end


require 'remote_control_server'



Expand Down Expand Up @@ -123,6 +122,9 @@
WelcomeWindow.new
end

if App::CONFIG["services"].include? :remote_control
RemoteControlServer.instance.open
end

Tray.instance.run(:watch => options[:watch_dir])

Expand Down
174 changes: 174 additions & 0 deletions src/remote_control_server.rb
@@ -0,0 +1,174 @@

require 'socket'
require "singleton"



Thread.abort_on_exception = true

class RemoteControlServer
include Singleton

def initialize
@server = nil
@connected_pool = []
end

def open(port = 13425)
close
@server = Thread.new do
TCPServer.open(13425) do |sock_server|

loop {
begin
@connected_pool << Thread.new(sock_server.accept) do |sock|
client_port, client_ip = Socket.unpack_sockaddr_in(sock.getpeername)
sock.puts "# Fireapp Remote Interface #"

puts "Client Connected from #{client_ip}:#{client_port}"

loop do
input = sock.gets
break if input.nil?

input = input.strip
puts "#{client_ip}:#{client_port} => #{input}"

output = case input
when /^watch path \s*(.*)\s*/i
App.get_stdout {
App.display.syncExec {
Tray.instance.watch $1 # if exception occurred, it'll stop at here
puts "Watching Success: #{$1}"
}
}


when /^watch lastest$/i
App.get_stdout {
App.display.syncExec {
if App.get_history[0]
Tray.instance.watch App.get_history[0]
puts "Watching Success: #{App.get_history[0]}"
end
}
}

when /^extension list$/i
App.display.syncExec {
output = JSON.pretty_generate fetch_menu_tree(Tray.instance.create_item)
}
output

when /^create project$/i
App.display.syncExec {
click(["compass", "project"], Tray.instance.create_item)
}

when /^watch stop$/i
App.display.syncExec {
Tray.instance.stop_watch
}

when /^watch status$/i
"Watching: #{Tray.instance.watching_dir || "Nothing"}"




when /^echo (.*)/i
$1

when /^quit$/i
App.display.syncExec {
Tray.instance.exit_handler.trigger
}

when /^help$/i
help()
else
"Command '#{input.strip}' is not found.\n#{help()}"
end

sock.puts output
puts "#{client_ip}:#{client_port} <= #{output}"

end
end

rescue Exception => e
puts "#{e.message}"
end
}
end
end

end

def close(disconnect_all = true)
@server.kill if open?
@server = nil
if disconnect_all
@connected_pool.each do |x|
x.kill if x and x.alive?
end
@connected_pool = []
end
end

def open?
return true if @server and @server.alive?
return false
end

def help
[
"** We support following commands:",
"- watch path [path]",
"- watch stop",
"- watch status",
"- watch lastest",
"- extension list",
"- quit",
"- echo [msg]",
"- help",
""
].join("\n")
end


def fetch_menu_tree (menuitem)

if menuitem.menu
tree = Hash.new
menuitem.menu.getItems.each do |item|
tree[item.text] = fetch_menu_tree(item)
end
tree
else
""
end

end

def click (steps = [], menuitem = nil)
menuitem = menuitem || Tray.instance.tray_item
step = steps[0]
if step and menuitem.menu
next_menuitem = menuitem.menu.getItems.find {|f| f.text.strip =~ Regexp.new(step) }
if next_menuitem and steps.size == 1
menuitem.getListeners(Swt::SWT::Selection).each { |l| l.trigger }
elsif next_menuitem
return click(steps[1..-1], next_menuitem)
end
else
return false
end

end

end



#server_thread.join
5 changes: 5 additions & 0 deletions src/simplehttpserver.rb
Expand Up @@ -39,10 +39,14 @@ def start(dir, options)
use Rack::ShowStatus
use Rack::ShowExceptions



if File.exists?( File.join(Compass.configuration.project_path, 'http_servlet_handler.rb'))
eval(File.read( File.join(Compass.configuration.project_path, 'http_servlet_handler.rb')))
end



views_dir = File.join(dir, 'views')
public_dir = File.join(dir, 'public')

Expand All @@ -58,6 +62,7 @@ def start(dir, options)
Rack::Directory.new( dir ),
Serve::RackAdapter.new( dir, true )
])

end
end

Expand Down
24 changes: 22 additions & 2 deletions src/ui/preference_panel.rb
Expand Up @@ -161,9 +161,18 @@ def services_composite
Swt::Program.launch(evt.text)
end)

layoutdata = Swt::Layout::FormData.new(150, Swt::SWT::DEFAULT)
layoutdata = Swt::Layout::FormData.new()
layoutdata.left = Swt::Layout::FormAttachment.new( livereload_service_help_info, 0, Swt::SWT::LEFT )
layoutdata.top = Swt::Layout::FormAttachment.new( livereload_service_help_info, 10, Swt::SWT::BOTTOM)
@service_remote_control_button = Swt::Widgets::Button.new(composite, Swt::SWT::CHECK )
@service_remote_control_button.setText( 'Enable Remote Control' )
@service_remote_control_button.setSelection( App::CONFIG["services"].include? :remote_control )
@service_remote_control_button.addListener(Swt::SWT::Selection, services_button_handler)
@service_remote_control_button.setLayoutData(layoutdata)

layoutdata = Swt::Layout::FormData.new(150, Swt::SWT::DEFAULT)
layoutdata.left = Swt::Layout::FormAttachment.new( @service_http_button, 0, Swt::SWT::LEFT )
layoutdata.top = Swt::Layout::FormAttachment.new( @service_remote_control_button, 10, Swt::SWT::BOTTOM)
@services_apply_button = Swt::Widgets::Button.new( composite, Swt::SWT::PUSH )
@services_apply_button.setLayoutData(layoutdata)
@services_apply_button.setText("Apply Change")
Expand All @@ -173,11 +182,22 @@ def services_composite
end

def services_button_handler
Swt::Widgets::Listener.impl do |method, evt|
Swt::Widgets::Listener.impl do |method, evt|
App::CONFIG["services"] = []
App::CONFIG["services"] << :http if @service_http_button.getSelection
App::CONFIG["services"] << :livereload if @service_livereload_button.getSelection
App::CONFIG["services"] << :remote_control if @service_remote_control_button.getSelection
App.save_config

case [App::CONFIG["services"].include?(:remote_control), RemoteControlServer.instance.open?]
when [true, false]
RemoteControlServer.instance.open
when [false, true]
RemoteControlServer.instance.close
end
# puts [App::CONFIG["services"].include?(:remote_control), RemoteControlServer.instance.open?].to_s


Tray.instance.rewatch
end
end
Expand Down

0 comments on commit a1d52f3

Please sign in to comment.