reactive-ruby 0.7.3 → 0.7.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -1
  3. data/README.md +67 -229
  4. data/example/tutorial/.gitignore +17 -0
  5. data/example/tutorial/Gemfile +51 -0
  6. data/example/tutorial/Gemfile.lock +211 -0
  7. data/example/tutorial/Gemfile.xlock +211 -0
  8. data/example/tutorial/README.rdoc +28 -0
  9. data/example/tutorial/Rakefile +6 -0
  10. data/example/tutorial/app/assets/images/.keep +0 -0
  11. data/example/tutorial/app/assets/javascripts/application.rb +10 -0
  12. data/example/tutorial/app/assets/stylesheets/application.css +15 -0
  13. data/example/tutorial/app/controllers/application_controller.rb +6 -0
  14. data/example/tutorial/app/controllers/concerns/.keep +0 -0
  15. data/example/tutorial/app/controllers/home_controller.rb +6 -0
  16. data/example/tutorial/app/helpers/application_helper.rb +2 -0
  17. data/example/tutorial/app/mailers/.keep +0 -0
  18. data/example/tutorial/app/models/.keep +0 -0
  19. data/example/tutorial/app/models/concerns/.keep +0 -0
  20. data/example/tutorial/app/views/components.rb +3 -0
  21. data/example/tutorial/app/views/components/home/show.rb +18 -0
  22. data/example/tutorial/app/views/layouts/application.html.erb +14 -0
  23. data/example/tutorial/bin/bundle +3 -0
  24. data/example/tutorial/bin/rails +8 -0
  25. data/example/tutorial/bin/rake +8 -0
  26. data/example/tutorial/bin/setup +29 -0
  27. data/example/tutorial/bin/spring +15 -0
  28. data/example/tutorial/config.ru +4 -0
  29. data/example/tutorial/config/application.rb +26 -0
  30. data/example/tutorial/config/boot.rb +3 -0
  31. data/example/tutorial/config/database.yml +25 -0
  32. data/example/tutorial/config/environment.rb +5 -0
  33. data/example/tutorial/config/environments/development.rb +41 -0
  34. data/example/tutorial/config/environments/production.rb +79 -0
  35. data/example/tutorial/config/environments/test.rb +42 -0
  36. data/example/tutorial/config/initializers/assets.rb +11 -0
  37. data/example/tutorial/config/initializers/backtrace_silencers.rb +7 -0
  38. data/example/tutorial/config/initializers/cookies_serializer.rb +3 -0
  39. data/example/tutorial/config/initializers/filter_parameter_logging.rb +4 -0
  40. data/example/tutorial/config/initializers/inflections.rb +16 -0
  41. data/example/tutorial/config/initializers/mime_types.rb +4 -0
  42. data/example/tutorial/config/initializers/session_store.rb +3 -0
  43. data/example/tutorial/config/initializers/wrap_parameters.rb +14 -0
  44. data/example/tutorial/config/locales/en.yml +23 -0
  45. data/example/tutorial/config/routes.rb +59 -0
  46. data/example/tutorial/config/secrets.yml +22 -0
  47. data/example/tutorial/db/seeds.rb +7 -0
  48. data/example/tutorial/lib/assets/.keep +0 -0
  49. data/example/tutorial/lib/tasks/.keep +0 -0
  50. data/example/tutorial/log/.keep +0 -0
  51. data/example/tutorial/public/404.html +67 -0
  52. data/example/tutorial/public/422.html +67 -0
  53. data/example/tutorial/public/500.html +66 -0
  54. data/example/tutorial/public/favicon.ico +0 -0
  55. data/example/tutorial/public/robots.txt +5 -0
  56. data/example/tutorial/test/controllers/.keep +0 -0
  57. data/example/tutorial/test/fixtures/.keep +0 -0
  58. data/example/tutorial/test/helpers/.keep +0 -0
  59. data/example/tutorial/test/integration/.keep +0 -0
  60. data/example/tutorial/test/mailers/.keep +0 -0
  61. data/example/tutorial/test/models/.keep +0 -0
  62. data/example/tutorial/test/test_helper.rb +10 -0
  63. data/example/tutorial/vendor/assets/javascripts/.keep +0 -0
  64. data/example/tutorial/vendor/assets/stylesheets/.keep +0 -0
  65. data/lib/rails-helpers/react_component.rb +19 -1
  66. data/lib/reactive-ruby.rb +9 -0
  67. data/lib/reactive-ruby/version.rb +1 -1
  68. data/old-readme +220 -0
  69. data/reactive-ruby.gemspec +5 -2
  70. metadata +87 -12
  71. data/Gemfile.lock +0 -53
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4e9023f2e2533170c453fff7af6571e8c95976b9
4
- data.tar.gz: 74e2553bd21a054c5674f46f1ededa4d7b339093
3
+ metadata.gz: e6e7e4fc40600191b0985643a430eca77bbc7816
4
+ data.tar.gz: 35c09c919ba8ceaabd2165960fcc963a4f2c57c1
5
5
  SHA512:
