react-sinatra 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 (33) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.travis.yml +14 -0
  4. data/.yardopts +4 -0
  5. data/Gemfile +14 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +147 -0
  8. data/Rakefile +10 -0
  9. data/lib/react-sinatra.rb +1 -0
  10. data/lib/react/sinatra.rb +32 -0
  11. data/lib/react/sinatra/component.rb +97 -0
  12. data/lib/react/sinatra/configuration.rb +39 -0
  13. data/lib/react/sinatra/error.rb +11 -0
  14. data/lib/react/sinatra/helpers.rb +18 -0
  15. data/lib/react/sinatra/pool.rb +80 -0
  16. data/lib/react/sinatra/runtime.rb +50 -0
  17. data/lib/react/sinatra/runtime/execjs.rb +41 -0
  18. data/lib/react/sinatra/runtime/runtime_based.rb +99 -0
  19. data/lib/react/sinatra/template.rb +45 -0
  20. data/lib/react/sinatra/templates/polyfill.js +10 -0
  21. data/lib/react/sinatra/templates/react-source/development-with-addons/react-server.js +24307 -0
  22. data/lib/react/sinatra/templates/react-source/development-with-addons/react.js +24156 -0
  23. data/lib/react/sinatra/templates/react-source/development/react-server.js +20587 -0
  24. data/lib/react/sinatra/templates/react-source/development/react.js +21462 -0
  25. data/lib/react/sinatra/templates/react-source/production-with-addons/react-server.js +19 -0
  26. data/lib/react/sinatra/templates/react-source/production-with-addons/react.js +19 -0
  27. data/lib/react/sinatra/templates/react-source/production/react-server.js +19 -0
  28. data/lib/react/sinatra/templates/react-source/production/react.js +19 -0
  29. data/lib/react/sinatra/templates/runtime.js +4 -0
  30. data/lib/react/sinatra/templates/variables.js +5 -0
  31. data/lib/react/sinatra/version.rb +5 -0
  32. data/react-sinatra.gemspec +27 -0
  33. metadata +117 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5ff3f7ac7e65e474e927285731ff48c025090e88
