reactive-ruby 0.7.28 → 0.7.29
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 +4 -4
- data/.codeclimate.yml +6 -0
- data/Gemfile.lock +4 -1
- data/README.md +132 -68
- data/Rakefile +5 -2
- data/example/examples/Gemfile +0 -2
- data/example/rails-tutorial/Gemfile +3 -2
- data/lib/generators/reactive_ruby/test_app/templates/application.rb +11 -0
- data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/application.rb +2 -0
- data/lib/generators/reactive_ruby/test_app/templates/assets/javascripts/components.rb +3 -0
- data/lib/generators/reactive_ruby/test_app/templates/boot.rb +6 -0
- data/lib/generators/reactive_ruby/test_app/templates/script/rails +5 -0
- data/lib/generators/reactive_ruby/test_app/templates/views/components/hello_world.rb +11 -0
- data/lib/generators/reactive_ruby/test_app/templates/views/components/todo.rb +14 -0
- data/lib/generators/reactive_ruby/test_app/test_app_generator.rb +105 -0
- data/lib/rails-helpers/top_level_rails_component.rb +9 -16
- data/lib/{reactive-ruby → react}/api.rb +8 -65
- data/lib/{reactive-ruby → react}/callbacks.rb +0 -0
- data/lib/react/component.rb +266 -0
- data/lib/react/component/api.rb +48 -0
- data/lib/react/component/class_methods.rb +183 -0
- data/lib/{reactive-ruby → react}/element.rb +10 -11
- data/lib/{reactive-ruby → react}/event.rb +0 -0
- data/lib/{reactive-ruby → react}/ext/hash.rb +0 -0
- data/lib/{reactive-ruby → react}/ext/string.rb +0 -0
- data/lib/react/native_library.rb +57 -0
- data/lib/{reactive-ruby → react}/observable.rb +0 -4
- data/lib/{reactive-ruby → react}/rendering_context.rb +0 -6
- data/lib/{reactive-ruby → react}/state.rb +3 -6
- data/lib/{reactive-ruby → react}/top_level.rb +2 -3
- data/lib/react/validator.rb +127 -0
- data/lib/reactive-ruby.rb +20 -20
- data/lib/reactive-ruby/version.rb +1 -1
- data/{opal-spec/reactjs → spec}/index.html.erb +1 -1
- data/{opal-spec → spec/react}/callbacks_spec.rb +8 -9
- data/{opal-spec → spec/react}/component_spec.rb +170 -120
- data/spec/react/dsl_spec.rb +16 -0
- data/{opal-spec → spec/react}/element_spec.rb +7 -20
- data/{opal-spec → spec/react}/event_spec.rb +3 -1
- data/spec/react/native_library_spec.rb +10 -0
- data/spec/react/param_declaration_spec.rb +18 -0
- data/{opal-spec → spec/react}/react_spec.rb +3 -2
- data/spec/react/react_state_spec.rb +22 -0
- data/spec/react/top_level_component_spec.rb +68 -0
- data/{opal-spec → spec/react}/tutorial/tutorial_spec.rb +11 -13
- data/{opal-spec → spec/react}/validator_spec.rb +50 -4
- data/spec/reactive-ruby/isomorphic_helpers_spec.rb +22 -4
- data/spec/spec_helper.rb +51 -0
- data/spec/support/react/spec_helpers.rb +57 -0
- data/spec/vendor/es5-shim.min.js +6 -0
- metadata +56 -24
- data/lib/reactive-ruby/component.rb +0 -502
- data/lib/reactive-ruby/validator.rb +0 -99
- data/old-readme +0 -220
- data/opal-spec/spec_helper.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 527fdef11e277159a4a1d5ad88da39bfbd35e62a
|
4
|
+
data.tar.gz: 9e6fde62b7bebb1e8aaec07afcc403e0a0392d2b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc02bce0e1e5517ae2f8c1dffbce49369185caa17d643d33adbebd7c543adc67687953e26dcd99f74ca1da907a7858224541b250fd68c75db797bcdcad282765
|
7
|
+
data.tar.gz: de428600bfbd14dda2ab4afddc171607de11a6c12dc4306c7cdc833e1c3d7a8ba4c196e7487e0e08ef93b1132b495757f0107cdb1d61417e736b83cd0562ff28
|
data/.codeclimate.yml
ADDED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,151 +1,212 @@
|
|
1
|
-
# Reactive-Ruby
|
1
|
+
# React.rb / Reactive-Ruby
|
2
2
|
|
3
|
-
[](https://gitter.im/zetachang/react.rb?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
4
|
+
[](https://travis-ci.org/zetachang/react.rb)
|
5
|
+
[](https://codeclimate.com/github/zetachang/react.rb)
|
4
6
|
|
5
|
-
**
|
7
|
+
**React.rb is an [Opal Ruby](http://opalrb.org) wrapper of
|
8
|
+
[React.js library](http://facebook.github.io/react/)**.
|
6
9
|
|
7
|
-
It lets you write reactive UI components, with Ruby's elegance using the tried
|
10
|
+
It lets you write reactive UI components, with Ruby's elegance using the tried
|
11
|
+
and true React.js engine. :heart:
|
8
12
|
|
9
|
-
|
13
|
+
### What's this Reactive Ruby?
|
10
14
|
|
11
|
-
|
15
|
+
Reactive Ruby started as a fork of the original react.rb gem, and has since been
|
16
|
+
merged back into react.rb's master branch. It aims to take react.rb a few steps
|
17
|
+
further by embracing it's 'Ruby-ness'.
|
12
18
|
|
13
|
-
|
19
|
+
Reactive-Ruby is maturing, but is still a work in progress. Currently it is
|
20
|
+
being used in a large rails app. However the gem itself has no dependency on
|
21
|
+
rails, and there are people using the gem in other environments.
|
14
22
|
|
15
|
-
|
23
|
+
Stable react.rb can be found in the
|
24
|
+
[0-3-stable](https://github.com/zetachang/react.rb/tree/0-3-stable) branch.
|
16
25
|
|
17
|
-
|
26
|
+
## Quick Overview
|
18
27
|
|
19
|
-
A
|
28
|
+
A react app is built from one or more trees of components. React components can
|
29
|
+
live side by side with other non-react html and javascript. A react component is
|
30
|
+
just like a rails view or a partial. Reactive-Ruby takes advantage of these
|
31
|
+
features by letting you add Reactive-Ruby components as views, and call them
|
32
|
+
directly from your controller like any other view.
|
20
33
|
|
21
|
-
|
34
|
+
By design Reactive-Ruby allows reactive components to be easily added to
|
35
|
+
existing Rails projects, as well in new development.
|
22
36
|
|
23
|
-
|
37
|
+
Components are first rendered to HTML on the server (called pre-rendering) this
|
38
|
+
is no different from what happens when your ERB or HAML templates are translated
|
39
|
+
to HTML.
|
24
40
|
|
25
|
-
|
41
|
+
A copy of the react engine, and your components follows the rendered HTML to the
|
42
|
+
browser, and then when a user interacts with the page, it is updated on the
|
43
|
+
client.
|
26
44
|
|
27
|
-
|
28
|
-
|
45
|
+
The beauty is you now have one markup description, written in the same language
|
46
|
+
as your server code, that works both as the HTML template and as an interactive
|
47
|
+
component.
|
29
48
|
|
30
|
-
|
49
|
+
See the [wiki](https://github.com/zetachang/react.rb/wiki) for more details.
|
31
50
|
|
32
|
-
|
33
|
-
gem 'react-rails', git: "https://github.com/catprintlabs/react-rails.git", :branch => 'isomorphic-methods-support'
|
34
|
-
gem 'opal-rails'
|
51
|
+
## Using React.rb with Rails
|
35
52
|
|
36
|
-
|
53
|
+
### Installation
|
37
54
|
|
38
|
-
|
55
|
+
In your Gemfile:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
gem 'reactive-ruby'
|
59
|
+
gem 'react-rails'
|
60
|
+
gem 'opal-rails'
|
61
|
+
gem 'therubyracer', platforms: :ruby # Required for prerendering
|
39
62
|
```
|
40
63
|
|
41
|
-
|
64
|
+
Run `bundle install` and restart your rails server.
|
42
65
|
|
43
|
-
|
66
|
+
### Both Client & Server Side Assets (Components)
|
67
|
+
|
68
|
+
Your react components will go into the `app/views/components/` directory of your
|
69
|
+
rails app.
|
70
|
+
|
71
|
+
Within your `app/views` directory you need to create a `components.rb` manifest.
|
72
|
+
Files required in `app/views/components.rb` will be made available to the server
|
73
|
+
side rendering system as well as the browser.
|
44
74
|
|
45
75
|
```
|
46
76
|
# app/views/components.rb
|
47
77
|
require 'opal'
|
48
78
|
require 'reactive-ruby'
|
49
79
|
require_tree './components'
|
50
|
-
```
|
80
|
+
```
|
51
81
|
|
52
|
-
|
82
|
+
### Client Side Assets
|
53
83
|
|
54
|
-
|
84
|
+
In `assets/javascript/application.rb` require your components manifest as well
|
85
|
+
as any additional browser only assets.
|
55
86
|
|
56
87
|
```
|
57
|
-
#assets/javascript/application.rb
|
88
|
+
# assets/javascript/application.rb
|
89
|
+
# Require files that are browser side only.
|
58
90
|
|
59
|
-
#
|
91
|
+
# Make components available by requiring your components.rb manifest.
|
92
|
+
require 'components'
|
60
93
|
|
61
|
-
|
62
|
-
require 'react_ujs'
|
94
|
+
# 'react_ujs' tells react in the browser to mount rendered components.
|
95
|
+
require 'react_ujs'
|
63
96
|
|
64
|
-
# require
|
65
|
-
#
|
66
|
-
#
|
67
|
-
|
68
|
-
require 'jquery' # you need both these files to access jQuery from Opal
|
69
|
-
require 'opal-jquery' # they must be in this order, and after the components require
|
70
|
-
require 'browser/interval' # for #every, and #after methods
|
97
|
+
# Finally, require your other javascript assets. jQuery for example...
|
98
|
+
require 'jquery' # You need both these files to access jQuery from Opal.
|
99
|
+
require 'opal-jquery' # They must be in this order.
|
71
100
|
```
|
72
101
|
|
73
|
-
|
102
|
+
### Rendering Components
|
74
103
|
|
75
|
-
|
76
|
-
|
104
|
+
Components may be rendered directly from a controller action by simply following
|
105
|
+
a naming convention. To render a component from the `home#show` action, create
|
106
|
+
component class `Components::Home::Show`:
|
77
107
|
|
78
108
|
```ruby
|
79
109
|
# app/views/components/home/show.rb
|
80
|
-
|
81
110
|
module Components
|
82
|
-
|
83
111
|
module Home
|
84
|
-
|
85
112
|
class Show
|
113
|
+
include React::Component # will create a new component named Show
|
86
114
|
|
87
|
-
include React::Component # will create a new component named Show
|
88
|
-
|
89
115
|
optional_param :say_hello_to
|
90
116
|
|
91
|
-
def render
|
117
|
+
def render
|
92
118
|
puts "Rendering my first component!"
|
93
|
-
"hello #{'there '+say_hello_to if say_hello_to}" # render "hello" with optional 'there ...'
|
94
|
-
end
|
95
119
|
|
120
|
+
# render "hello" with optional 'say_hello_to' param
|
121
|
+
"hello #{say_hello_to if say_hello_to}"
|
122
|
+
end
|
96
123
|
end
|
97
|
-
|
98
124
|
end
|
99
|
-
|
100
125
|
end
|
101
126
|
```
|
102
127
|
|
103
|
-
|
128
|
+
Call `render_component` in the controller action passing in any params (React
|
129
|
+
props), to render the component:
|
130
|
+
|
104
131
|
```ruby
|
105
132
|
# controllers/home_controller.rb
|
106
133
|
class HomeController < ApplicationController
|
107
134
|
def show
|
108
|
-
|
135
|
+
# render_component uses the controller name to find the 'show' component.
|
136
|
+
render_component say_hello_to: params[:say_hello_to]
|
109
137
|
end
|
110
138
|
end
|
111
139
|
```
|
112
140
|
|
113
|
-
Make sure your routes file has a route to your home#show
|
141
|
+
Make sure your routes file has a route to your home#show action. Visit that
|
142
|
+
route in your browser and you should see 'Hello' rendered.
|
114
143
|
|
115
|
-
Open up the js console in the browser and you will see a log showing what went
|
144
|
+
Open up the js console in the browser and you will see a log showing what went
|
145
|
+
on during rendering.
|
116
146
|
|
117
|
-
Have a look at the sources in the console, and notice your ruby code is there,
|
147
|
+
Have a look at the sources in the console, and notice your ruby code is there,
|
148
|
+
and you can set break points etc.
|
118
149
|
|
119
150
|
### Changing the top level component name and search path
|
120
151
|
|
121
|
-
You can control the top level component name and search path.
|
152
|
+
You can control the top level component name and search path.
|
122
153
|
|
123
|
-
You can specify the component name explicitly in the `render_component` method.
|
124
|
-
`Blatz`
|
154
|
+
You can specify the component name explicitly in the `render_component` method.
|
155
|
+
`render_component "Blatz` will search the for a component class named `Blatz`
|
156
|
+
regardless of the controller method.
|
125
157
|
|
126
|
-
Searching for components normally works like this: Given a controller named
|
127
|
-
|
158
|
+
Searching for components normally works like this: Given a controller named
|
159
|
+
"Foo" then the component should be either in the `Components::Foo` module, the
|
160
|
+
`Components` module (no controller - useful if you have just a couple of shared
|
161
|
+
components) or just the outer scope (i.e. `Module`) which is useful for small
|
162
|
+
apps.
|
128
163
|
|
129
|
-
Saying `render_component "::Blatz"` will only search the outer scope, while
|
164
|
+
Saying `render_component "::Blatz"` will only search the outer scope, while
|
165
|
+
`"::Foo::Blatz"` will look only in the module `Foo` for a class named `Blatz`.
|
130
166
|
|
131
167
|
|
132
168
|
## Integration with Sinatra
|
133
169
|
|
134
|
-
See the sinatra-tutorial
|
170
|
+
See the [sinatra example](https://github.com/zetachang/react.rb/tree/master/example/sinatra-tutorial).
|
171
|
+
|
172
|
+
## Contextual Code
|
173
|
+
|
174
|
+
Sometimes it may be necessary to run code only on the server or only in the
|
175
|
+
browser. To execute code only during server side rendering:
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
if React::IsomorphicHelpers.on_opal_server?
|
179
|
+
puts 'Hello from the server'
|
180
|
+
end
|
181
|
+
```
|
182
|
+
|
183
|
+
To execute code only in the browser:
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
if React::IsomorphicHelpers.on_opal_client?
|
187
|
+
puts 'Hello from the browser'
|
188
|
+
end
|
189
|
+
```
|
135
190
|
|
136
191
|
## Typical Problems
|
137
192
|
|
138
|
-
`Uncaught TypeError: Cannot read property 'toUpperCase' of undefined` This
|
193
|
+
`Uncaught TypeError: Cannot read property 'toUpperCase' of undefined` This
|
194
|
+
means the thing you are trying to render is not actually a react component.
|
195
|
+
Often is because the top level component name is wrong. For example if you are
|
196
|
+
in controller Foo and the method is `bar`, but you have named the component
|
197
|
+
Foo::Bars then you would see this message.
|
139
198
|
|
140
199
|
## Turning off Prerendering
|
141
200
|
|
142
|
-
Sometimes its handy to switch off prerendering. Add `?no_prerender=1` ... to
|
201
|
+
Sometimes its handy to switch off prerendering. Add `?no_prerender=1` ... to
|
202
|
+
your url.
|
143
203
|
|
144
204
|
|
145
205
|
## TODOS / Work arounds / Issues
|
146
206
|
|
147
207
|
* Documentation
|
148
|
-
* Should load the RubyRacer, or at least report an error if the RubyRacer is not
|
208
|
+
* Should load the RubyRacer, or at least report an error if the RubyRacer is not
|
209
|
+
present
|
149
210
|
* Get everything to autoload what it needs (i.e. much less config setup)
|
150
211
|
|
151
212
|
## Developing
|
@@ -165,8 +226,10 @@ To run the above examples project yourself:
|
|
165
226
|
|
166
227
|
## Contributions
|
167
228
|
|
168
|
-
This project is still in early stage, so discussion, bug report and PR are
|
169
|
-
We check in often at
|
229
|
+
This project is still in early stage, so discussion, bug report and PR are
|
230
|
+
really welcome :wink:. We check in often at
|
231
|
+
https://gitter.im/zetachang/react.rb ask for @catmando as David is on leave
|
232
|
+
right now.
|
170
233
|
|
171
234
|
## Contact
|
172
235
|
|
@@ -174,4 +237,5 @@ We check in often at https://gitter.im/zetachang/react.rb ask for @catmando.
|
|
174
237
|
|
175
238
|
## License
|
176
239
|
|
177
|
-
In short, React.rb is available under the MIT license. See the LICENSE file for
|
240
|
+
In short, React.rb is available under the MIT license. See the LICENSE file for
|
241
|
+
more info.
|
data/Rakefile
CHANGED
@@ -7,11 +7,14 @@ require 'rspec/core/rake_task'
|
|
7
7
|
require 'opal/rspec/rake_task'
|
8
8
|
|
9
9
|
RSpec::Core::RakeTask.new('ruby:rspec')
|
10
|
-
Opal::RSpec::RakeTask.new('opal:rspec')
|
10
|
+
Opal::RSpec::RakeTask.new('opal:rspec') do |s|
|
11
|
+
s.append_path 'spec/vendor'
|
12
|
+
s.index_path = 'spec/index.html.erb'
|
13
|
+
end
|
11
14
|
|
12
15
|
task :test do
|
13
16
|
Rake::Task['ruby:rspec'].invoke
|
14
|
-
|
17
|
+
Rake::Task['opal:rspec'].invoke
|
15
18
|
end
|
16
19
|
|
17
20
|
require 'generators/reactive_ruby/test_app/test_app_generator'
|
data/example/examples/Gemfile
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
3
|
gem 'therubyracer', platforms: :ruby
|
4
|
-
gem 'react-rails'
|
4
|
+
gem 'react-rails'
|
5
5
|
gem 'opal-rails'
|
6
|
-
|
6
|
+
|
7
|
+
# integrates opal with sprockets etc
|
7
8
|
gem 'reactive-ruby', path: "../.."
|
8
9
|
|
9
10
|
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require File.expand_path('../boot', __FILE__)
|
2
|
+
|
3
|
+
require 'rails/all'
|
4
|
+
|
5
|
+
# Require the gems listed in Gemfile, including any gems
|
6
|
+
# you've limited to :test, :development, or :production.
|
7
|
+
Bundler.require(*Rails.groups(assets: %w(development test)))
|
8
|
+
|
9
|
+
require 'reactive-ruby'
|
10
|
+
|
11
|
+
<%= application_definition %>
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'rails/generators/rails/app/app_generator'
|
2
|
+
|
3
|
+
module ReactiveRuby
|
4
|
+
class TestAppGenerator < ::Rails::Generators::Base
|
5
|
+
def self.source_paths
|
6
|
+
paths = self.superclass.source_paths
|
7
|
+
paths << File.expand_path('../templates', __FILE__)
|
8
|
+
paths.flatten
|
9
|
+
end
|
10
|
+
|
11
|
+
def remove_existing_app
|
12
|
+
remove_dir(test_app_path) if File.directory?(test_app_path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def generate_test_app
|
16
|
+
opts = options.dup
|
17
|
+
opts[:database] = 'sqlite3' if opts[:database].blank?
|
18
|
+
opts[:force] = true
|
19
|
+
opts[:skip_bundle] = true
|
20
|
+
|
21
|
+
puts "Generating Test Rails Application..."
|
22
|
+
invoke ::Rails::Generators::AppGenerator,
|
23
|
+
[ File.expand_path(test_app_path, destination_root) ], opts
|
24
|
+
end
|
25
|
+
|
26
|
+
def configure_test_app
|
27
|
+
template 'boot.rb', "#{test_app_path}/config/boot.rb", force: true
|
28
|
+
template 'application.rb', "#{test_app_path}/config/application.rb", force: true
|
29
|
+
template 'assets/javascripts/application.rb',
|
30
|
+
"#{test_app_path}/app/assets/javascripts/application.rb", force: true
|
31
|
+
template 'assets/javascripts/components.rb',
|
32
|
+
"#{test_app_path}/app/views/components.rb", force: true
|
33
|
+
template 'views/components/hello_world.rb',
|
34
|
+
"#{test_app_path}/app/views/components/hello_world.rb", force: true
|
35
|
+
template 'views/components/todo.rb',
|
36
|
+
"#{test_app_path}/app/views/components/todo.rb", force: true
|
37
|
+
end
|
38
|
+
|
39
|
+
def clean_superfluous_files
|
40
|
+
inside test_app_path do
|
41
|
+
remove_file '.gitignore'
|
42
|
+
remove_file 'doc'
|
43
|
+
remove_file 'Gemfile'
|
44
|
+
remove_file 'lib/tasks'
|
45
|
+
remove_file 'app/assets/images/rails.png'
|
46
|
+
remove_file 'app/assets/javascripts/application.js'
|
47
|
+
remove_file 'public/index.html'
|
48
|
+
remove_file 'public/robots.txt'
|
49
|
+
remove_file 'README.rdoc'
|
50
|
+
remove_file 'test'
|
51
|
+
remove_file 'vendor'
|
52
|
+
remove_file 'spec'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def configure_opal_rspec
|
57
|
+
inject_into_file "#{test_app_path}/config/application.rb",
|
58
|
+
after: /class Application < Rails::Application/, verbose: true do
|
59
|
+
%Q[
|
60
|
+
config.opal.method_missing = true
|
61
|
+
config.opal.optimized_operators = true
|
62
|
+
config.opal.arity_check = false
|
63
|
+
config.opal.const_missing = true
|
64
|
+
config.opal.dynamic_require_severity = :ignore
|
65
|
+
config.opal.enable_specs = true
|
66
|
+
config.opal.spec_location = 'spec-opal'
|
67
|
+
]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
protected
|
72
|
+
|
73
|
+
def application_definition
|
74
|
+
@application_definition ||= begin
|
75
|
+
test_application_contents
|
76
|
+
end
|
77
|
+
end
|
78
|
+
alias :store_application_definition! :application_definition
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def test_app_path
|
83
|
+
'spec/test_app'
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_application_path
|
87
|
+
File.expand_path("#{test_app_path}/config/application.rb",
|
88
|
+
destination_root)
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_application_contents
|
92
|
+
return unless File.exists?(test_application_path) && !options[:pretend]
|
93
|
+
contents = File.read(test_application_path)
|
94
|
+
contents[(contents.index("module #{module_name}"))..-1]
|
95
|
+
end
|
96
|
+
|
97
|
+
def module_name
|
98
|
+
'TestApp'
|
99
|
+
end
|
100
|
+
|
101
|
+
def gemfile_path
|
102
|
+
'../../../../Gemfile'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|