├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── clock.rb ├── index.html ├── reactrb-express.js ├── reactrb-express.min.js └── reactrb-express ├── application.rb └── react └── config └── client.rb /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rake' 4 | gem 'opal' 5 | gem 'opal-browser' 6 | gem 'rails' 7 | gem 'opal-rails', '>= 0.8.1' 8 | gem 'react-rails', '~> 1.9.0' 9 | gem 'hyper-operation' 10 | gem 'hyperloop-config' 11 | gem 'opal-jquery' 12 | gem 'uglifier' 13 | #gem 'therubyracer' 14 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actioncable (5.0.2) 5 | actionpack (= 5.0.2) 6 | nio4r (>= 1.2, < 3.0) 7 | websocket-driver (~> 0.6.1) 8 | actionmailer (5.0.2) 9 | actionpack (= 5.0.2) 10 | actionview (= 5.0.2) 11 | activejob (= 5.0.2) 12 | mail (~> 2.5, >= 2.5.4) 13 | rails-dom-testing (~> 2.0) 14 | actionpack (5.0.2) 15 | actionview (= 5.0.2) 16 | activesupport (= 5.0.2) 17 | rack (~> 2.0) 18 | rack-test (~> 0.6.3) 19 | rails-dom-testing (~> 2.0) 20 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 21 | actionview (5.0.2) 22 | activesupport (= 5.0.2) 23 | builder (~> 3.1) 24 | erubis (~> 2.7.0) 25 | rails-dom-testing (~> 2.0) 26 | rails-html-sanitizer (~> 1.0, >= 1.0.3) 27 | activejob (5.0.2) 28 | activesupport (= 5.0.2) 29 | globalid (>= 0.3.6) 30 | activemodel (5.0.2) 31 | activesupport (= 5.0.2) 32 | activerecord (5.0.2) 33 | activemodel (= 5.0.2) 34 | activesupport (= 5.0.2) 35 | arel (~> 7.0) 36 | activesupport (5.0.2) 37 | concurrent-ruby (~> 1.0, >= 1.0.2) 38 | i18n (~> 0.7) 39 | minitest (~> 5.1) 40 | tzinfo (~> 1.1) 41 | addressable (2.5.0) 42 | public_suffix (~> 2.0, >= 2.0.2) 43 | arel (7.1.4) 44 | babel-source (5.8.35) 45 | babel-transpiler (0.7.0) 46 | babel-source (>= 4.0, < 6) 47 | execjs (~> 2.0) 48 | builder (3.2.3) 49 | coffee-script-source (1.12.2) 50 | concurrent-ruby (1.0.5) 51 | connection_pool (2.2.1) 52 | cookiejar (0.3.3) 53 | daemons (1.2.4) 54 | em-http-request (1.1.5) 55 | addressable (>= 2.3.4) 56 | cookiejar (!= 0.3.1) 57 | em-socksify (>= 0.3) 58 | eventmachine (>= 1.0.3) 59 | http_parser.rb (>= 0.6.0) 60 | em-socksify (0.3.1) 61 | eventmachine (>= 1.0.0.beta.4) 62 | em-websocket (0.5.1) 63 | eventmachine (>= 0.12.9) 64 | http_parser.rb (~> 0.6.0) 65 | erubis (2.7.0) 66 | eventmachine (1.2.3) 67 | execjs (2.7.0) 68 | globalid (0.3.7) 69 | activesupport (>= 4.1.0) 70 | hike (1.2.3) 71 | http_parser.rb (0.6.0) 72 | httpclient (2.8.3) 73 | hyper-component (0.12.3) 74 | hyper-react (>= 0.12.3) 75 | hyperloop-config (>= 0.9.2) 76 | opal-rails (~> 0.9.0) 77 | react-rails (< 1.10.0) 78 | hyper-operation (0.5.0) 79 | activerecord (>= 0.3.0) 80 | hyper-component (>= 0.12.2) 81 | hyperloop-config (>= 0.9.2) 82 | mutations 83 | opal-activesupport 84 | pusher 85 | pusher-fake 86 | hyper-react (0.12.3) 87 | hyper-store (>= 0.2.1) 88 | hyperloop-config (>= 0.9.2) 89 | opal (>= 0.8.0) 90 | opal-activesupport (>= 0.2.0) 91 | hyper-store (0.2.1) 92 | hyperloop-config (>= 0.9.2) 93 | hyperloop-config (0.9.2) 94 | opal 95 | i18n (0.8.1) 96 | jquery-rails (4.3.1) 97 | rails-dom-testing (>= 1, < 3) 98 | railties (>= 4.2.0) 99 | thor (>= 0.14, < 2.0) 100 | loofah (2.0.3) 101 | nokogiri (>= 1.5.9) 102 | mail (2.6.4) 103 | mime-types (>= 1.16, < 4) 104 | method_source (0.8.2) 105 | mime-types (3.1) 106 | mime-types-data (~> 3.2015) 107 | mime-types-data (3.2016.0521) 108 | mini_portile2 (2.1.0) 109 | minitest (5.10.1) 110 | multi_json (1.12.1) 111 | mutations (0.8.1) 112 | activesupport 113 | nio4r (2.0.0) 114 | nokogiri (1.7.1) 115 | mini_portile2 (~> 2.1.0) 116 | opal (0.10.3) 117 | hike (~> 1.2) 118 | sourcemap (~> 0.1.0) 119 | sprockets (~> 3.1) 120 | tilt (>= 1.4) 121 | opal-activesupport (0.3.0) 122 | opal (>= 0.5.0, < 1.0.0) 123 | opal-browser (0.2.0) 124 | opal 125 | paggio 126 | opal-jquery (0.4.2) 127 | opal (>= 0.7.0, < 0.11.0) 128 | opal-rails (0.9.1) 129 | jquery-rails 130 | opal (>= 0.8.0, < 0.11) 131 | opal-activesupport (>= 0.0.5) 132 | opal-jquery (~> 0.4.0) 133 | opal-sprockets (~> 0.4.0) 134 | rails (>= 4.0, < 6.0) 135 | sprockets-rails (< 3.0) 136 | opal-sprockets (0.4.0.0.10.0.3.0.0) 137 | opal (~> 0.10.0) 138 | sprockets (~> 3.0) 139 | tilt (>= 1.4) 140 | paggio (0.2.6) 141 | public_suffix (2.0.5) 142 | pusher (1.3.1) 143 | httpclient (~> 2.7) 144 | multi_json (~> 1.0) 145 | pusher-signature (~> 0.1.8) 146 | pusher-fake (1.8.0) 147 | em-http-request (~> 1.1) 148 | em-websocket (~> 0.5) 149 | multi_json (~> 1.6) 150 | thin (~> 1.5) 151 | pusher-signature (0.1.8) 152 | rack (2.0.1) 153 | rack-test (0.6.3) 154 | rack (>= 1.0) 155 | rails (5.0.2) 156 | actioncable (= 5.0.2) 157 | actionmailer (= 5.0.2) 158 | actionpack (= 5.0.2) 159 | actionview (= 5.0.2) 160 | activejob (= 5.0.2) 161 | activemodel (= 5.0.2) 162 | activerecord (= 5.0.2) 163 | activesupport (= 5.0.2) 164 | bundler (>= 1.3.0, < 2.0) 165 | railties (= 5.0.2) 166 | sprockets-rails (>= 2.0.0) 167 | rails-dom-testing (2.0.2) 168 | activesupport (>= 4.2.0, < 6.0) 169 | nokogiri (~> 1.6) 170 | rails-html-sanitizer (1.0.3) 171 | loofah (~> 2.0) 172 | railties (5.0.2) 173 | actionpack (= 5.0.2) 174 | activesupport (= 5.0.2) 175 | method_source 176 | rake (>= 0.8.7) 177 | thor (>= 0.18.1, < 2.0) 178 | rake (12.0.0) 179 | react-rails (1.9.0) 180 | babel-transpiler (>= 0.7.0) 181 | coffee-script-source (~> 1.8) 182 | connection_pool 183 | execjs 184 | railties (>= 3.2) 185 | tilt 186 | sourcemap (0.1.1) 187 | sprockets (3.7.1) 188 | concurrent-ruby (~> 1.0) 189 | rack (> 1, < 3) 190 | sprockets-rails (2.3.3) 191 | actionpack (>= 3.0) 192 | activesupport (>= 3.0) 193 | sprockets (>= 2.8, < 4.0) 194 | thin (1.7.0) 195 | daemons (~> 1.0, >= 1.0.9) 196 | eventmachine (~> 1.0, >= 1.0.4) 197 | rack (>= 1, < 3) 198 | thor (0.19.4) 199 | thread_safe (0.3.6) 200 | tilt (2.0.7) 201 | tzinfo (1.2.2) 202 | thread_safe (~> 0.1) 203 | uglifier (3.1.9) 204 | execjs (>= 0.3.0, < 3) 205 | websocket-driver (0.6.5) 206 | websocket-extensions (>= 0.1.0) 207 | websocket-extensions (0.1.2) 208 | 209 | PLATFORMS 210 | ruby 211 | 212 | DEPENDENCIES 213 | hyper-operation 214 | hyperloop-config 215 | opal 216 | opal-browser 217 | opal-jquery 218 | opal-rails (>= 0.8.1) 219 | rails 220 | rake 221 | react-rails (~> 1.9.0) 222 | uglifier 223 | 224 | BUNDLED WITH 225 | 1.14.6 226 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![](https://github.com/Serzhenka/hyper-loop-logos/blob/master/hyper-express_150.png)Hyper-express 2 | 3 | React.rb for static sites, with no build process needed 4 | 5 | ## How To 6 | 7 | 1. Include reactrb-express.js in with your js files, or link to it from here: https://rawgit.com/reactrb/reactrb-express/master/reactrb-express.js 8 | 2. Link to a version of jQuery 9 | 3. Specify your ruby code inside of `` tags 10 | or link to your ruby code using the src attribute ` 41 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 55 | 56 | 57 | 58 |
59 | 66 | 67 | 68 | ``` 69 | 70 | ```ruby 71 | # clock.rb: Displays the current time 72 | class Clock < Hyperloop::Component 73 | param format: '%a, %e %b %Y %H:%M' 74 | before_mount do 75 | mutate.time Time.now.strftime(params.format) 76 | every(1) { mutate.time Time.now.strftime(params.format) } 77 | end 78 | 79 | render do 80 | state.time 81 | end 82 | end 83 | ``` 84 | 85 | # Want a larger example? 86 | 87 | The [Reactrb ChatRoom application and tutorial](http://reactrb.github.io/docs/tutorial.html) uses Reactrb-Express. 88 | 89 | # Trying it out using github 90 | 91 | Github makes a great sandbox to try out small Reactrb online with nothing but your browser. 92 | 93 | Have a look at the instructions here: https://pages.github.com/ 94 | 95 | but rather than "cloning" the repo, and editing your files on your computer 96 | you can just create and edit files right on the github site. 97 | 98 | # Mounting Components 99 | 100 | In addition to the standard ways to mount top level components reactrb-express will directly mount components onto DOM elements that have the `data-reactrb-mount` attribute. The attribute value should be the fully qualified name of the component. For example "Clock". Any additional data attributes will be passed as params to the component. The attribute names will be snake cased (i.e. `data-foo-bar` becomes the `foo_bar` key) 101 | 102 | # Building and Contributing 103 | 104 | To build, clone the repo, run `bundle install` and then `bundle exec rake` 105 | 106 | This will combine all the pieces and build the `reactrb-express.js` file. 107 | 108 | To be sure we have no ruby dependencies we use this server for smoke testing: 109 | 110 | `python -m SimpleHTTPServer 4000` 111 | 112 | Contributions are welcome - things we need: 113 | 114 | + Examples 115 | + Some test cases 116 | + Minimization 117 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'hyperloop-config' 2 | require 'rubygems' 3 | require 'opal-rails' 4 | require 'hyper-operation' 5 | require 'opal-browser' 6 | require 'opal-jquery' 7 | require 'uglifier' 8 | 9 | desc 'Build reactrb express' 10 | task :build do 11 | Opal.append_path 'reactrb-express' 12 | File.binwrite 'reactrb-express.js', Opal::Builder.build('application').to_s 13 | end 14 | 15 | desc 'Minify using uglifier gem' 16 | task :minify do 17 | js_file = "reactrb-express.js" 18 | js_min_file = "reactrb-express.min.js" 19 | File.open(js_min_file, "w").write(Uglifier.new.compile(File.read(js_file))) 20 | end 21 | task default: [:build, :minify] 22 | -------------------------------------------------------------------------------- /clock.rb: -------------------------------------------------------------------------------- 1 | # clock.rb: Displays the current time 2 | class Clock < Hyperloop::Component 3 | param format: '%a, %e %b %Y %H:%M' 4 | before_mount do 5 | mutate.time Time.now.strftime(params.format) 6 | every(60) { mutate.time Time.now.strftime(params.format) } 7 | end 8 | 9 | render do 10 | "#{state.time} - last message: #{TestStore.message}" 11 | end 12 | end 13 | 14 | class TestOperation < Hyperloop::Operation 15 | param :message 16 | step { params.message = params.message * 2 } 17 | end 18 | 19 | class TestStore < Hyperloop::Store 20 | state :message, scope: :class, reader: true 21 | receives(TestOperation) { |params| mutate.message params.message } 22 | end 23 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Reactrb-Express Demo 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 23 | 24 | 25 | 26 |
27 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /reactrb-express/application.rb: -------------------------------------------------------------------------------- 1 | require 'opal' 2 | require 'opal/compiler' 3 | require 'browser' 4 | require 'browser/socket' 5 | require 'browser/interval' 6 | require 'browser/delay' 7 | require 'opal-jquery' 8 | module Hyperloop 9 | class Component 10 | VERSION = "0.12.4" 11 | end 12 | end 13 | require 'hyper-operation' 14 | require 'react/top_level_render' 15 | require 'react/react-source-browser' 16 | 17 | Document.ready? do 18 | # rubocop:disable Lint/RescueException 19 | # need to catch and report all exceptions 20 | promises = [] 21 | code = [] 22 | Element['script[type="text/ruby"]'].each_with_index do |script_tag, index| 23 | src = script_tag.attr('src') 24 | if src 25 | promises << HTTP.get(src).then do |response| 26 | code[index] = response.body 27 | end 28 | else 29 | code[index] = script_tag.html 30 | end 31 | end 32 | Promise.when(*promises).then do 33 | compiled_code = nil 34 | continue_to_mounting = nil 35 | begin 36 | compiled_code = Opal::Compiler.new(code.join("\n")).compile 37 | rescue Exception => e 38 | message = "Error raised while compiling: #{e.message}" 39 | `console.error(message)` 40 | end 41 | begin 42 | `eval(compiled_code)` 43 | continue_to_mounting = true 44 | rescue Exception => e 45 | message = "Error raised during execution: #{e.message}" 46 | `console.error(message)` 47 | end if compiled_code 48 | Element['[data-reactrb-mount]'].each do |mount_point| 49 | component_name = mount_point.attr('data-reactrb-mount') 50 | component = nil 51 | begin 52 | component = Object.const_get(component_name) 53 | rescue 54 | message = "Could not find Component class named #{component_name}" 55 | `console.error(message)` 56 | next 57 | end 58 | params = Hash[*Hash.new(mount_point.data).collect do |name, value| 59 | [name.underscore, value] unless name == 'reactrbMount' 60 | end.compact.flatten(1)] 61 | React.render(React.create_element(component, params), mount_point) 62 | end if continue_to_mounting 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /reactrb-express/react/config/client.rb: -------------------------------------------------------------------------------- 1 | # dummy up React::Config so it doesn't complain 2 | 3 | module React 4 | module Config 5 | extend self 6 | def environment=(value) 7 | config[:environment] = value 8 | end 9 | 10 | def config 11 | @config ||= default_config 12 | end 13 | 14 | def default_config 15 | { 16 | environment: 'express' 17 | } 18 | end 19 | end 20 | end 21 | --------------------------------------------------------------------------------