flammarion_rails 0.1.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.
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