4
+ data.tar.gz: f9539bf7704022157a4b16210bb61add72e281a2
5
+ SHA512:
6
+ metadata.gz: f361c16cef519f84addbdff5d049de26b08a6f55407971c9ec615e18b506d9a94a458bfb2816ca11fa30e06f26ef29be6b932f364810f59863b660a3eb715fe7
7
+ data.tar.gz: be72c4dfef7dabce8f939038c4a357d1a8e4cee7d037bd3cabe2289464a764e1eedd78c4ffc17383a62d5d3adab0c074ee0a8d6cad88ea974e82fa830efc7dcf
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,14 @@
1
+ sudo: required
2
+ dist: trusty
3
+ language: ruby
4
+ addons:
5
+ apt:
6
+ sources:
7
+ - ubuntu-toolchain-r-test
8
+ packages:
9
+ - g++-4.8
10
+ rvm:
11
+ - 2.2.6
12
+ - 2.3.3
13
+ - 2.4.0
14
+ before_install: gem install bundler -v 1.13.6
@@ -0,0 +1,4 @@
1
+ --exclude lib/react/sinatra/templates/
2
+ -
3
+ lib/**/*.rb
4
+ lib/react/sinatra/runtime.rb
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rake'
4
+ gem 'test-unit'
5
+ gem 'mocha'
6
+ gem 'rack-test'
7
+
8
+ gem 'mini_racer'
9
+ gem 'execjs'
10
+
11
+ gem 'yard'
12
+
13
+ # Specify your gem's dependencies in react-sinatra.gemspec
14
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 namusyaka
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,147 @@
1
+ # react-sinatra
2
+
3
+ [![Build Status](https://travis-ci.org/namusyaka/react-sinatra.svg?branch=master)](https://travis-ci.org/namusyaka/react-sinatra)
4
+
5
+ `react-sinatra` makes it easy to use React in your Sinatra and Padrino application.
6
+
7
+ Please see [a sample](https://github.com/namusyaka/react-sinatra-sample).
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'react-sinatra'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install react-sinatra
24
+
25
+ ## Anti-Features
26
+
27
+ `react-sinatra` does not:
28
+
29
+ - transform `.jsx` files or JSX syntax.
30
+ - transpile asset using babel.
31
+ - support asset-pipeline.
32
+ - have generator for registering this extension.
33
+
34
+ I think those features should be solved by using [webpack](https://webpack.github.io/), or other build tools.
35
+
36
+ ## Usage
37
+
38
+ ### Sinatra Plug-in
39
+
40
+ It's easy to register `react-sinatra` in your application, next section will describe three steps for introduction.
41
+
42
+ #### 1. Add react-sinatra and runtime into your Gemfile
43
+
44
+ ```ruby
45
+ source 'https://rubygems.org'
46
+
47
+ gem 'react-sinatra'
48
+ gem 'execjs'
49
+ gem 'mini_racer' # also `therubyracer` may be available, but mini_racer is simpler and faster.
50
+ ```
51
+
52
+ #### 2. Register react-sinatra and configure.
53
+
54
+ ```ruby
55
+ class App < Sinatra::Base
56
+ register React::Sinatra
57
+
58
+ configure do
59
+ React::Sinatra.configure do |config|
60
+ # configures for bundled React.js
61
+ config.use_bundled_react = true
62
+ config.env = ENV['RACK_ENV'] || :development
63
+ config.addon = true
64
+
65
+ # The asset should be able to be compiled by your server side runtime.
66
+ # react-sinatra does not transform jsx into js, also ES2015 may not be worked through.
67
+ config.asset_path = File.join('client', 'dist', 'server.js')
68
+ config.runtime = :execjs
69
+ end
70
+ end
71
+ end
72
+ ```
73
+
74
+ #### 3. Use `react_component` on your view or sinatra application.
75
+
76
+ ```erb
77
+ <%= react_component('Hello', { name: 'namusyaka' }, prerender: true) %>
78
+ ```
79
+
80
+ ```ruby
81
+ get '/:name' do |name|
82
+ component = react_component('Hello', { name: name }, prerender: true)
83
+ # ...
84
+ end
85
+ ```
86
+
87
+ The react component **must** be able to be referred from global scope, so you should be careful for your asset javascript.
88
+ In the above example, you need to provide an asset that puts the component called `Hello` in a state that it can be referred to from the global.
89
+
90
+ ```javascript
91
+ class Hello extends React.Component {
92
+ render() {
93
+ return (
94
+ <div className="hello">
95
+ Hello, {this.props.name}!
96
+ </div>
97
+ );
98
+ }
99
+ }
100
+
101
+ global.Hello = Hello;
102
+ ```
103
+
104
+ ### React.js
105
+
106
+ #### Bundled React.js
107
+
108
+ You can use bundled React.js by specifying configurations.
109
+
110
+ ```ruby
111
+ React::Sinatra.configure do |config|
112
+ config.use_bundled_react = true # Defaults to false
113
+ end
114
+ ```
115
+
116
+ You can also specify `addon` and `env` for each environments.
117
+
118
+ ```ruby
119
+ React::Sinatra.configure do |config|
120
+ config.addon = false # Defaults to false
121
+ config.env = ENV['RACK_ENV'] || :development
122
+ end
123
+ ```
124
+
125
+ #### Note
126
+
127
+ **If you want to use your own react, you must include React.js and react-server source code in the asset.**
128
+
129
+ ```ruby
130
+ React::Sinatra.configure do |config|
131
+ config.asset_path = 'client/dist/*.js'
132
+ end
133
+ ```
134
+
135
+ ### TODO
136
+
137
+ - Support nodejs as a runtime
138
+
139
+ ## Contributing
140
+
141
+ Bug reports and pull requests are welcome on GitHub at https://github.com/namusyaka/react-sinatra.
142
+
143
+
144
+ ## License
145
+
146
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
147
+
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |test|
5
+ test.libs << 'test'
6
+ test.warning = false
7
+ test.test_files = Dir['test/**/test_*.rb']
8
+ end
9
+
10
+ task default: :test
@@ -0,0 +1 @@
1
+ require 'react/sinatra'
@@ -0,0 +1,32 @@
1
+ module React
2
+ # Namespace for the react-sinatra library.
3
+ #
4
+ # This module is a Sinatra extension and is used with the `register` keyword.
5
+ module Sinatra
6
+ # Method for the `register` keyword hook on your application.
7
+ # @param [Sinatra::Base] app
8
+ def self.registered(app)
9
+ app.helpers Helpers
10
+ end
11
+
12
+ # Configures react-sinatra by using {React::Sinatra::Configuration}.
13
+ # @see [React::Sinatra::Configuration]
14
+ # @yieldparam [React::Sinatra::Configuration]
15
+ def self.configure
16
+ yield config
17
+ end
18
+
19
+ # Returns configuration instance using Singleton module.
20
+ # @see [React::Sinatra::Configuration]
21
+ # @return [React::Sinatra::Configuration]
22
+ def self.config
23
+ Configuration.instance
24
+ end
25
+ end
26
+ end
27
+
28
+ require 'react/sinatra/version'
29
+ require 'react/sinatra/configuration'
30
+ require 'react/sinatra/helpers'
31
+ require 'react/sinatra/template'
32
+ require 'react/sinatra/runtime'
@@ -0,0 +1,97 @@
1
+ require 'json'
2
+ require 'padrino-helpers'
3
+ require 'active_support/core_ext/hash/reverse_merge'
4
+ require 'active_support/core_ext/string/inflections'
5
+ require 'react/sinatra/pool'
6
+
7
+ module React
8
+ module Sinatra
9
+ # Class for expressing react component.
10
+ #
11
+ # This is main entrypoint for rendering react component
12
+ # and is referred from {React::Sinatra::Helpers#react_component}.
13
+ class Component
14
+ include Padrino::Helpers::OutputHelpers
15
+ include Padrino::Helpers::TagHelpers
16
+
17
+ attr_reader :component, :props, :prerender
18
+
19
+ # Renders react component from given arguments.
20
+ #
21
+ # @example
22
+ # React::Sinatra::Component.render('Hello', { name: 'namusyaka' }, prerender: true)
23
+ #
24
+ # @param [String] component The name for component must be able to be referred from asset javascript.
25
+ # @param [Hash] props The props for rendering react-dom
26
+ # @param [TrueClass, FalseClass] prerender Whether server side rendering is enabled or not.
27
+ # @param [TrueClass, FalseClass] camelize_props Whether camelization is enabled or not.
28
+ # @param [String, Symbol] tag The tag name for rendering react root element. Defaults to :div
29
+ #
30
+ # @raise [React::Sinatra::RuntimeError] Raised if server side rendering is enabled and failed
31
+ # @return [ActiveSupport::SafeBuffer] safe string for rendering in application views
32
+ def self.render(component, props, prerender: false, **options)
33
+ new(component, prerender: prerender).render(props, **options)
34
+ end
35
+
36
+ # Camelizes props for enabling {camelize_props} option.
37
+ #
38
+ # @param [Hash] props The props for rendering react-dom
39
+ # @return [Hash] camelized props
40
+ def self.camelize_props(props)
41
+ case props
42
+ when Hash
43
+ props.each_with_object({}) do |(key, value), new_props|
44
+ new_key = key.to_s.camelize(:lower)
45
+ new_value = camelize_props(value)
46
+ new_props[new_key] = new_value
47
+ end
48
+ when Array
49
+ props.map(&method(:camelize_props))
50
+ else
51
+ props
52
+ end
53
+ end
54
+
55
+ # Constructs an instance of React::Sinatra::Component.
56
+ #
57
+ # @param [String] component The name for component must be able to be referred from asset javascript
58
+ # @param [TrueClass, FalseClass] prerender Whether server side rendering is enabled or not.
59
+ # @return [React::Sinatra::Component]
60
+ def initialize(component, prerender: false, **options)
61
+ @component = component
62
+ @prerender = prerender
63
+ end
64
+
65
+ # Renders react component from given arguments and component.
66
+ #
67
+ # @param [Hash] props The props for rendering react-dom
68
+ # @param [TrueClass, FalseClass] camelize_props Whether camelization is enabled or not
69
+ # @param [String, Symbol] tag The tag name for rendering react root element. Defaults to :div
70
+ # @see https://github.com/padrino/padrino-framework/blob/master/padrino-helpers/lib/padrino-helpers/tag_helpers.rb#L119
71
+ # @return [ActiveSupport::SafeBuffer] safe string for rendering in application views
72
+ def render(props, camelize_props: false, tag: :div, **options, &block)
73
+ props = self.class.camelize_props(props) if camelize_props
74
+ block = -> {
75
+ concat React::Sinatra::Pool.render(component, props.to_json)
76
+ } if prerender?
77
+
78
+ options.reverse_merge!(data: {})
79
+ options[:data].tap do |data|
80
+ data[:react_class] = component
81
+ data[:react_props] = props.is_a?(String) ? props : props.to_json
82
+ end unless prerender == :static
83
+
84
+ content_tag(tag, '', options, &block)
85
+ end
86
+
87
+ # Returns true if the :prerender option is enabled.
88
+ #
89
+ # @return [TrueClass, FalseClass]
90
+ def prerender?
91
+ !! prerender
92
+ end
93
+
94
+ private :prerender?
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,39 @@
1
+ require 'react/sinatra/runtime'
2
+ require 'singleton'
3
+
4
+ module React
5
+ module Sinatra
6
+ # Class for expressing configuration delegated from classes.
7
+ #
8
+ # This #instance entrypoint is referred from application.
9
+ class Configuration
10
+ include Singleton
11
+
12
+ attr_accessor :pool_size, :pool_timeout, :asset_path, :use_bundled_react, :env, :addon
13
+
14
+ # Assigns runtime by given name.
15
+ #
16
+ # @param [String, Symbol] name The name of runtime
17
+ # @see React::Sinatra::Runtime.[]
18
+ # @return [React::Sinatra::Runtime::RuntimeBased]
19
+ def runtime=(name)
20
+ @runtime = Runtime[name.to_sym]
21
+ end
22
+
23
+ # Returns current runtime.
24
+ # @return [React::Sinatra::Runtime::RuntimeBased]
25
+ def runtime
26
+ @runtime
27
+ end
28
+
29
+ # Assigns default values for use in server-side rendering.
30
+ instance.pool_size = 5
31
+ instance.pool_timeout = 10
32
+ instance.runtime = :execjs
33
+
34
+ instance.use_bundled_react = false
35
+ instance.env = ENV['RACK_ENV'] || :development
36
+ instance.addon = false
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,11 @@
1
+ module React
2
+ module Sinatra
3
+ class RuntimeError < ::RuntimeError
4
+ def initialize(component, props, message:, **options)
5
+ message = ["Encountered error \"#{message}\" when prerendering #{component} with #{props}",
6
+ message.backtrace.join(?\n)].join(?\n)
7
+ super(message)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ require 'react/sinatra/component'
2
+
3
+ module React
4
+ module Sinatra
5
+ # Module for registering Sinatra application.
6
+ #
7
+ # This is main entrypoint for integration on Sinatra.
8
+ module Helpers
9
+ # Renders react component from given arguments.
10
+ #
11
+ # @see {React::Sinatra::Component.render}
12
+ # @return [ActiveSupport::SafeBuffer] safe string for rendering in application views
13
+ def react_component(*args)
14
+ React::Sinatra::Component.render(*args)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,80 @@
1
+ require 'forwardable'
2
+
3
+ module React
4
+ module Sinatra
5
+ # Class for expressing connection pool.
6
+ #
7
+ # @example
8
+ # pool = React::Sinatra::Pool.new
9
+ # pool.render('Hello', { name: 'bob' }, prerender: true)
10
+ # pool.reset
11
+ #
12
+ # @!visibility private
13
+ class Pool
14
+ # Utility for runtime renderer
15
+ #
16
+ # @see {#render}
17
+ # @!visibility private
18
+ def self.render(*args, **options)
19
+ pool.render(*args, **options)
20
+ end
21
+
22
+ # Returns an instance of React::Sinatra::Pool as a singleton.
23
+ #
24
+ # @return [React::Sinatra::Pool]
25
+ # @!visibility private
26
+ def self.pool
27
+ @pool ||= Pool.new
28
+ end
29
+
30
+ extend Forwardable
31
+ instance_delegate %i[pool_size pool_timeout runtime] => 'React::Sinatra.config'
32
+
33
+ # Detects runtime from pool, and its runtime
34
+ # renders react component by using server side runtime.
35
+ #
36
+ # @see React::Sinatra::Runtime::RuntimeBased#render
37
+ # @!visibility private
38
+ def render(*args, **options)
39
+ pool.with { |runtime| runtime.render(*args, **options) }
40
+ end
41
+
42
+ # Resets current connection pool.
43
+ #
44
+ # @!visibility private
45
+ def reset
46
+ @pool = nil
47
+ pool
48
+ end
49
+
50
+ # Returns an instance of ConnectionPool.
51
+ #
52
+ # @see https://github.com/mperham/connection_pool
53
+ # @return [Connectionpool]
54
+ # @!visibility private
55
+ def pool
56
+ @pool ||= ConnectionPool.new(size: size, timeout: timeout) { runtime.new }
57
+ end
58
+
59
+ # Returns the globally set pool size or default pool size.
60
+ #
61
+ # @see React::Sinatra::Configuration#pool_size
62
+ # @return [Integer]
63
+ # @!visibility private
64
+ def size
65
+ pool_size || 5
66
+ end
67
+
68
+ # Returns the globally set pool timeout or default pool timeout.
69
+ #
70
+ # @see React::Sinatra::Configuration#pool_timeout
71
+ # @return [Integer]
72
+ # @!visibility private
73
+ def timeout
74
+ pool_timeout || 10
75
+ end
76
+
77
+ private :pool, :size, :timeout
78
+ end
79
+ end
80
+ end