react.rb 0.0.2 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2dbadd7b8df4dbcfb57fd8b20da802307d913740
4
- data.tar.gz: 649ec20f57caec86097ad8abdfd2075abf3d8997
3
+ metadata.gz: c1e1e89f7d83f0101d262fc4249dedea946c4af1
4
+ data.tar.gz: ea0be5738084717bcc7388913225388086d67c7f
5
5
  SHA512:
6
- metadata.gz: 0ce9d3607ecca7d4891b0ba96dca4ebe5bcd950ff8092a3bd569cbd0da536a4c0f2bbc96faa6e17361875f07b0620bfd3aa13958edc96d7c51225af17d5ed053
7
- data.tar.gz: a351a443594be61d7759cf02242a42424e2048a520aa2cac19208d533b358b50d33b9f09d37a1e3dbf32218f95d8d797b2ec19915dd9215d81b3a4be12820fec
6
+ metadata.gz: a52fc1017a3b72de35f702ecdee98e467b210b2d32dd236a7741a73a53874ddabf8b8711e335668f08e0321d1caa135a5519bcb13b402d219e4ddfc1c50ba45c
7
+ data.tar.gz: 7dd1646fc98747735c9511d96ef08a859834897781daa9f9029a6902ecb021f9972d7432ce635875e126666eded3ec4d440d5d50d3d2c078db65e9017a4866e0
@@ -1,16 +1,22 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- react.rb (0.0.1)
4
+ react.rb (0.1.0)
5
5
  opal (~> 0.6.0)
6
6
  opal-activesupport
7
+ sprockets-es6
8
+ therubyracer
7
9
 
8
10
  GEM
9
11
  remote: https://rubygems.org/
10
12
  specs:
11
- hike (1.2.3)
13
+ babel-source (4.7.16)
14
+ babel-transpiler (0.6.0)
15
+ babel-source (>= 4.0, < 5)
16
+ execjs (~> 2.0)
17
+ execjs (2.4.0)
12
18
  json (1.8.2)
13
- multi_json (1.10.1)
19
+ libv8 (3.16.14.7)
14
20
  opal (0.6.3)
15
21
  source_map
16
22
  sprockets
@@ -24,17 +30,21 @@ GEM
24
30
  rack-protection (1.5.3)
25
31
  rack
26
32
  react-source (0.12.2)
33
+ ref (1.0.5)
27
34
  sinatra (1.4.5)
28
35
  rack (~> 1.4)
29
36
  rack-protection (~> 1.4)
30
37
  tilt (~> 1.3, >= 1.3.4)
31
38
  source_map (3.0.1)
32
39
  json
33
- sprockets (2.12.3)
34
- hike (~> 1.2)
35
- multi_json (~> 1.0)
40
+ sprockets (3.0.0.beta.10)
36
41
  rack (~> 1.0)
37
- tilt (~> 1.1, != 1.3.0)
42
+ sprockets-es6 (0.6.0)
43
+ babel-transpiler
44
+ sprockets (~> 3.0.0.beta)
45
+ therubyracer (0.12.1)
46
+ libv8 (~> 3.16.14.0)
47
+ ref
38
48
  tilt (1.4.1)
39
49
 
40
50
  PLATFORMS
