react.rb 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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