6
- metadata.gz: 341472a072828b2af26cb27ddfe4dfb2ef85dc1dea31af2df3a2fcdab8fc362a7d4b438afe605f89f71d20c5dbe4e2280e1e56c0b03f469f825b8945309530cf
7
- data.tar.gz: 91e7a1556c9a53c5576c17194d0b416bbae63781e7180a802cfccec4fb8cf8ba409eed68ae208cc79ce83e1b76f6d52d6ba79bb8ecafc6d349682c29a2b339f4
6
+ metadata.gz: 6cc80309ecd3a8115eeef622a7f61e96188fc2e52e3e3b03ba6c5b6d672ccdd2588db5100894fb4763dd38e74f31737d76ff586337d7c65db0c2f92b63f2d7a2
7
+ data.tar.gz: b1321b290e26ca408b04143d0a3f29e4e0d5efd33b119224eeb9c2bdffc5d05410652cdd9e3cec0b4643faf08d6a6a0f1b957091d619d5bc63f4cd0ef2a94d83
data/Gemfile CHANGED
@@ -1,3 +1,2 @@
1
1
  source 'https://rubygems.org'
2
2
  gemspec
3
- gem 'opal', :git => "https://github.com/catprintlabs/opal.git"
data/README.md CHANGED
@@ -1,302 +1,140 @@
1
- # React.rb
1
+ # Reactive-Ruby
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)
3
+ **Reactive-Ruby is an [Opal Ruby](http://opalrb.org) wrapper of [React.js library](http://facebook.github.io/react/)**.
5
4
 
6
- **React.rb is an [Opal Ruby](http://opalrb.org) wrapper of [React.js library](http://facebook.github.io/react/)**.
5
+ It lets you write reactive UI components, with Ruby's elegance using the tried and true React.js engine. :heart:
7
6
 
8
- It lets you write reactive UI components, with Ruby's elegance and compiled to run in JavaScript. :heart:
7
+ This fork of the original react.rb gem is a work in progress. Currently it is being used in a large rails app, so until a better guide can be written we will just assume you will use it a rails app too. However the gem itself has no dependency on rails, and there are people using the gem in other environments.
9
8
 
10
- ## Installation
9
+ ## Quick Overview
11
10
 
12
- Currently this branch (0.8 in catprint labs) is being used with the following configuration.
13
- It is suggested to begin with this
14
- set of gems which is known to work and then add / remove them as needed. Let us know if you discover anything.
11
+ A react app is built from one or more trees of components. A react component can live side by side with other non-react html. A react component is just like a rails view or a partial. Reactive-Ruby takes advantage of this by letting you add Reactive-Ruby components as views, and call them directly from your controller like any other view.
15
12
 
16
- Currently this has been tested to some extent with rails 3x and 4x
13
+ Components are first rendered to HTML on the server (called pre-rendering) this is no different from what happens when your ERB or HAML templates are translated to HTML.
17
14
 
18
- ```ruby
19
- gem 'react-rails', git: "https://github.com/catprintlabs/react-rails.git", :branch => 'isomorphic-methods-support' # you need this branch of react-rails
20
- gem 'opal', git: "https://github.com/catprintlabs/opal.git"
21
- gem 'opal-jquery', git: "https://github.com/catprintlabs/opal-jquery.git" # optional if you are using jquery
22
- gem 'opal-rails' # not sure if you need this in all cases
23
- gem 'opal-browser' # gets you things like intervals (every method)
24
- gem 'opal-react', git: "https://github.com/catprintlabs/react.rb.git", :branch => 'opal-0.8'
25
- gem 'reactive_record', git: "https://github.com/catprintlabs/reactive-record.git", :branch => 'rails4' # if you want to interface to active_record
26
- gem 'react-bootstrap-rails' # if you want to use boot strap styles
27
- gem 'react-router-rails', '~>0.13.3' # if you want a single page app router
28
- gem 'reactor-router', git: "https://github.com/catprintlabs/reactor-router.git" # same as above
29
- ```
15
+ A copy of the react engine, and your components follows the rendered HTML to the browser, and then when a user interacts with the page, it is updated on the client.
30
16
 
31
- In addition
17
+ The beauty is you now have one markup description, written in the same language as your server code, that works both as the HTML template and as an interactive component.
32
18
 
33
- ```ruby
34
- # Gemfile
35
- # gem 'react-rails', git: "https://github.com/catprintlabs/react-rails.git" # include if you want integration with rails
36
- gem 'opal'
19
+ By design Reactive-Ruby allows reactive components to be easily added to existing Rails projects, as well in new development.
37
20
 
38
- # gem 'opal', git: "https://github.com/catprintlabs/opal.git" # use this if you are stuck on rails 3.x
39
- # gem 'opal-jquery', git: "https://github.com/catprintlabs/opal-jquery.git" # same as above
40
21
 
41
- # include if you want integration with rails
42
- # gem 'opal-rails'
22
+ ## Installation and setup
43
23
 
44
- # while not absolutely necessary you will probably want this at least for timers and such
45
- # gem 'opal-browser'
24
+ In your gem file:
46
25
 
47
- gem 'opal-react', git: "https://github.com/catprintlabs/react.rb.git", :branch => 'opal-0.8'
26
+ ```ruby
27
+ gem 'reactive-ruby'
48
28
 
49
- # access active record models from opal!
50
- # gem 'reactive_record', git: "https://github.com/catprintlabs/reactive-record.git"
29
+ # the next three gems are for integration with rails (TODO - package these up as a reactive-rails gem)
51
30
 
52
- # include if you want to use bootstrap
53
- # gem 'react-bootstrap-rails'
31
+ gem 'therubyracer', platforms: :ruby # you need this for prerendering to work
32
+ gem 'react-rails', git: "https://github.com/catprintlabs/react-rails.git", :branch => 'isomorphic-methods-support'
33
+ gem 'opal-rails'
54
34
  ```
55
35
 
56
- and in your Opal application,
36
+ Your react components will go into the `app/views/components/` directory of your rails app.
57
37
 
58
- ```ruby
59
- require "opal-react"
60
- require "react"
38
+ In addition within your views directory you need a `components.rb` manifest file like this:
61
39
 
62
- React.render(React.create_element('h1'){ "Hello World!" }, `document.body`)
63
40
  ```
64
-
65
- For a complete example covering most key features, as well as integration with a server (Sinatra, etc), see setup of [Examples](example/tutorial). For additional information on integrating Opal with a server see the [official docs](http://opalrb.org/docs/) of Opal.
66
-
67
- ## React Overview
68
-
69
- ### Basics
70
-
71
- The biggest problem with react is that its almost too simple.
72
-
73
- In react you define components. Components are simply classes that have a "render" method. The render method "draws" a chunk of
74
- HTML.
75
-
76
- Here is a very simple component:
77
-
78
- ```ruby
79
-
41
+ # app/views/components.rb
80
42
  require 'opal'
81
- require 'opal-react'
82
-
83
- class Hello
84
- def render
85
- "hello world"
86
- end
87
- end
88
-
89
- # to use the component we first create an instance o
90
-
91
- Include the `React::Component` mixin in a class to turn it into a react component
92
-
93
- ```ruby
94
- require 'opal'
95
- require 'opal-react'
96
-
97
- class HelloMessage
98
-
99
- include React::Component # will create a new component named HelloMessage
100
-
101
- MSG = {great: 'Cool!', bad: 'Cheer up!'}
102
-
103
- optional_param :mood
104
- required_param :name
105
- define_state :foo, "Default greeting"
106
-
107
- before_mount do # you can define life cycle callbacks inline
108
- foo! "#{name}: #{MSG[mood]}" if mood # change the state of foo using foo!, read the state using foo
109
- end
110
-
111
- after_mount :log # you can also define life cycle callbacks by reference to a method
112
-
113
- def log
114
- puts "mounted!"
115
- end
116
-
117
- def render # render method MUST return just one component
118
- div do # basic dsl syntax component_name(options) { ...children... }
119
- span { "#{foo} #{name}!" } # all html5 components are defined with lower case text
120
- end
121
- end
122
- end
123
-
124
- class App
125
- include React::Component
126
-
127
- def render
128
- HelloMessage name: 'John', mood: :great # new components are accessed via the class name
129
- end
130
- end
43
+ require 'reactive-ruby'
44
+ require_tree './components'
45
+ ```
131
46
 
132
- # later we will talk about nicer ways to do this: For now wait till doc is loaded
133
- # then tell React to create an "App" and render it into the document body.
47
+ This pulls in the files that will be used both for server side and browser rendering.
134
48
 
135
- `window.onload = #{lambda {React.render(React.create_element(App), `document.body`)}}`
49
+ Then your `assets/javascript/application.rb` file looks like this:
136
50
 
137
- # -> console says: mounted!
138
51
  ```
139
-
140
- * Callback of life cycle could be created through helpers `before_mount`, `after_mount`, etc
141
- * `this.props` is accessed through method `self.params`
142
- * Use helper method `define_state` to create setter & getter of `this.state` for you
143
- * 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.
144
-
145
- ### Element Building DSL
146
-
147
- As a replacement of JSX, include `React::Component` and you can build `React.Element` hierarchy without all the `React.create_element` noises.
148
-
149
- ```ruby
150
- def render
151
- div do
152
- h1 { "Title" }
153
- h2 { "subtitle"}
154
- div(class_name: 'fancy', id: 'foo') { span { "some text #{interpolation}"} }
155
- present FancyElement, fancy_props: '10'
156
- end
157
- end
52
+ #assets/javascript/application.rb
53
+ # only put files that are browser side only.
54
+
55
+ require 'components' # this pulls in your components from the components.rb manifest file
56
+ require 'jquery' # you need both these files to access jQuery from Opal
57
+ require 'opal-jquery' # they must be in this order, and after the components require
58
+ require 'browser' # opal access to browser specific methods (such as setTimer)
59
+ require 'react_ujs' # this is required and is part of the prerendering system
60
+ # whatever else you might need here
158
61
  ```
159
62
 
160
- ### Props validation
63
+ Okay that is your setup.
161
64
 
162
- 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,
65
+ Now for a simple component. We are going to render this from the `show` method of the home controller. We want to use convention over configuration so by default. So the component will be the "Show" class, of the "Home" module,
66
+ of the Components module.
163
67
 
164
68
  ```ruby
165
- class App
166
- include React::Component
167
-
168
- params do
169
- requires :username, type: String
170
- requires :enum, values: ['foo', 'bar', 'awesome']
171
- requires :payload, type: Todo # yeah, a plain Ruby class
172
- optional :filters, type: Array[String]
173
- optional :flash_message, type: String, default: 'Welcome!' # no need to feed through `getDefaultProps`
174
- end
69
+ # app/views/components/home.rb
175
70
 
176
- def render
177
- div
178
- end
179
- end
180
- ```
71
+ module Components
72
+ module Home
73
+ class Show
181
74
 
182
- ### Mixins
75
+ include React::Component # will create a new component named Home
183
76
 
184
- Simply create a Ruby module to encapsulate the behavior. Example below is modified from the original [React.js Exmaple on Mixin](http://facebook.github.io/react/docs/reusable-components.html#mixins). [Opal Browser](https://github.com/opal/opal-browser) syntax are used here to make it cleaner.
77
+ export_component # export the component name into the javascript space
185
78
 
186
- ```ruby
187
- module SetInterval
188
- def self.included(base)
189
- base.class_eval do
190
- before_mount { @interval = [] }
191
- before_unmount do
192
- # abort associated timer of a component right before unmount
193
- @interval.each { |i| i.abort }
79
+ def render
80
+ puts "Rendering my first component!"
81
+ "hello" # render "hello"
194
82
  end
195
- end
196
- end
197
-
198
- def set_interval(seconds, &block)
199
- @interval << $window.every(seconds, &block)
200
- end
201
- end
202
-
203
- class TickTock
204
- include React::Component
205
- include SetInterval
206
83
 
207
- define_state(:seconds) { 0 }
208
-
209
- before_mount do
210
- set_interval(1) { self.seconds = self.seconds + 1 }
211
- set_interval(1) { puts "Tick!" }
212
- end
213
-
214
- def render
215
- span do
216
- "React has been running for: #{self.seconds}"
217
84
  end
218
85
  end
219
86
  end
220
-
221
- React.render(React.create_element(TickTock), $document.body.to_n)
222
-
223
- $window.after(5) do
224
- React.unmount_component_at_node($document.body.to_n)
225
- end
226
-
227
- # => Tick!
228
- # => ... for 5 times then stop ticking after 5 seconds
229
87
  ```
230
88
 
231
-
232
- ### A Simple Component
233
-
234
- A ruby class which define method `render` is a valid component.
235
-
89
+ Components work just like views so put this in your home controller
236
90
  ```ruby
237
- class HelloMessage
238
- def render
239
- React.create_element("div") { "Hello World!" }
91
+ # controllers/home_controller.rb
92
+ class HomeController < ApplicationController
93
+ def show
94
+ render_component # by default render_component will use the controller name to find the appropriate component
240
95
  end
241
96
  end
242
-
243
- puts React.render_to_static_markup(React.create_element(HelloMessage))
244
-
245
- # => '<div>Hello World!</div>'
246
97
  ```
247
98
 
248
- ### More complicated one
99
+ Make sure your routes file has a route to your home#show method, and you have done a bundle install. Fire up your development server and you should see "hello world" displayed.
249
100
 
250
- To hook into native ReactComponent life cycle, the native `this` will be passed to the class's initializer. And all corresponding life cycle methods (`componentDidMount`, etc) will be invoked on the instance using the snake-case method name.
101
+ Open up the js console in the browser and you will see a log showing what went on during the rendering.
251
102
 
252
- ```ruby
253
- class HelloMessage
254
- def initialize(native)
255
- @native = Native(native)
256
- end
103
+ Have a look at the sources in the console, and notice your ruby code is there, and you can set break points etc.
257
104
 
258
- def component_will_mount
259
- puts "will mount!"
260
- end
105
+ ## Typical Problems
261
106
 
262
- def render
263
- React.create_element("div") { "Hello #{@native[:props][:name]}!" }
264
- end
265
- end
107
+ `Uncaught TypeError: Cannot read property 'toUpperCase' of undefined` This means the thing you are trying to render is not actually a react component. Often is because the top level component name is wrong. For example if you are in controller Foo and the method is `bar`, but you have named the component Foo::Bars then you would see this message.
266
108
 
267
- puts React.render_to_static_markup(React.create_element(HelloMessage, name: 'John'))
109
+ ## Turning off Prerendering
268
110
 
269
- # => will_mount!
270
- # => '<div>Hello John!</div>'
271
- ```
272
- ## Example
111
+ Sometimes its handy to switch off prerendering. Add `?no_prerender=1` ... to your url.
273
112
 
274
- * React Tutorial: see [example/react-tutorial](example/react-tutorial), the original CommentBox example.
275
- * TodoMVC: see [example/todos](example/todos), your beloved TodoMVC <3.
276
113
 
277
- ## TODOS
114
+ ## TODOS / Work arounds / Issues
278
115
 
279
116
  * Documentation
280
- * API wrapping coverage of the original js library (pretty close though)
281
- * React Native?
117
+ * Should load the RubyRacer, or at least report an error if the RubyRacer is not present
118
+ * Get everything to autoload what it needs (i.e. much less config setup)
282
119
 
283
120
  ## Developing
284
121
 
285
- To run the test case of the project yourself.
122
+ To run the above examples project yourself:
286
123
 
287
124
  1. `git clone` the project
125
+ 2. `cd example/tutorial`
288
126
  2. `bundle install`
289
127
  3. `bundle exec rackup`
290
- 4. Open `http://localhost:9292` to run the spec
128
+ 4. Open `http://localhost`
291
129
 
292
130
  ## Contributions
293
131
 
294
132
  This project is still in early stage, so discussion, bug report and PR are really welcome :wink:.
133
+ We check in often at https://gitter.im/zetachang/react.rb ask for @catmando as David is on leave right now.
295
134
 
296
135
  ## Contact
297
136
 
298
- [David Chang](http://github.com/zetachang)
299
- [@zetachang](https://twitter.com/zetachang)
137
+ We check in often at https://gitter.im/zetachang/react.rb ask for @catmando.
300
138
 
301
139
  ## License
302
140
 
@@ -0,0 +1,17 @@
1
+ # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2
+ #
3
+ # If you find yourself ignoring temporary files generated by your text editor
4
+ # or operating system, you probably want to add a global ignore instead:
5
+ # git config --global core.excludesfile '~/.gitignore_global'
6
+
7
+ # Ignore bundler config.
8
+ /.bundle
9
+
10
+ # Ignore the default SQLite database.
11
+ /db/*.sqlite3
12
+ /db/*.sqlite3-journal
13
+
14
+ # Ignore all logfiles and tempfiles.
15
+ /log/*
16
+ !/log/.keep
17
+ /tmp
@@ -0,0 +1,51 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'therubyracer', platforms: :ruby
4
+ gem 'react-rails', git: "https://github.com/catprintlabs/react-rails.git", :branch => 'isomorphic-methods-support' # you need this branch of react-rails
5
+ gem 'opal-rails'
6
+ # integrates opal with sprockets etc
7
+ gem 'reactive-ruby', path: "../.."
8
+
9
+ # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
10
+ gem 'rails', '4.2.3'
11
+ # Use sqlite3 as the database for Active Record
12
+ gem 'sqlite3'
13
+ # Use SCSS for stylesheets
14
+ gem 'sass-rails', '~> 5.0'
15
+ # Use Uglifier as compressor for JavaScript assets
16
+ gem 'uglifier', '>= 1.3.0'
17
+ # Use CoffeeScript for .coffee assets and views
18
+ gem 'coffee-rails', '~> 4.1.0'
19
+ # See https://github.com/rails/execjs#readme for more supported runtimes
20
+ # gem 'therubyracer', platforms: :ruby
21
+
22
+ # Use jquery as the JavaScript library
23
+ gem 'jquery-rails'
24
+ # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
25
+ gem 'turbolinks'
26
+ # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
27
+ gem 'jbuilder', '~> 2.0'
28
+ # bundle exec rake doc:rails generates the API under doc/api.
29
+ gem 'sdoc', '~> 0.4.0', group: :doc
30
+
31
+ # Use ActiveModel has_secure_password
32
+ # gem 'bcrypt', '~> 3.1.7'
33
+
34
+ # Use Unicorn as the app server
35
+ # gem 'unicorn'
36
+
37
+ # Use Capistrano for deployment
38
+ # gem 'capistrano-rails', group: :development
39
+
40
+
41
+ group :development, :test do
42
+ # Call 'byebug' anywhere in the code to stop execution and get a debugger console
43
+ gem 'byebug'
44
+
45
+ # Access an IRB console on exception pages or by using <%= console %> in views
46
+ gem 'web-console', '~> 2.0'
47
+
48
+ # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
49
+ gem 'spring'
50
+ end
51
+