flammarion_rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +3 -0
  4. data/Rakefile +26 -0
  5. data/app/assets/config/flammarion_rails_manifest.js +0 -0
  6. data/config/routes.rb +2 -0
  7. data/lib/flammarion/engraving.rb +95 -0
  8. data/lib/flammarion/revelator.rb +88 -0
  9. data/lib/flammarion/server.rb +88 -0
  10. data/lib/flammarion_rails.rb +5 -0
  11. data/lib/flammarion_rails/configuration.rb +25 -0
  12. data/lib/flammarion_rails/engine.rb +28 -0
  13. data/lib/flammarion_rails/version.rb +3 -0
  14. data/lib/rubame/server.rb +172 -0
  15. data/lib/tasks/flammarion_rails_tasks.rake +4 -0
  16. data/public/index.html +23 -0
  17. data/spec/dummy/Rakefile +6 -0
  18. data/spec/dummy/app/assets/config/manifest.js +4 -0
  19. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  20. data/spec/dummy/app/assets/javascripts/cable.js +13 -0
  21. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  22. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  23. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  24. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  25. data/spec/dummy/app/controllers/products_controller.rb +4 -0
  26. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  27. data/spec/dummy/app/jobs/application_job.rb +2 -0
  28. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  29. data/spec/dummy/app/models/application_record.rb +3 -0
  30. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  31. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  32. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  33. data/spec/dummy/app/views/products/index.html.erb +1 -0
  34. data/spec/dummy/bin/bundle +3 -0
  35. data/spec/dummy/bin/flammarion +8 -0
  36. data/spec/dummy/bin/rails +4 -0
  37. data/spec/dummy/bin/rake +4 -0
  38. data/spec/dummy/bin/setup +34 -0
  39. data/spec/dummy/bin/update +29 -0
  40. data/spec/dummy/config.ru +5 -0
  41. data/spec/dummy/config/application.rb +15 -0
  42. data/spec/dummy/config/boot.rb +5 -0
  43. data/spec/dummy/config/cable.yml +9 -0
  44. data/spec/dummy/config/database.yml +25 -0
  45. data/spec/dummy/config/environment.rb +5 -0
  46. data/spec/dummy/config/environments/development.rb +56 -0
  47. data/spec/dummy/config/environments/production.rb +88 -0
  48. data/spec/dummy/config/environments/test.rb +42 -0
  49. data/spec/dummy/config/initializers/application_controller_renderer.rb +6 -0
  50. data/spec/dummy/config/initializers/assets.rb +11 -0
  51. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  52. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  53. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  54. data/spec/dummy/config/initializers/flammarion_rails.rb +3 -0
  55. data/spec/dummy/config/initializers/inflections.rb +16 -0
  56. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  57. data/spec/dummy/config/initializers/new_framework_defaults.rb +24 -0
  58. data/spec/dummy/config/initializers/session_store.rb +3 -0
  59. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  60. data/spec/dummy/config/locales/en.yml +23 -0
  61. data/spec/dummy/config/puma.rb +47 -0
  62. data/spec/dummy/config/routes.rb +3 -0
  63. data/spec/dummy/config/secrets.yml +22 -0
  64. data/spec/dummy/config/spring.rb +6 -0
  65. data/spec/dummy/db/development.sqlite3 +0 -0
  66. data/spec/dummy/db/test.sqlite3 +0 -0
  67. data/spec/dummy/log/development.log +819 -0
  68. data/spec/dummy/log/test.log +16 -0
  69. data/spec/dummy/public/404.html +67 -0
  70. data/spec/dummy/public/422.html +67 -0
  71. data/spec/dummy/public/500.html +66 -0
  72. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  73. data/spec/dummy/public/apple-touch-icon.png +0 -0
  74. data/spec/dummy/public/favicon.ico +0 -0
  75. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/0M/0Mg2krTmzvpvndy5rHnhuun5BBfgA8mY3nz6myP6kdQ.cache +0 -0
  76. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/0P/0P6sKgG4-T4rwdhhAvLJKqmC2XHD18DbbxM7-oLWhXA.cache +1 -0
  77. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/22/22E2TQFHi7XchzTLK5w8ZZ_iYCMIyyGwRmVazx3irqY.cache +0 -0
  78. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/2R/2RUaNi4ZIYrYbhyd2C-Ww2kZHGazuNcAfrKjQoSubl4.cache +0 -0
  79. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/3L/3LxcdISAk1c4X--4-sTB_h--GZF2Epue7I2gjbgvkQ4.cache +1 -0
  80. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/3p/3pa5vZojTdQB44mkfj5buJFWDKREMuCNJoOEP84rcDU.cache +1 -0
  81. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/58/586fjw8FKooCH-968ZTIIrVkE9RuHmBnkYj5OYwcs9g.cache +1 -0
  82. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Bu/BuE1MKiZRiA2ei8bcxwYLjyJdTpEy4wxW852s55oNNk.cache +1 -0
  83. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/JD/JDCUTDoF2rC0R-NS53x4NhZ_wcTPq8E_ccURRvkjrbg.cache +3 -0
  84. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/KH/KHGH8y7fyC26rhzb2-uu0cV4z5-pxa2N3NkDIL_feBk.cache +1 -0
  85. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/LY/LYOt9-oLcF3-pZyrj-ObZCV5sJnzOX-ChSBrkZI72Oc.cache +0 -0
  86. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Lu/LuB__cHcdlpiqGBKHUifyKQAk1Q8hYTNxvUnK1VPuJs.cache +1 -0
  87. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Lv/LvkAiAXzEhMj_E-ZRx0qe2EAXBsm_M7MRf4ids1kxdw.cache +0 -0
  88. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Mw/MwOabPFIFDte8ZBuoVOOtd1Qu476fF4T4E78To9L2Eo.cache +1 -0
  89. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Mx/MxvaxgCXDp1kMg4ZPyEj18UaF7hti7eGd5irCjj7CYA.cache +0 -0
  90. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/QF/QFvF-z4bMPE9dkk_vRFZYF8e2goQi8vWGeExjSUT92Q.cache +1 -0
  91. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Re/Red0n__pXlXNH8SzCQTdfqRmTJxrcVN9AWr1__pJFP0.cache +1 -0
  92. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/SI/SI9DDWxIPbS6sVOJ772L6_Jbfq17xBHX_sJw6gutPq4.cache +0 -0
  93. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/SN/SNWLiOT9nptoa_fIBzkk6rau30FCwQnjLilnZtLUWKU.cache +1 -0
  94. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/TF/TFYV3Z2UC54NjsDNnfNTRAaCpEG-_o6wRVDYezsnnzk.cache +0 -0
  95. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/X_/X_-4jxim1KTh1Fjxx8_IJyO-3Wzv3M4N70rE0Hi5ipY.cache +2 -0
  96. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Z4/Z4xtfQJ5r3sHK5ASuH1nYxFVJM360rK3PnL_RtnWXAk.cache +1 -0
  97. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/_h/_hwlFJ7l4uM_oLaUe5ZKud1WAQO5QCyqspXi5bIjitk.cache +0 -0
  98. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/b_/b_Itlk9QZZd7Rvf8kcA4yLP1R5Acu7jB-m1xQiSU0qE.cache +2 -0
  99. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ct/ctAKDrnI0EvuP7FAZXCRPZCWA3hmwlIMurxUpX4dOO8.cache +0 -0
  100. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/dg/dg3wGWeFjow5LX98G4IGeWcMyoTHYsklNz3gOLQSXr8.cache +0 -0
  101. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/lV/lVt8G4NTNqewg9C9PXeaR1Abtx7AHu0vaSkHAhGWR0M.cache +1 -0
  102. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/mX/mX1nlsL_SWOB4y22W5FheRX0YEONKyOY7xUeIvRiHco.cache +2 -0
  103. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/me/me4uxBSFG-PjZ9e6iUX80JlbffE5GmRQ7Un-fsLO0QA.cache +1 -0
  104. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/n_/n_xYqQYhwEMQknb3jFQnjlxxBE9TzMNHCdJ-bEyZFIw.cache +2 -0
  105. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/nx/nxTv3sKVUQZADJyM3dPaVmUA78MIsMLD_K279yN_GsI.cache +2 -0
  106. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/oz/ozK4IPJj2CBcsem3fLJx3wqcOl6mI6B91PkN0wVfkyk.cache +2 -0
  107. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/pZ/pZoXZhq291Gxs21xFN0T4V1N1KwyFISSp7okEgbD7lY.cache +1 -0
  108. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/qP/qPmv5snMrDw830S6hSICDcnIy7kVEWoFKXhGKT38lG4.cache +2 -0
  109. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/vA/vAcjLVwbHcbzsPpYo9NmFa4oW46YZoyQRriaJNpEoqw.cache +0 -0
  110. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/vu/vugy1YR_4jOXqgIEqNMSTWQ6DiSEhSEzsBTMqENWhxU.cache +1 -0
  111. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/x7/x7PYh8DJvPykcEqpVab2vcY9-GFz-3cqtoMlRAu94Uc.cache +2 -0
  112. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/z1/z1pCDnU1k4aeEb3hZmNX4QNvaCUFbtHX4L7noBd035M.cache +1 -0
  113. data/spec/dummy/tmp/pids/server.pid +1 -0
  114. data/spec/rails_helper.rb +78 -0
  115. data/spec/rubame/server_spec.rb +113 -0
  116. data/spec/spec_helper.rb +99 -0
  117. metadata +414 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 15f7d10ed9788e61ebd810c34eeb3d67bf63ef9c