data/README.md CHANGED
@@ -1,8 +1,11 @@
1
1
  # React.rb
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/react.rb.svg)](http://badge.fury.io/rb/react.rb)
4
+ [![Code Climate](https://codeclimate.com/github/zetachang/react.rb/badges/gpa.svg)](https://codeclimate.com/github/zetachang/react.rb)
5
+
3
6
  **React.rb is an [Opal Ruby](http://opalrb.org) wrapper of [React.js library](http://facebook.github.io/react/)**.
4
7
 
5
- It let you write reactive UI component with Ruby's elegancy and compiled to run in Javascript. :heart:
8
+ It lets you write reactive UI components, with Ruby's elegance and compiled to run in JavaScript. :heart:
6
9
 
7
10
  ## Installation
8
11
 
@@ -115,6 +118,51 @@ React.render(React.create_element(App), `document.body`)
115
118
  * Use helper method `define_state` to create setter & getter of `this.state` for you
116
119
  * For the detailed mapping to the original API, see [this issue](https://github.com/zetachang/react.rb/issues/2) for reference. Complete reference will come soon.
117
120
 
121
+ ### Element Building DSL
122
+
123
+ As a replacement of JSX, include `React::Component` and you can build `React.Element` hierarchy without all the `React.create_element` noises.
124
+
125
+ ```ruby
126
+ def render
127
+ div do
128
+ h1 { "Title" }
129
+ h2 { "subtitle"}
130
+ div(class_name: 'fancy', id: 'foo') { span { "some text #{interpolation}"} }
131
+ present FancyElement, fancy_props: '10'
132
+ end
133
+ end
134
+ ```
135
+
136
+ ## JSX Support
137
+
138
+ Not a fan of using element building DSL? Use file extension `.jsx.rb` to get JSX fragment compiled.
139
+
140
+ ```ruby
141
+ # app.jsx.rb
142
+ class Fancy
143
+ def render
144
+ React.create_element('div') { "fancy" }
145
+ end
146
+ end
147
+
148
+ class App
149
+ include React::Component
150
+
151
+ def render
152
+ jsx(%x{
153
+ <div>
154
+ <h1>Outer</h1>
155
+ <Fancy>{ #{5.times.to_a.join(",")} }</Fancy>
156
+ </div>
157
+ })
158
+ end
159
+ end
160
+
161
+ React.expose_native_class(Fancy)
162
+
163
+ React.render React.create_element(App), `document.body`
164
+ ```
165
+
118
166
  ### Props validation
119
167
 
120
168
  How about props validation? Inspired by [Grape API](https://github.com/intridea/grape), props validation rule could be created easily through `params` class method as below,
@@ -190,6 +238,7 @@ end
190
238
 
191
239
  * React Tutorial: see [example/react-tutorial](example/react-tutorial), the original CommentBox example.
192
240
  * TodoMVC: see [example/todos](example/todos), your beloved TodoMVC <3.
241
+ * JSX Example: see [example/basic-jsx](example/basic-jsx).
193
242
 
194
243
  ## TODOS
195
244
 
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'react.rb', :path => '../..'
4
+ gem 'sinatra'
5
+ gem 'react-source'
6
+ gem 'opal-browser'
@@ -0,0 +1,57 @@
1
+ PATH
2
+ remote: ../..
3
+ specs:
4
+ react.rb (0.0.2)
5
+ opal (~> 0.6.0)
6
+ opal-activesupport
7
+ sprockets-es6
8
+ therubyracer
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ babel-source (4.7.3)
14
+ babel-transpiler (0.6.0)
15
+ babel-source (>= 4.0, < 5)
16
+ execjs (~> 2.0)
17
+ execjs (2.4.0)
18
+ json (1.8.2)
19
+ libv8 (3.16.14.7)
20
+ opal (0.6.3)
21
+ source_map
22
+ sprockets
23
+ opal-activesupport (0.1.0)
24
+ opal (>= 0.5.0, < 1.0.0)
25
+ opal-browser (0.2.0.beta1)
26
+ opal (~> 0.6.0)
27
+ paggio
28
+ paggio (0.2.4)
29
+ rack (1.6.0)
30
+ rack-protection (1.5.3)
31
+ rack
32
+ react-source (0.12.2)
33
+ ref (1.0.5)
34
+ sinatra (1.4.5)
35
+ rack (~> 1.4)
36
+ rack-protection (~> 1.4)
37
+ tilt (~> 1.3, >= 1.3.4)
38
+ source_map (3.0.1)
39
+ json
40
+ sprockets (3.0.0.beta.8)
41
+ rack (~> 1.0)
42
+ sprockets-es6 (0.6.0)
43
+ babel-transpiler
44
+ sprockets (~> 3.0.0.beta)
45
+ therubyracer (0.12.1)
46
+ libv8 (~> 3.16.14.0)
47
+ ref
48
+ tilt (1.4.1)
49
+
50
+ PLATFORMS
51
+ ruby
52
+
53
+ DEPENDENCIES
54
+ opal-browser
55
+ react-source
56
+ react.rb!
57
+ sinatra
@@ -0,0 +1,38 @@
1
+ # config.ru
2
+ require 'bundler'
3
+ Bundler.require
4
+
5
+ Opal::Processor.source_map_enabled = true
6
+
7
+ opal = Opal::Server.new {|s|
8
+ s.append_path './'
9
+ s.append_path File.dirname(::React::Source.bundled_path_for("react-with-addons.js"))
10
+ s.main = 'example'
11
+ s.debug = true
12
+ }
13
+
14
+ map opal.source_maps.prefix do
15
+ run opal.source_maps
16
+ end
17
+
18
+ map '/assets' do
19
+ run opal.sprockets
20
+ end
21
+
22
+ get '/' do
23
+ <<-HTML
24
+ <!doctype html>
25
+ <html>
26
+ <head>
27
+ <title>Hello React</title>
28
+ <script src="/assets/react-with-addons.js"></script>
29
+ </head>
30
+ <body>
31
+ <div id="container"></div>
32
+ <script src="/assets/example.js"></script>
33
+ </body>
34
+ </html>
35
+ HTML
36
+ end
37
+
38
+ run Sinatra::Application
@@ -0,0 +1,35 @@
1
+ require "opal"
2
+ require "react"
3
+ require "browser"
4
+
5
+ class Clock
6
+ include React::Component
7
+
8
+ def render
9
+ message = "React has been successfully running for #{params[:elapsed].round} seconds."
10
+
11
+ jsx %x{
12
+ <p>{#{message}}</p>
13
+ }
14
+ end
15
+ end
16
+
17
+ class ExampleApp
18
+ include React::Component
19
+
20
+ def render
21
+ jsx(%x{
22
+ <Clock elapsed={#{params[:elapsed]}} />
23
+ })
24
+ end
25
+ end
26
+
27
+ React.expose_native_class(Clock, ExampleApp)
28
+
29
+ start = Time.now
30
+
31
+ $window.every(0.05) do
32
+ element = React::Element.new(`<ExampleApp elapsed={#{Time.now - start}}/>`)
33
+ container = `document.getElementById('container')`
34
+ React.render element, container
35
+ end
@@ -7,9 +7,11 @@ if RUBY_ENGINE == 'opal'
7
7
  require "react/api"
8
8
  require "react/validator"
9
9
  else
10
+ require "tilt"
10
11
  require "opal"
11
12
  require "react/version"
12
13
  require "opal-activesupport"
14
+ require "react/ext/jsx_support"
13
15
 
14
16
  Opal.append_path File.expand_path('../', __FILE__).untaint
15
17
  Opal.append_path File.expand_path('../../vendor', __FILE__).untaint
@@ -8,60 +8,7 @@ module React
8
8
  # Component Spec or Nomral DOM
9
9
  if type.kind_of?(Class)
10
10
  raise "Provided class should define `render` method" if !(type.method_defined? :render)
11
- @@component_classes[type.to_s] ||= %x{
12
- React.createClass({
13
- propTypes: #{type.respond_to?(:prop_types) ? type.prop_types.to_n : `{}`},
14
- getDefaultProps: function(){
15
- return #{type.respond_to?(:default_props) ? type.default_props.to_n : `{}`};
16
- },
17
- getInitialState: function(){
18
- return #{type.respond_to?(:initial_state) ? type.initial_state.to_n : `{}`};
19
- },
20
- componentWillMount: function() {
21
- var instance = this._getOpalInstance.apply(this);
22
- return #{`instance`.component_will_mount if type.method_defined? :component_will_mount};
23
- },
24
- componentDidMount: function() {
25
- var instance = this._getOpalInstance.apply(this);
26
- return #{`instance`.component_did_mount if type.method_defined? :component_did_mount};
27
- },
28
- componentWillReceiveProps: function(next_props) {
29
- var instance = this._getOpalInstance.apply(this);
30
- return #{`instance`.component_will_receive_props(`next_props`) if type.method_defined? :component_will_receive_props};
31
- },
32
- shouldComponentUpdate: function(next_props, next_state) {
33
- var instance = this._getOpalInstance.apply(this);
34
- return #{`instance`.should_component_update?(`next_props`, `next_state`) if type.method_defined? :should_component_update?};
35
- },
36
- componentWillUpdate: function(next_props, next_state) {
37
- var instance = this._getOpalInstance.apply(this);
38
- return #{`instance`.component_will_update(`next_props`, `next_state`) if type.method_defined? :component_will_update};
39
- },
40
- componentDidUpdate: function(prev_props, prev_state) {
41
- var instance = this._getOpalInstance.apply(this);
42
- return #{`instance`.component_did_update(`prev_props`, `prev_state`) if type.method_defined? :component_did_update};
43
- },
44
- componentWillUnmount: function() {
45
- var instance = this._getOpalInstance.apply(this);
46
- return #{`instance`.component_will_unmount if type.method_defined? :component_will_unmount};
47
- },
48
- _getOpalInstance: function() {
49
- if (this.__opalInstance == undefined) {
50
- var instance = #{type.new(`this`)};
51
- } else {
52
- var instance = this.__opalInstance;
53
- }
54
- this.__opalInstance = instance;
55
- return instance;
56
- },
57
- render: function() {
58
- var instance = this._getOpalInstance.apply(this);
59
- return #{`instance`.render.to_n};
60
- }
61
- })
62
- }
63
-
64
- params << @@component_classes[type.to_s]
11
+ params << self.native_component_class(type)
65
12
  else
66
13
  raise "#{type} not implemented" unless HTML_TAGS.include?(type)
67
14
  params << type
@@ -71,7 +18,7 @@ module React
71
18
  props = {}
72
19
  properties.map do |key, value|
73
20
  if key == "class_name" && value.is_a?(Hash)
74
- props[lower_camelize(key)] = `React.addons.classSet(#{value.to_n})`
21
+ props[lower_camelize(key)] = value.inject([]) {|ary, (k,v)| v ? ary.push(k) : ary}.join(" ")
75
22
  else
76
23
  props[React::ATTRIBUTES.include?(lower_camelize(key)) ? lower_camelize(key) : key] = value
77
24
  end
@@ -92,6 +39,61 @@ module React
92
39
  @@component_classes = {}
93
40
  end
94
41
 
42
+ def self.native_component_class(type)
43
+ @@component_classes[type.to_s] ||= %x{
44
+ React.createClass({
45
+ propTypes: #{type.respond_to?(:prop_types) ? type.prop_types.to_n : `{}`},
46
+ getDefaultProps: function(){
47
+ return #{type.respond_to?(:default_props) ? type.default_props.to_n : `{}`};
48
+ },
49
+ getInitialState: function(){
50
+ return #{type.respond_to?(:initial_state) ? type.initial_state.to_n : `{}`};
51
+ },
52
+ componentWillMount: function() {
53
+ var instance = this._getOpalInstance.apply(this);
54
+ return #{`instance`.component_will_mount if type.method_defined? :component_will_mount};
55
+ },
56
+ componentDidMount: function() {
57
+ var instance = this._getOpalInstance.apply(this);
58
+ return #{`instance`.component_did_mount if type.method_defined? :component_did_mount};
59
+ },
60
+ componentWillReceiveProps: function(next_props) {
61
+ var instance = this._getOpalInstance.apply(this);
62
+ return #{`instance`.component_will_receive_props(`next_props`) if type.method_defined? :component_will_receive_props};
63
+ },
64
+ shouldComponentUpdate: function(next_props, next_state) {
65
+ var instance = this._getOpalInstance.apply(this);
66
+ return #{`instance`.should_component_update?(`next_props`, `next_state`) if type.method_defined? :should_component_update?};
67
+ },
68
+ componentWillUpdate: function(next_props, next_state) {
69
+ var instance = this._getOpalInstance.apply(this);
70
+ return #{`instance`.component_will_update(`next_props`, `next_state`) if type.method_defined? :component_will_update};
71
+ },
72
+ componentDidUpdate: function(prev_props, prev_state) {
73
+ var instance = this._getOpalInstance.apply(this);
74
+ return #{`instance`.component_did_update(`prev_props`, `prev_state`) if type.method_defined? :component_did_update};
75
+ },
76
+ componentWillUnmount: function() {
77
+ var instance = this._getOpalInstance.apply(this);
78
+ return #{`instance`.component_will_unmount if type.method_defined? :component_will_unmount};
79
+ },
80
+ _getOpalInstance: function() {
81
+ if (this.__opalInstance == undefined) {
82
+ var instance = #{type.new(`this`)};
83
+ } else {
84
+ var instance = this.__opalInstance;
85
+ }
86
+ this.__opalInstance = instance;
87
+ return instance;
88
+ },
89
+ render: function() {
90
+ var instance = this._getOpalInstance.apply(this);
91
+ return #{`instance`.render.to_n};
92
+ }
93
+ })
94
+ }
95
+ end
96
+
95
97
  private
96
98
 
97
99
  def self.lower_camelize(snake_cased_word)
@@ -77,6 +77,10 @@ module React
77
77
  end
78
78
  end
79
79
 
80
+ def jsx(native)
81
+ React::Element.new(native)
82
+ end
83
+
80
84
  def method_missing(name, *args, &block)
81
85
  unless (React::HTML_TAGS.include?(name) || name == 'present' || name == '_p_tag')
82
86
  return super
@@ -0,0 +1,16 @@
1
+ require "execjs"
2
+ require "sprockets"
3
+ require "sprockets/es6"
4
+
5
+ module ExecJS
6
+ class Runtime
7
+ alias_method :orig_compile, :compile
8
+ def compile(source)
9
+ context = orig_compile("var console = {error: function(){}, log: function(){}, warn: function(){}, info: function(){}};" + source)
10
+ context
11
+ end
12
+ end
13
+ end
14
+
15
+ Sprockets.register_mime_type 'text/jsx', extensions: ['.jsx']
16
+ Sprockets.register_transformer 'text/jsx', 'application/javascript', Sprockets::ES6.new('whitelist' => ['react'])
@@ -47,4 +47,10 @@ module React
47
47
  def self.unmount_component_at_node(node)
48
48
  `React.unmountComponentAtNode(node)`
49
49
  end
50
+
51
+ def self.expose_native_class(*args)
52
+ args.each do |klass|
53
+ `window[#{klass.to_s}] = #{React::API.native_component_class(klass)}`
54
+ end
55
+ end
50
56
  end
@@ -1,3 +1,3 @@
1
1
  module React
2
- VERSION = "0.0.2"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -18,6 +18,8 @@ Gem::Specification.new do |s|
18
18
 
19
19
  s.add_runtime_dependency 'opal', '~> 0.6.0'
20
20
  s.add_runtime_dependency 'opal-activesupport'
21
+ s.add_runtime_dependency 'sprockets-es6'
22
+ s.add_runtime_dependency 'therubyracer'
21
23
  s.add_development_dependency 'react-source', '~> 0.12'
22
24
  s.add_development_dependency 'opal-rspec', '~> 0.3.0.beta3'
23
25
  s.add_development_dependency 'sinatra'
@@ -594,4 +594,21 @@ describe React::Component do
594
594
  expect(component.mounted?).to eq(true)
595
595
  end
596
596
  end
597
+
598
+ describe "Helpers" do
599
+ describe "jsx" do
600
+ it "should wrap passed JS object to React::Element" do
601
+ stub_const 'Foo', Class.new
602
+ Foo.class_eval do
603
+ include React::Component
604
+
605
+ def foo
606
+ jsx(`React.createElement('div')`)
607
+ end
608
+ end
609
+
610
+ expect(Foo.new.foo).to be_kind_of(React::Element)
611
+ end
612
+ end
613
+ end
597
614
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: react.rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Chang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-25 00:00:00.000000000 Z
11
+ date: 2015-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: opal
@@ -38,6 +38,34 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sprockets-es6
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: therubyracer
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
41
69
  - !ruby/object:Gem::Dependency
42
70
  name: react-source
43
71
  requirement: !ruby/object:Gem::Requirement
@@ -107,6 +135,10 @@ files:
107
135
  - LICENSE
108
136
  - README.md
109
137
  - config.ru
138
+ - example/basic-jsx/Gemfile
139
+ - example/basic-jsx/Gemfile.lock
140
+ - example/basic-jsx/config.ru
141
+ - example/basic-jsx/example.jsx.rb
110
142
  - example/react-tutorial/Gemfile
111
143
  - example/react-tutorial/Gemfile.lock
112
144
  - example/react-tutorial/README.md
@@ -137,6 +169,7 @@ files:
137
169
  - lib/react/element.rb
138
170
  - lib/react/event.rb
139
171
  - lib/react/ext/hash.rb
172
+ - lib/react/ext/jsx_support.rb
140
173
  - lib/react/ext/string.rb
141
174
  - lib/react/top_level.rb
142
175
  - lib/react/validator.rb