server_component 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 +7 -0
- data/.circleci/config.yml +42 -0
- data/.gitignore +11 -0
- data/.rspec +2 -0
- data/.rubocop.yml +46 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +19 -0
- data/README.md +108 -0
- data/Rakefile +12 -0
- data/app/controllers/server_component/base_component_controller.rb +95 -0
- data/app/controllers/server_component/component_routers_controller.rb +27 -0
- data/app/controllers/server_component/components_controller.rb +35 -0
- data/app/helpers/server_component_helper.rb +30 -0
- data/bin/rails +9 -0
- data/bin/webpack +4 -0
- data/config/routes.rb +6 -0
- data/lib/generators/server_component/component_controller_generator.rb +28 -0
- data/lib/generators/server_component/install_generator.rb +31 -0
- data/lib/generators/templates/AFTER_INSTALL +45 -0
- data/lib/generators/templates/api_component_router.rb +3 -0
- data/lib/generators/templates/component_controller.rb.tt +5 -0
- data/lib/generators/templates/server_component.rb +64 -0
- data/lib/server_component.rb +29 -0
- data/lib/server_component/component_router.rb +58 -0
- data/lib/server_component/engine.rb +7 -0
- data/lib/server_component/js_function_builder.rb +0 -0
- data/lib/server_component/jsrb_handler.rb +10 -0
- data/lib/server_component/railtie.rb +17 -0
- data/lib/server_component/version.rb +5 -0
- data/server_component.gemspec +40 -0
- metadata +152 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: dba7844e0d4e1ca570d77659d7ef3eea346900fc8ab9e84afef4dba723bf8113
|
4
|
+
data.tar.gz: 8d331e9937dfc21a5f281724831a18ab9c56dc6168b4df3dc0ca398bea09d9dd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a70a74596bbf8482aa244ed247bf04a8a412318c2aed0e8533c19715c9551a55099245bf8c7b93d02b5ebfd21b505c07734a655bb951dab25a9dcfc73df29796
|
7
|
+
data.tar.gz: 963688b759513463ef25f378c074830ec2e83710b1465a16ee1a5b31fc495eeabb3a798c3560dd405d58409d05a0747fab5e451ab3c38b0c3b5ea80b490377e7
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Ruby CircleCI 2.0 configuration file
|
2
|
+
#
|
3
|
+
# Check https://circleci.com/docs/2.0/language-ruby/ for more details
|
4
|
+
#
|
5
|
+
version: 2
|
6
|
+
jobs:
|
7
|
+
build:
|
8
|
+
docker:
|
9
|
+
- image: circleci/ruby:2.5.1-node-browsers
|
10
|
+
working_directory: ~/repo
|
11
|
+
steps:
|
12
|
+
- checkout
|
13
|
+
- run:
|
14
|
+
name: install dependencies
|
15
|
+
working_directory: ~/repo
|
16
|
+
command: |
|
17
|
+
bundle install --jobs=4 --retry=3 --path vendor/bundle
|
18
|
+
- run:
|
19
|
+
name: install binstubs
|
20
|
+
working_directory: ~/repo
|
21
|
+
command: |
|
22
|
+
bundle exec rake app:update:bin webpacker:binstubs
|
23
|
+
- run:
|
24
|
+
name: install dependencies
|
25
|
+
working_directory: ~/repo/spec/dummy
|
26
|
+
command: |
|
27
|
+
yarn install
|
28
|
+
- run:
|
29
|
+
name: run tests
|
30
|
+
working_directory: ~/repo
|
31
|
+
command: |
|
32
|
+
bundle exec rspec
|
33
|
+
- run:
|
34
|
+
name: run rubocop
|
35
|
+
working_directory: ~/repo
|
36
|
+
command: |
|
37
|
+
bundle exec rubocop
|
38
|
+
- run:
|
39
|
+
name: report coverage
|
40
|
+
working_directory: ~/repo
|
41
|
+
command: |
|
42
|
+
bash <(curl -s https://codecov.io/bash) -f coverage/.resultset.json
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
- 'lib/generators/templates/**/*'
|
4
|
+
- 'tmp/**/*'
|
5
|
+
- 'bin/*'
|
6
|
+
- 'spec/dummy/bin/**/*'
|
7
|
+
- 'spec/dummy/node_modules/**/*'
|
8
|
+
- 'vendor/**/*'
|
9
|
+
TargetRubyVersion: 2.5
|
10
|
+
|
11
|
+
Metrics/LineLength:
|
12
|
+
Max: 130
|
13
|
+
|
14
|
+
Metrics/MethodLength:
|
15
|
+
Max: 30
|
16
|
+
|
17
|
+
Metrics/AbcSize:
|
18
|
+
Max: 25
|
19
|
+
|
20
|
+
Metrics/ClassLength:
|
21
|
+
Max: 200
|
22
|
+
|
23
|
+
Style/ClassVars:
|
24
|
+
Enabled: false
|
25
|
+
|
26
|
+
Style/MissingRespondToMissing:
|
27
|
+
Enabled: false
|
28
|
+
|
29
|
+
Style/GuardClause:
|
30
|
+
Enabled: false
|
31
|
+
|
32
|
+
Naming/MemoizedInstanceVariableName:
|
33
|
+
EnforcedStyleForLeadingUnderscores: optional
|
34
|
+
|
35
|
+
Style/Documentation:
|
36
|
+
Enabled: false
|
37
|
+
|
38
|
+
Style/AccessModifierDeclarations:
|
39
|
+
Enabled: false
|
40
|
+
|
41
|
+
Style/NumericPredicate:
|
42
|
+
Enabled: false
|
43
|
+
|
44
|
+
Metrics/BlockLength:
|
45
|
+
Exclude:
|
46
|
+
- 'spec/**/*'
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.5.1
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source 'https://rubygems.org'
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in server_component.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
gem 'execjs'
|
9
|
+
gem 'stitch-rb'
|
10
|
+
|
11
|
+
group :development, :test do
|
12
|
+
gem 'capybara', '~> 2.18.0'
|
13
|
+
gem 'pry-byebug'
|
14
|
+
gem 'rspec-rails', '~> 3.8'
|
15
|
+
gem 'rubocop'
|
16
|
+
gem 'selenium-webdriver'
|
17
|
+
gem 'simplecov'
|
18
|
+
gem 'webpacker', '4.0.0.rc.2'
|
19
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
[](https://circleci.com/gh/effective-spa/server_component_rails) [](https://codecov.io/gh/effective-spa/server_component_rails) [](https://codeclimate.com/github/effective-spa/server_component_rails/maintainability)
|
2
|
+
|
3
|
+
# ServerComponent
|
4
|
+
|
5
|
+
ServerComponent makes it possible to implement React component as Rails controller.
|
6
|
+
This gem is designed to be used with [server_component](https://github.com/effective-spa/server_component) NPM package.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile and run `bundle install`:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'server_component'
|
14
|
+
```
|
15
|
+
|
16
|
+
Run this command to initialize configuration file and base classes.
|
17
|
+
|
18
|
+
```sh
|
19
|
+
$ rails generate server_component:install
|
20
|
+
```
|
21
|
+
|
22
|
+
## Getting Started
|
23
|
+
|
24
|
+
Add a new component controller:
|
25
|
+
|
26
|
+
```rb
|
27
|
+
class CounterComponentController < ServerComponent::ComponentController
|
28
|
+
def self.initial_state
|
29
|
+
{
|
30
|
+
count: 0
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
def increment
|
35
|
+
set_state do |s|
|
36
|
+
s.count { |prev| prev + 1 }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def decrement
|
41
|
+
set_state do |s|
|
42
|
+
s.count { |prev| prev - 1 }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
Specify in a component router:
|
49
|
+
|
50
|
+
```rb
|
51
|
+
class ApiComponentRouter < ServerComponent::ComponentRouter
|
52
|
+
component :counter
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
In the client code (with Babel >= 7):
|
57
|
+
|
58
|
+
```js
|
59
|
+
import React, { Component } from 'react';
|
60
|
+
import ReactDOM from 'react-dom';
|
61
|
+
import ServerComponent, { server_component, consumer } from 'server_component';
|
62
|
+
|
63
|
+
@server_component('counter')
|
64
|
+
class CounterContainer extends Component { }
|
65
|
+
|
66
|
+
@consumer('counter')
|
67
|
+
class CounterBody extends Component {
|
68
|
+
render() {
|
69
|
+
const { increment, decrement, state: { count } } = this.props.counter;
|
70
|
+
return (
|
71
|
+
<div>
|
72
|
+
<h1>Counter</h1>
|
73
|
+
<button onClick={increment}>increment</button>
|
74
|
+
<button onClick={decrement}>decrement</button>
|
75
|
+
<hr />
|
76
|
+
<div>Count: {counter}</div>
|
77
|
+
</div>
|
78
|
+
);
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
class Main extends Component {
|
83
|
+
render() {
|
84
|
+
return (
|
85
|
+
<ServerComponent.Use at="/react" name="api">
|
86
|
+
<CounterContainer>
|
87
|
+
<CounterBody />
|
88
|
+
</CounterContainer>
|
89
|
+
</ServerComponent.Use>
|
90
|
+
);
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
ReactDOM.render(<Main />, document.getElementById('root'));
|
95
|
+
```
|
96
|
+
|
97
|
+
## API Documentation
|
98
|
+
|
99
|
+
(TODO)
|
100
|
+
|
101
|
+
## Contributing
|
102
|
+
|
103
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/effective-spa/server_component.
|
104
|
+
|
105
|
+
|
106
|
+
## License
|
107
|
+
|
108
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
7
|
+
|
8
|
+
task default: :spec
|
9
|
+
|
10
|
+
# This let CI know tasks 'app:update:bin' and 'webpacker:binstubs'
|
11
|
+
require_relative 'spec/dummy/config/application'
|
12
|
+
Rails.application.load_tasks
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ServerComponent
|
4
|
+
class FetcherConfigurator
|
5
|
+
include ServerComponentHelper
|
6
|
+
|
7
|
+
def initialize(component_class, actions)
|
8
|
+
@component_class = component_class
|
9
|
+
@actions = actions
|
10
|
+
end
|
11
|
+
|
12
|
+
def before
|
13
|
+
jsrb = Jsrb::Base.new
|
14
|
+
set_state(jsrb) do |s|
|
15
|
+
yield(s, jsrb.expr.context.data)
|
16
|
+
end
|
17
|
+
set_value(:start_function, jsrb.generate_code)
|
18
|
+
end
|
19
|
+
|
20
|
+
def accept_file!
|
21
|
+
set_value :upload, true
|
22
|
+
end
|
23
|
+
|
24
|
+
def set_value(key, value)
|
25
|
+
@actions.each do |action|
|
26
|
+
@component_class.action_descriptors[action][key] = value
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class BaseComponentController < ServerComponent.parent_controller.constantize
|
32
|
+
class_attribute :initial_state, :action_on_mount
|
33
|
+
|
34
|
+
helper ServerComponentHelper
|
35
|
+
include ServerComponentHelper
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def set_state(&block) # rubocop:disable Naming/AccessorMethodName
|
40
|
+
jsrb = Jsrb::Base.new
|
41
|
+
super(jsrb, &block)
|
42
|
+
render jsrb: jsrb.generate_code
|
43
|
+
end
|
44
|
+
|
45
|
+
class << self
|
46
|
+
def to_json
|
47
|
+
{
|
48
|
+
action_on_mount: action_on_mount,
|
49
|
+
initial_state: initial_state,
|
50
|
+
actions: action_descriptors
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def action_descriptors
|
55
|
+
if instance_variable_defined?('@action_descriptors')
|
56
|
+
instance_variable_get '@action_descriptors'
|
57
|
+
else
|
58
|
+
h = {}
|
59
|
+
instance_variable_set '@action_descriptors', h
|
60
|
+
h
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def action(*action_names, &block)
|
65
|
+
options = action_names.extract_options!
|
66
|
+
raise ArgumentError, 'specify at least one action!' if action_names.empty?
|
67
|
+
|
68
|
+
action_names.each do |action_name|
|
69
|
+
action_descriptors[action_name.to_sym] = {}
|
70
|
+
end
|
71
|
+
fetcher(*action_names, &block) if block
|
72
|
+
if options[:on_mount]
|
73
|
+
raise ArgumentError, 'on_mount option can accept only one action' if action_names.size > 1
|
74
|
+
|
75
|
+
on_mount action_names.first
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def on_mount(action)
|
80
|
+
self.action_on_mount = action
|
81
|
+
end
|
82
|
+
|
83
|
+
def fetcher(*actions)
|
84
|
+
configurator = FetcherConfigurator.new(self, actions.map(&:to_sym))
|
85
|
+
yield configurator
|
86
|
+
end
|
87
|
+
|
88
|
+
def before(*actions, &block)
|
89
|
+
fetcher(*actions) do |c|
|
90
|
+
c.before(&block)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ServerComponent
|
4
|
+
class ComponentRoutersController < ServerComponent.parent_controller.constantize
|
5
|
+
def show
|
6
|
+
component_router = find_component_router
|
7
|
+
render json: component_router.to_json
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def find_component_router
|
13
|
+
component_router_class_name = "#{name.camelize}ComponentRouter"
|
14
|
+
component_router_class =
|
15
|
+
begin
|
16
|
+
component_router_class_name.constantize
|
17
|
+
rescue NameError
|
18
|
+
raise ActionController::RoutingError, "No route matches routes/#{name}"
|
19
|
+
end
|
20
|
+
component_router_class.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def name
|
24
|
+
params[:name]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ServerComponent
|
4
|
+
class ComponentsController < ServerComponent.parent_controller.constantize
|
5
|
+
def perform
|
6
|
+
component_class = find_component_class!
|
7
|
+
component_class.dispatch(component_action, request, response)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def find_component_class!
|
13
|
+
klass_name = "#{component_name.camelize}ComponentController"
|
14
|
+
klass =
|
15
|
+
begin
|
16
|
+
klass_name.constantize
|
17
|
+
rescue NameError
|
18
|
+
raise ActionController::RoutingError, "No route matches #{component_name}/#{component_action}"
|
19
|
+
end
|
20
|
+
unless klass <= ServerComponent::BaseComponentController
|
21
|
+
raise ActionController::RoutingError, "No route matches #{component_name}/#{component_action}"
|
22
|
+
end
|
23
|
+
|
24
|
+
klass
|
25
|
+
end
|
26
|
+
|
27
|
+
def component_name
|
28
|
+
params[:component_name]
|
29
|
+
end
|
30
|
+
|
31
|
+
def component_action
|
32
|
+
params[:component_action_name]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ServerComponentHelper
|
4
|
+
class SetStateDSL
|
5
|
+
def initialize(new_state, prev_state)
|
6
|
+
@new_state = new_state
|
7
|
+
@prev_state = prev_state
|
8
|
+
end
|
9
|
+
|
10
|
+
# rubocop:disable Style/MethodMissingSuper
|
11
|
+
def method_missing(name, *args)
|
12
|
+
if (matches = name.to_s.match(/^(.+)=$/))
|
13
|
+
@new_state.member!(matches[1]).assign!(args[0]).as_statement!
|
14
|
+
elsif block_given?
|
15
|
+
block_result = yield @prev_state.member!(name)
|
16
|
+
@new_state.member!(name).assign!(block_result).as_statement!
|
17
|
+
else
|
18
|
+
@new_state.member!(name)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
# rubocop:enable Style/MethodMissingSuper
|
22
|
+
end
|
23
|
+
|
24
|
+
def set_state(jsrb) # rubocop:disable Naming/AccessorMethodName
|
25
|
+
new_state = jsrb.var! { {} }
|
26
|
+
prev_state = jsrb.expr.component.state
|
27
|
+
yield SetStateDSL.new(new_state, prev_state)
|
28
|
+
jsrb.expr.component.setState.call(new_state).as_statement!
|
29
|
+
end
|
30
|
+
end
|
data/bin/rails
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# This is a manually created bin file.
|
4
|
+
# Some webpacker tasks depend on the current directory,
|
5
|
+
# which is fixed in https://github.com/rails/webpacker/pull/1475
|
6
|
+
# but in webpacker 3.x, it's not fixed yet;)
|
7
|
+
APP_PATH = File.expand_path('../spec/dummy/config/application', __dir__)
|
8
|
+
require_relative '../spec/dummy/config/boot'
|
9
|
+
require 'rails/commands'
|
data/bin/webpack
ADDED
data/config/routes.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ServerComponent
|
4
|
+
module Generators
|
5
|
+
class ComponentControllerGenerator < Rails::Generators::NamedBase
|
6
|
+
source_root File.expand_path('../templates', __dir__)
|
7
|
+
|
8
|
+
desc 'Creates a ServerComponent initializer.'
|
9
|
+
|
10
|
+
def create_controller_file
|
11
|
+
template 'component_controller.rb', File.join('app/controllers', class_path, "#{file_name}_component_controller.rb")
|
12
|
+
end
|
13
|
+
|
14
|
+
def component
|
15
|
+
rgx = /class [A-z::]+ComponentRouter < ServerComponent::ComponentRouter\n/
|
16
|
+
router_path = File.join('app/component_routers', "#{router_name}_component_router.rb")
|
17
|
+
routing_code = " component '#{name}'\n"
|
18
|
+
inject_into_file router_path, routing_code, after: rgx
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def router_name
|
24
|
+
@options.fetch(:router, 'api')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ServerComponent
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path('../templates', __dir__)
|
7
|
+
|
8
|
+
desc 'Creates a ServerComponent initializer.'
|
9
|
+
|
10
|
+
def create_initializer_file
|
11
|
+
template 'server_component.rb', 'config/initializers/server_component.rb'
|
12
|
+
end
|
13
|
+
|
14
|
+
def setup_route
|
15
|
+
route "mount ServerComponent::Engine, at: 'react'"
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_directory
|
19
|
+
empty_directory 'app/component_routers'
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_one_router
|
23
|
+
template 'api_component_router.rb', 'app/component_routers/api_component_router.rb'
|
24
|
+
end
|
25
|
+
|
26
|
+
def show_readme
|
27
|
+
readme 'AFTER_INSTALL' if behavior == :invoke
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
===============================================================================
|
2
|
+
|
3
|
+
What's next:
|
4
|
+
|
5
|
+
1. Add "component_routers" directory into autoload_paths
|
6
|
+
in config/application.rb
|
7
|
+
|
8
|
+
config.autoload_paths << Rails.root.join('component_routers')
|
9
|
+
|
10
|
+
|
11
|
+
2. Create component controller. If you want BookComponent for example, run:
|
12
|
+
|
13
|
+
$ rails generate server_component:component_controller book --router api
|
14
|
+
|
15
|
+
|
16
|
+
3. Conjoin client application with `server_component`:
|
17
|
+
|
18
|
+
// Create a component class.
|
19
|
+
|
20
|
+
@ServerComponent.component('book')
|
21
|
+
class BookContainer extends React.Component {}
|
22
|
+
|
23
|
+
// Create a component-attached class.
|
24
|
+
|
25
|
+
@ServerComponent.attach('book')
|
26
|
+
class Book extends React.Component {
|
27
|
+
render() {
|
28
|
+
const { state: { list } } = this.props.book;
|
29
|
+
return (
|
30
|
+
<ul>
|
31
|
+
{list.map(book => <li>{book.title}</li>)}
|
32
|
+
</ul>
|
33
|
+
);
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
// And use in your root render function:
|
38
|
+
|
39
|
+
<ServerComponent.Use at="/react" name="api">
|
40
|
+
<BookContainer>
|
41
|
+
<Book />
|
42
|
+
</BookContainer>
|
43
|
+
</ServerComponent.Use>
|
44
|
+
|
45
|
+
===============================================================================
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Use this hook to configure ServerComponent
|
4
|
+
ServerComponent.setup do |config|
|
5
|
+
# context_script is executed before response scripts.
|
6
|
+
# It is useful if you want to add functions
|
7
|
+
# invoked from JSRB renderer.
|
8
|
+
# You can get identifiers from JSRB by calling
|
9
|
+
# `js.identifier_name` .
|
10
|
+
# The combination of 'add_custom_chain' option will
|
11
|
+
# make your JSRB code more readable.
|
12
|
+
config.context_script = <<~JAVASCRIPT
|
13
|
+
// Returns new object or array
|
14
|
+
// updating a property by immediate value or updator
|
15
|
+
// function in the specified path.
|
16
|
+
// without side effect in the original.
|
17
|
+
//
|
18
|
+
// example:
|
19
|
+
// immutable_update({ foo: 1 }, 'foo', 2) => { foo: 2 }
|
20
|
+
//
|
21
|
+
var immutable_update = (function () {
|
22
|
+
function _is_array(target) {
|
23
|
+
return Object.prototype.toString.call(target) === '[object Array]';
|
24
|
+
}
|
25
|
+
|
26
|
+
function _is_object(target) {
|
27
|
+
return typeof target === 'object' && target !== null;
|
28
|
+
}
|
29
|
+
|
30
|
+
function _clone_array_or_object(target) {
|
31
|
+
if (_is_array(target)) {
|
32
|
+
return target.slice(0);
|
33
|
+
} else if (_is_object(target)) {
|
34
|
+
return Object(target);
|
35
|
+
} else {
|
36
|
+
throw 'Argument Error: target is neither object nor array';
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
function _clone_update_internal(obj, field, value) {
|
41
|
+
if (value instanceof Function) {
|
42
|
+
obj[field] = value(obj[field]);
|
43
|
+
} else {
|
44
|
+
obj[field] = value;
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
return function (target, field, updator) {
|
49
|
+
var clone = _clone_array_or_object(target);
|
50
|
+
_clone_update_internal(clone, field, updator);
|
51
|
+
return clone;
|
52
|
+
}
|
53
|
+
})();
|
54
|
+
JAVASCRIPT
|
55
|
+
|
56
|
+
# Adds functionality to JSRB DSL.
|
57
|
+
# If you add 'func!' to 'some_func',
|
58
|
+
# then `obj.func!(arg)` is equivalent to `js.some_func.(obj, arg)`
|
59
|
+
# where `obj` is a chain instance in JSRB.
|
60
|
+
config.add_custom_chain(:update!, :immutable_update)
|
61
|
+
|
62
|
+
# Specify parent class of ServerComponent's controllers.
|
63
|
+
# config.parent_controller = 'ApplicationController'
|
64
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/dependencies'
|
4
|
+
require 'jsrb'
|
5
|
+
|
6
|
+
module ServerComponent
|
7
|
+
autoload :VERSION, 'server_component/version'
|
8
|
+
autoload :ComponentRouter, 'server_component/component_router'
|
9
|
+
|
10
|
+
class NamespaceNotFound < StandardError; end
|
11
|
+
|
12
|
+
mattr_accessor :parent_controller
|
13
|
+
@@parent_controller = 'ApplicationController'
|
14
|
+
|
15
|
+
def self.setup
|
16
|
+
yield self
|
17
|
+
end
|
18
|
+
|
19
|
+
mattr_accessor :context_script
|
20
|
+
@@context_script = ''
|
21
|
+
|
22
|
+
def self.add_custom_chain(name, function_name)
|
23
|
+
Jsrb::ExprChain.add_custom_chain name, function_name
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
require 'server_component/engine'
|
28
|
+
require 'server_component/jsrb_handler'
|
29
|
+
require 'server_component/railtie' if defined?(Rails)
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ServerComponent
|
4
|
+
class ComponentRouter
|
5
|
+
class_attribute :_namespace
|
6
|
+
|
7
|
+
def to_json
|
8
|
+
h = {}
|
9
|
+
h[:components] = components.each_with_object({}) do |component, json|
|
10
|
+
component_controller_class = find_component_controller_class(component)
|
11
|
+
json[component] = component_controller_class.to_json
|
12
|
+
end
|
13
|
+
h[:context_script] = context_script
|
14
|
+
h
|
15
|
+
end
|
16
|
+
|
17
|
+
def components
|
18
|
+
self.class.components
|
19
|
+
end
|
20
|
+
|
21
|
+
def namespace
|
22
|
+
self.class._namespace
|
23
|
+
end
|
24
|
+
|
25
|
+
def context_script
|
26
|
+
ServerComponent.context_script
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def find_component_controller_class(component)
|
32
|
+
name = (namespace ? "#{namespace}/#{component}" : component).to_s
|
33
|
+
"#{name.camelize}ComponentController".constantize
|
34
|
+
end
|
35
|
+
|
36
|
+
class << self
|
37
|
+
def namespace(mod)
|
38
|
+
self._namespace = mod
|
39
|
+
end
|
40
|
+
|
41
|
+
def components
|
42
|
+
if instance_variable_defined?('@components')
|
43
|
+
instance_variable_get '@components'
|
44
|
+
else
|
45
|
+
a = []
|
46
|
+
instance_variable_set '@components', a
|
47
|
+
a
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def component(*component_names)
|
52
|
+
component_names.each do |name|
|
53
|
+
components << name.to_sym
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
File without changes
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ServerComponent
|
4
|
+
class Railtie < ::Rails::Railtie
|
5
|
+
initializer :server_component do
|
6
|
+
ActiveSupport.on_load :action_view do
|
7
|
+
ActionView::Template.register_template_handler :jsrb, JsrbHandler
|
8
|
+
end
|
9
|
+
|
10
|
+
ActiveSupport.on_load :action_controller do
|
11
|
+
ActionController::Renderers.add :jsrb do |obj, _options|
|
12
|
+
send_data obj.to_s, type: Mime[:js]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'server_component/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'server_component'
|
9
|
+
spec.version = ServerComponent::VERSION
|
10
|
+
spec.authors = ['Shun MIZUKAMI']
|
11
|
+
spec.email = ['norainu234@gmail.com']
|
12
|
+
|
13
|
+
spec.summary = 'ServerComponent support for Rails'
|
14
|
+
spec.description = 'ServerComponent makes it possible to implement React component as Rails controller'
|
15
|
+
spec.homepage = 'https://github.com/effective-spa/server_component_rails'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
19
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
20
|
+
if spec.respond_to?(:metadata)
|
21
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
22
|
+
else
|
23
|
+
raise 'RubyGems 2.0 or newer is required to protect against ' \
|
24
|
+
'public gem pushes.'
|
25
|
+
end
|
26
|
+
|
27
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
28
|
+
f.match(%r{^(test|spec|features)/})
|
29
|
+
end
|
30
|
+
spec.bindir = 'exe'
|
31
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
32
|
+
spec.require_paths = ['lib']
|
33
|
+
|
34
|
+
spec.add_runtime_dependency 'jsrb', '~> 0.1'
|
35
|
+
spec.add_runtime_dependency 'rails', '~> 5.0', '>= 5.0.0'
|
36
|
+
|
37
|
+
spec.add_development_dependency 'bundler', '~> 1.13'
|
38
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
39
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
40
|
+
end
|
metadata
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: server_component
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Shun MIZUKAMI
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-01-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: jsrb
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rails
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '5.0'
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 5.0.0
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - "~>"
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '5.0'
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 5.0.0
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: bundler
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.13'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '1.13'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: rake
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '10.0'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '10.0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: rspec
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '3.0'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '3.0'
|
89
|
+
description: ServerComponent makes it possible to implement React component as Rails
|
90
|
+
controller
|
91
|
+
email:
|
92
|
+
- norainu234@gmail.com
|
93
|
+
executables: []
|
94
|
+
extensions: []
|
95
|
+
extra_rdoc_files: []
|
96
|
+
files:
|
97
|
+
- ".circleci/config.yml"
|
98
|
+
- ".gitignore"
|
99
|
+
- ".rspec"
|
100
|
+
- ".rubocop.yml"
|
101
|
+
- ".ruby-version"
|
102
|
+
- ".travis.yml"
|
103
|
+
- Gemfile
|
104
|
+
- README.md
|
105
|
+
- Rakefile
|
106
|
+
- app/controllers/server_component/base_component_controller.rb
|
107
|
+
- app/controllers/server_component/component_routers_controller.rb
|
108
|
+
- app/controllers/server_component/components_controller.rb
|
109
|
+
- app/helpers/server_component_helper.rb
|
110
|
+
- bin/rails
|
111
|
+
- bin/webpack
|
112
|
+
- config/routes.rb
|
113
|
+
- lib/generators/server_component/component_controller_generator.rb
|
114
|
+
- lib/generators/server_component/install_generator.rb
|
115
|
+
- lib/generators/templates/AFTER_INSTALL
|
116
|
+
- lib/generators/templates/api_component_router.rb
|
117
|
+
- lib/generators/templates/component_controller.rb.tt
|
118
|
+
- lib/generators/templates/server_component.rb
|
119
|
+
- lib/server_component.rb
|
120
|
+
- lib/server_component/component_router.rb
|
121
|
+
- lib/server_component/engine.rb
|
122
|
+
- lib/server_component/js_function_builder.rb
|
123
|
+
- lib/server_component/jsrb_handler.rb
|
124
|
+
- lib/server_component/railtie.rb
|
125
|
+
- lib/server_component/version.rb
|
126
|
+
- server_component.gemspec
|
127
|
+
homepage: https://github.com/effective-spa/server_component_rails
|
128
|
+
licenses:
|
129
|
+
- MIT
|
130
|
+
metadata:
|
131
|
+
allowed_push_host: https://rubygems.org
|
132
|
+
post_install_message:
|
133
|
+
rdoc_options: []
|
134
|
+
require_paths:
|
135
|
+
- lib
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
requirements: []
|
147
|
+
rubyforge_project:
|
148
|
+
rubygems_version: 2.7.6
|
149
|
+
signing_key:
|
150
|
+
specification_version: 4
|
151
|
+
summary: ServerComponent support for Rails
|
152
|
+
test_files: []
|