4
+ data.tar.gz: 6396f3d2703750b5026fd5e560b2b0ff9dd4265e
5
+ SHA512:
6
+ metadata.gz: 99e21f6996bf87be7d1db17b57ecc6e9349403953ab039c85ed7dd73814f183163c104d71bb603518fdd88dedfce99114853f64f9020da06e14bdeef361bb110
7
+ data.tar.gz: 5c8b6fb0b2a96475dd74d278e513eeb548d55553eabdd477cd9dca47036a90a35916860c6aa6b0f0149f3a0ee11bf3084b0d4d053a3c873c8e37846dfc0f6f00
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2016 Patrice Lebel
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/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # FlammarionRails
2
+
3
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'FlammarionRails'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+ load 'rails/tasks/statistics.rake'
22
+
23
+
24
+
25
+ require 'bundler/gem_tasks'
26
+
File without changes
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ Rails.application.routes.draw do
2
+ end
@@ -0,0 +1,95 @@
1
+ module Flammarion
2
+ class Engraving
3
+ include Revelator
4
+
5
+ attr_accessor :on_disconnect, :on_connect, :sockets
6
+
7
+ # Creates a new Engraving (i.e., a new display window)
8
+ # @option options [Proc] :on_connect Called when the display window is
9
+ # connected (i.e., displayed)
10
+ # @option options [Proc] :on_disconnect Called when the display windows is
11
+ # disconnected (i.e., closed)
12
+ # @option options [Boolean] :exit_on_disconnect (false) Will call +exit+
13
+ # when the widow is closed if this option is true.
14
+ # @option options [Boolean] :close_on_exit (false) Will close the window
15
+ # when the process exits if this is true. Otherwise, it will just stay
16
+ # around, but not actually be interactive.
17
+ # @raise {SetupError} if neither chrome is set up correctly and
18
+ # and Flammarion is unable to display the engraving.
19
+ def initialize(**options)
20
+ @chrome = OpenStruct.new
21
+ @sockets = []
22
+ @on_connect = options[:on_connect]
23
+ @on_disconnect = options[:on_disconnect]
24
+ @exit_on_disconnect = options.fetch(:exit_on_disconnect, false)
25
+
26
+ start_server
27
+ @window_id = @@server.register_window(self)
28
+ open_a_window(options) unless options[:no_window]
29
+ wait_for_a_connection unless options[:no_wait]
30
+
31
+ at_exit {close if window_open?} if options.fetch(:close_on_exit, true)
32
+ end
33
+
34
+ # Blocks the current thread until the window has been closed. All user
35
+ # interactions and callbacks will continue in other threads.
36
+ def wait_until_closed
37
+ sleep 1 until @sockets.empty?
38
+ end
39
+
40
+ # Is this Engraving displayed on the screen.
41
+ def window_open?
42
+ !@sockets.empty?
43
+ end
44
+
45
+ def disconnect(ws)
46
+ @sockets.delete ws
47
+ exit 0 if @exit_on_disconnect
48
+ @on_disconnect.call if @on_disconnect
49
+ end
50
+
51
+ def process_message(msg)
52
+ result = dispatch(JSON.parse(msg).with_indifferent_access)
53
+
54
+ send_json(document: result.last.body)
55
+
56
+ rescue JSON::ParserError
57
+ Rails.logger.debug "Invalid JSON String #{msg}"
58
+ end
59
+
60
+ def dispatch(params)
61
+ http_method = params.delete(:method) || :get
62
+ mapping = Rails.application.routes.recognize_path(params.delete(:url), method: http_method, **params)
63
+
64
+ unless mapping.key?(:controller)
65
+ Rails.logger.debug "Path not found"
66
+ return ActionDispatch::Request::PASS_NOT_FOUND
67
+ end
68
+
69
+ controller_name = "#{mapping[:controller].underscore.camelize}Controller"
70
+ controller = ActiveSupport::Dependencies.constantize(controller_name)
71
+ action = mapping[:action] || 'index'
72
+ request = ActionDispatch::Request.new('rack.input' => '', 'REQUEST_METHOD' => http_method.to_s.upcase!)
73
+ response = controller.make_response! request
74
+
75
+ controller.dispatch(action, request, response)
76
+ end
77
+
78
+ def start_server
79
+ @@server ||= Server.new
80
+ end
81
+
82
+ def server
83
+ @@server
84
+ end
85
+
86
+ def send_json(val)
87
+ if @sockets.empty?
88
+ open_a_window
89
+ wait_for_a_connection
90
+ end
91
+ @sockets.each{ |ws| ws.send val.to_json }
92
+ nil
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,88 @@
1
+ module Flammarion
2
+ class SetupError < StandardError; end
3
+
4
+ module Revelator
5
+ CHROME_PATH = ENV["FLAMMARION_REVELATOR_PATH"] || 'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe'
6
+
7
+ def open_a_window(**options)
8
+ index_path = FlammarionRails::Engine.root.join('public', 'index.html')
9
+ index_path = `cygpath -w '#{index_path}'`.strip if RbConfig::CONFIG["host_os"] == "cygwin"
10
+
11
+ url = "file://#{index_path}?" + { port: server.port, path: @window_id, boot: FlammarionRails.config.boot_path }.to_query
12
+ @browser_options = options.merge(url: url)
13
+ @requested_browser = ENV["FLAMMARION_BROWSER"] || options[:browser]
14
+
15
+ @browser = @@browsers.find do |browser|
16
+ next if @requested_browser and browser.name.to_s != @requested_browser
17
+ begin
18
+ send(browser.name, @browser_options)
19
+ rescue Exception
20
+ next
21
+ end
22
+ end
23
+
24
+ raise SetupError.new("You must have google-chrome installed and accesible via your path.") unless @browser
25
+ end
26
+
27
+ def wait_for_a_connection
28
+ Timeout.timeout(20) { sleep 0.5 while @sockets.empty? }
29
+ rescue Timeout::Error
30
+ raise SetupError.new("Timed out while waiting for a connecting using #{@browser.name}.")
31
+ end
32
+
33
+ private
34
+
35
+ @@browsers = []
36
+
37
+ def self.browser(name, &block)
38
+ @@browsers << OpenStruct.new(name: name, method:define_method(name, block))
39
+ end
40
+
41
+ browser :osx do |options|
42
+ return false unless RbConfig::CONFIG["host_os"] =~ /darwin|mac os/
43
+ executable = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
44
+ @chrome.in, @chrome.out, @chrome.err, @chrome.thread = Open3.popen3("'#{executable}' --app='#{options[:url]}'")
45
+ true if @chrome.in
46
+ end
47
+
48
+ browser :chrome_windows do |options|
49
+ return false unless RbConfig::CONFIG["host_os"] =~ /cygwin|mswin|mingw/
50
+ file_path = File.absolute_path(File.join(File.dirname(__FILE__), ".."))
51
+ file_path = `cygpath -w '#{file_path}'`.strip if RbConfig::CONFIG["host_os"] == "cygwin"
52
+ resource = %[file\://#{file_path}/html/build/index.html]
53
+ resource = "http://localhost:4567/" if options[:development_mode]
54
+ chrome_path = CHROME_PATH
55
+ chrome_path = `cygpath -u '#{CHROME_PATH}'`.strip if RbConfig::CONFIG["host_os"] == "cygwin"
56
+ return false unless File.exist?(chrome_path)
57
+ Process.detach(spawn(chrome_path, %[--app=#{resource}?path=#{@window_id}&port=#{server.port}]))
58
+ end
59
+
60
+ browser :chrome do |options|
61
+ %w[google-chrome google-chrome-stable chromium chromium-browser chrome].each do |executable|
62
+ next unless which(executable)
63
+ @chrome.in, @chrome.out, @chrome.err, @chrome.thread = Open3.popen3("#{executable} --app='#{options[:url]}'")
64
+ return true if @chrome.in
65
+ end
66
+ false
67
+ end
68
+
69
+ browser :www do |options|
70
+ # Last ditch effort to display something
71
+ Launchy.open(options[:url].gsub(/\s/, "%20")) do |error|
72
+ return false
73
+ end
74
+ true
75
+ end
76
+
77
+ def which(cmd)
78
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
79
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
80
+ exts.each do |ext|
81
+ exe = File.join(path, "#{cmd}#{ext}")
82
+ return exe if File.executable?(exe) && !File.directory?(exe)
83
+ end
84
+ end
85
+ nil
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,88 @@
1
+ module Flammarion
2
+ class Server
3
+ attr_accessor :port
4
+
5
+ def initialize
6
+ @windows = {}
7
+ @socket_paths = {}
8
+ @started = false
9
+ @launch_thread = Thread.current
10
+ @server_thread = Thread.new do
11
+ begin
12
+ start_server_internal
13
+ rescue StandardError => e
14
+ handle_exception(e)
15
+ end
16
+ end
17
+ sleep 0.5 until @started
18
+ end
19
+
20
+ def start_server_internal
21
+ self.port =
22
+ if Gem.win_platform?
23
+ rand(65000 - 1024) + 1024
24
+ else
25
+ 7870
26
+ end
27
+ begin
28
+ @server = Rubame::Server.new("0.0.0.0", port)
29
+ loop do
30
+ @started = true
31
+ @server.run do |ws|
32
+ ws.onopen {
33
+ log "Connection open"
34
+ if @windows.include?(ws.handshake.path)
35
+ @windows[ws.handshake.path].sockets << ws
36
+ @windows[ws.handshake.path].on_connect.call if @windows[ws.handshake.path].on_connect
37
+ @socket_paths[ws] = ws.handshake.path
38
+ else
39
+ log "No such window: #{handshake.path}"
40
+ end
41
+ }
42
+
43
+ ws.onclose do
44
+ log "Connection closed"
45
+ @windows[@socket_paths[ws]].disconnect(ws) if @windows[@socket_paths[ws]]
46
+ end
47
+
48
+ ws.onmessage { |msg|
49
+ Thread.new do
50
+ begin
51
+ @windows[@socket_paths[ws]].process_message(msg)
52
+ rescue StandardError => e
53
+ handle_exception(e)
54
+ end
55
+ end
56
+ }
57
+ end
58
+ end
59
+ rescue RuntimeError, Errno::EADDRINUSE => e
60
+ if e.message == "no acceptor (port is in use or requires root privileges)" || e.is_a?(Errno::EADDRINUSE)
61
+ self.port = rand(65000 - 1024) + 1024
62
+ retry
63
+ else
64
+ raise
65
+ end
66
+ end
67
+ @started = true
68
+ end
69
+
70
+ def stop
71
+ end
72
+
73
+ def log(str)
74
+ Rails.logger.debug str
75
+ end
76
+
77
+ def handle_exception(e)
78
+ @launch_thread.raise(e)
79
+ end
80
+
81
+ def register_window(window)
82
+ @new_path ||= 0
83
+ @new_path += 1
84
+ @windows["/w#{@new_path}"] = window
85
+ "w#{@new_path}"
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,5 @@
1
+ require "flammarion_rails/engine"
2
+
3
+ module FlammarionRails
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,25 @@
1
+ module FlammarionRails
2
+ @@config = nil
3
+
4
+ def self.configure
5
+ @@config ||= Configuration.new
6
+
7
+ if block_given?
8
+ yield config
9
+ end
10
+
11
+ config
12
+ end
13
+
14
+ def self.config
15
+ @@config || configure
16
+ end
17
+
18
+ class Configuration
19
+ attr_accessor :boot_path
20
+
21
+ def boot_path
22
+ @boot_path ||= '/'
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ require "flammarion_rails/configuration"
2
+
3
+ require 'open3'
4
+ require "rubame/server"
5
+ require "flammarion/server"
6
+ require "flammarion/revelator"
7
+ require "flammarion/engraving"
8
+
9
+ module FlammarionRails
10
+ class Engine < ::Rails::Engine
11
+ # Initialize engine dependencies on wrapper application
12
+ Gem.loaded_specs["flammarion_rails"].dependencies.each do |d|
13
+ begin
14
+ require d.name
15
+ rescue LoadError => e
16
+ # Put exceptions here.
17
+ end
18
+ end
19
+ # Uncomment if migrations need to be shared
20
+ # initializer :append_migrations do |app|
21
+ # unless app.root.to_s.match root.to_s
22
+ # config.paths["db/migrate"].expanded.each do |expanded_path|
23
+ # app.config.paths["db/migrate"] << expanded_path
24
+ # end
25
+ # end
26
+ # end
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ module FlammarionRails
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,172 @@
1
+ module Rubame
2
+ class Server
3
+ def initialize(host, port)
4
+ Socket.do_not_reverse_lookup
5
+ @hostname = host
6
+ @port = port
7
+
8
+ @reading = []
9
+ @writing = []
10
+
11
+ @clients = {} # Socket as key, and Client as value
12
+
13
+ @socket = TCPServer.new(@hostname, @port)
14
+ @reading.push @socket
15
+ end
16
+
17
+ def accept
18
+ socket = @socket.accept_nonblock
19
+ @reading.push socket
20
+ handshake = WebSocket::Handshake::Server.new
21
+ client = Rubame::Client.new(socket, handshake, self)
22
+
23
+ while (line = socket.gets)
24
+ client.handshake << line
25
+ break if client.handshake.finished?
26
+ end
27
+
28
+ if client.handshake.valid?
29
+ @clients[socket] = client
30
+ client.write handshake.to_s
31
+ client.opened = true
32
+ return client
33
+ else
34
+ close(client)
35
+ end
36
+
37
+ nil
38
+ end
39
+
40
+ def read(client)
41
+ pairs = client.socket.recvfrom(2000)
42
+ messages = []
43
+
44
+ if pairs[0].length == 0
45
+ close(client)
46
+ else
47
+ client.frame << pairs[0]
48
+
49
+ while (f = client.frame.next)
50
+ if f.type == :close
51
+ close(client)
52
+ return messages
53
+ else
54
+ messages.push f
55
+ end
56
+ end
57
+ end
58
+
59
+ messages
60
+ end
61
+
62
+ def close(client)
63
+ @reading.delete client.socket
64
+ @clients.delete client.socket
65
+ begin
66
+ client.socket.close
67
+ rescue
68
+ # do nothing
69
+ end
70
+ client.closed = true
71
+ end
72
+
73
+ def run(&blk)
74
+ readable, _writable = IO.select(@reading, @writing)
75
+
76
+ if readable
77
+ readable.each do |socket|
78
+ client = @clients[socket]
79
+ if socket == @socket
80
+ client = accept
81
+ else
82
+ msg = read(client)
83
+ client.messaged = msg
84
+ end
85
+
86
+ blk.call(client) if client && blk
87
+ end
88
+ end
89
+ end
90
+
91
+ def stop
92
+ @socket.close
93
+ end
94
+ end
95
+
96
+ class Client
97
+ attr_accessor :socket, :handshake, :frame, :opened, :messaged, :closed
98
+
99
+ def initialize(socket, handshake, server)
100
+ @socket = socket
101
+ @handshake = handshake
102
+ @frame = WebSocket::Frame::Incoming::Server.new(version: @handshake.version)
103
+ @opened = false
104
+ @messaged = []
105
+ @closed = false
106
+ @server = server
107
+ end
108
+
109
+ def write(data)
110
+ @socket.write data
111
+ end
112
+
113
+ def send(data)
114
+ frame = WebSocket::Frame::Outgoing::Server.new(version: @handshake.version, data: data, type: :text)
115
+ begin
116
+ @socket.write frame
117
+ @socket.flush
118
+ rescue
119
+ @server.close(self) unless @closed
120
+ end
121
+ end
122
+
123
+ def onopen(&blk)
124
+ if @opened
125
+ begin
126
+ blk.call
127
+ ensure
128
+ @opened = false
129
+ end
130
+ end
131
+ end
132
+
133
+ def onmessage(&blk)
134
+ if @messaged.size > 0
135
+ begin
136
+ @messaged.each do |x|
137
+ blk.call(x.to_s)
138
+ end
139
+ ensure
140
+ @messaged = []
141
+ end
142
+ end
143
+ end
144
+
145
+ def onclose(&blk)
146
+ if @closed
147
+ begin
148
+ blk.call
149
+ ensure
150
+ # do nothing
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
156
+
157
+ if __FILE__==$0
158
+ server = Rubame::Server.new("0.0.0.0", 25222)
159
+ while !$quit
160
+ server.run do |client|
161
+ client.onopen do
162
+ puts "Server reports: client open"
163
+ end
164
+ client.onmessage do |mess|
165
+ puts "Server reports: message received: #{mess}"
166
+ end
167
+ client.onclose do
168
+ puts "Server reports: client closed"
169
+ end
170
+ end
171
+ end
172
+ end