reactrb-router 0.8.1
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/LICENSE +22 -0
- data/README.md +185 -0
- data/Rakefile +1 -0
- data/lib/reactrb-router/component.rb +15 -0
- data/lib/reactrb-router/history.rb +153 -0
- data/lib/reactrb-router/router.rb +228 -0
- data/lib/reactrb-router/version.rb +3 -0
- data/lib/reactrb-router/window_location.rb +11 -0
- data/lib/reactrb-router.rb +15 -0
- metadata +94 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 560f5e4ceb0cf919265e4be43f4835b9d52040a9
|
4
|
+
data.tar.gz: 18412d2c5caa9760f48a683d7faff813c2600a1d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d060b980b6d63dd302eaa7e05677893dac2e94da3b948aef5a7f57971ee82e60279c9b6a43e16332288bde2cfb86607b946a08bc489ea34e25f10884507a4caa
|
7
|
+
data.tar.gz: a81767b732acf324860098494e1b326960c7b7ed8f3374f2bec9843a28b95c72f7e86450f422703ed883fd85cabb6f65e7a2b8de2ed63572253ab8bb868150b9
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 catprintlabs
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
## Reactrb Router
|
2
|
+
|
3
|
+
Reactrb Router allows you write and use the React Router in Ruby through Opal.
|
4
|
+
|
5
|
+
### Note
|
6
|
+
|
7
|
+
This gem is in the process of being re-written. It will be based on latest react-router which is way better. Please see the [v2-4-0 branch](https://github.com/reactrb/reactrb-router/tree/v2-4-0).
|
8
|
+
If you you want to use this branch with `reactrb-rails-generator`, be sure to remove `require 'react_router` from `components.rb`. And to use the new Router syntax as per the docs.
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
gem 'reactrb-router'
|
16
|
+
```
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
$ bundle
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
$ gem install reactor-router
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
The router is a React component that loads other components depending on the current URL.
|
29
|
+
|
30
|
+
Unlike other components there can only be one router on a page.
|
31
|
+
|
32
|
+
To get you started here is a sample router.
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
module Components
|
36
|
+
module Accounts
|
37
|
+
|
38
|
+
class Show
|
39
|
+
|
40
|
+
include React::Router # instead of React::Component, you use React::Router
|
41
|
+
|
42
|
+
# the routes macro creates the mapping between URLs and components to display
|
43
|
+
|
44
|
+
routes(path: '/account/:user_id') do # i.e. we expect to see something like /account/12345
|
45
|
+
# routes can be nested the dashboard will be at /account/12345/dashboard
|
46
|
+
# the DashboardRoute component will be mounted
|
47
|
+
route(name: 'Dashboard', path: 'dashboard', handler: Components::Accounts::DashboardRoute)
|
48
|
+
route(path: 'orders', name: 'Orders', handler: Components::Accounts::OrdersRoute)
|
49
|
+
# when displaying an order we need the order order as well as the user_id
|
50
|
+
route(path: 'orders/:order_id', name: 'Order', handler: Components::Accounts::OrderRoute)
|
51
|
+
route(path: 'statement', name: 'Statement', handler: Components::Accounts::StatementRoute)
|
52
|
+
# the special redirect route
|
53
|
+
redirect(from: '/account/:user_id', to: 'Dashboard')
|
54
|
+
end
|
55
|
+
|
56
|
+
# you grab the url params and preprocess them using the router_param macro.
|
57
|
+
# when Router is mounted it will receive the :user_id from the url. In this case we grab
|
58
|
+
# the corresponding active_record model.
|
59
|
+
|
60
|
+
router_param :user_id do |id|
|
61
|
+
User.find(id)
|
62
|
+
end
|
63
|
+
|
64
|
+
# like any component routers can have params that are passed in when the router is mounted
|
65
|
+
|
66
|
+
param :user_param, type: User
|
67
|
+
param :user_orders_param, type: [Order]
|
68
|
+
param :production_center_address_param, type: Address
|
69
|
+
param :open_invoices_param
|
70
|
+
param :user_profiles_param, type: [PaymentProfile]
|
71
|
+
param :user_addresses_param, type: [Address]
|
72
|
+
|
73
|
+
# because the underlying javascript router has no provisions to pass params we
|
74
|
+
# will export states and copy the params to the states so the lower components can read them
|
75
|
+
# expect this get fixed in the near future
|
76
|
+
|
77
|
+
export_state :user
|
78
|
+
export_state :production_center_address
|
79
|
+
export_state :open_invoices
|
80
|
+
export_state :payment_profiles
|
81
|
+
export_state :addresses
|
82
|
+
|
83
|
+
# the router also makes a good place for other top level states to be housed (i.e. the flux architecture)
|
84
|
+
export_state :order_count
|
85
|
+
|
86
|
+
before_mount do
|
87
|
+
# before mounting the router we copy the incoming params that the lower level components will need
|
88
|
+
user! user_param
|
89
|
+
production_center_address! production_center_address_param
|
90
|
+
open_invoices! open_invoices_param
|
91
|
+
payment_profiles! user_profiles_param
|
92
|
+
addresses! user_addresses_param
|
93
|
+
|
94
|
+
order_count! user.orders.count # grab our top level state info and save it away
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
# For routers you define a show method instead of a render method
|
99
|
+
def show
|
100
|
+
div do
|
101
|
+
div.account_nav do
|
102
|
+
|
103
|
+
# link is a special router component that generates an on page link, that will maintain history etc.
|
104
|
+
# basically an intelligent anchor tag. When a user clicks a link, it will rerender the router, update
|
105
|
+
# the history etc.
|
106
|
+
# So for example when 'My Statement' is clicked. The route changes to /account/:id/statement
|
107
|
+
|
108
|
+
link(to: 'Dashboard', class: 'no-underline btn btn-default', params: { user_id: user.id }) { 'Account Dashboard' }
|
109
|
+
link(to: 'Orders', class: 'no-underline btn btn-default', params: { user_id: user.id }) { 'My Quotes & Orders' }
|
110
|
+
link(to: 'Statement', class: 'no-underline btn btn-default', params: { user_id: user.id }) { 'My Statement' }
|
111
|
+
|
112
|
+
end
|
113
|
+
# someplace in the router show method you will have route_handler component which mounts and renders the component
|
114
|
+
# indicated by the current route.
|
115
|
+
route_handler
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# We can't pass parameters to the routed components, so we set up these mini components
|
121
|
+
# which grab the state from router and send it along to the actual component
|
122
|
+
|
123
|
+
class DashboardRoute
|
124
|
+
|
125
|
+
include React::Component
|
126
|
+
|
127
|
+
def render
|
128
|
+
AccountDashboard user: Show.user, addresses: Show.addresses, payment_profiles: Show.payment_profiles
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
class StatementRoute
|
134
|
+
|
135
|
+
include React::Component
|
136
|
+
|
137
|
+
def render
|
138
|
+
Statement production_center_address: Show.production_center_address,
|
139
|
+
open_invoices: Show.open_invoices, current_invoices: Show.open_invoices[:invoices],
|
140
|
+
mailing_address: Show.open_invoices[:mailing_address]
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
class OrdersRoute
|
146
|
+
|
147
|
+
include React::Component
|
148
|
+
|
149
|
+
def render
|
150
|
+
AccountOrders user: Show.user #, orders: Show.orders
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
class OrderRoute
|
156
|
+
|
157
|
+
include React::Component
|
158
|
+
|
159
|
+
router_param :order_id do |id|
|
160
|
+
Order.find(id)
|
161
|
+
end
|
162
|
+
|
163
|
+
def render
|
164
|
+
OrderShow(order: order_id, referrer: 'account')
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
end
|
171
|
+
```
|
172
|
+
|
173
|
+
## Development
|
174
|
+
|
175
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
176
|
+
|
177
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
178
|
+
|
179
|
+
## Contributing
|
180
|
+
|
181
|
+
1. Fork it ( https://github.com/reactrb/reactrb-router/fork )
|
182
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
183
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
184
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
185
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module React
|
2
|
+
module Component
|
3
|
+
module ClassMethods
|
4
|
+
end
|
5
|
+
|
6
|
+
def route_handler(*args)
|
7
|
+
Router::RR::RouteHandler(*args)
|
8
|
+
end
|
9
|
+
|
10
|
+
def link(opts = {}, &block)
|
11
|
+
opts[:params] = opts[:params].to_n if opts[:params]
|
12
|
+
Router::RR::Link(opts, &block)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
class History
|
2
|
+
class << self
|
3
|
+
def setup_handler
|
4
|
+
unless @handlers_setup
|
5
|
+
handler = lambda { |event| window_history_pop_handler(event) }
|
6
|
+
%x{
|
7
|
+
if (window.addEventListener) {
|
8
|
+
window.addEventListener('popstate', handler, false);
|
9
|
+
} else {
|
10
|
+
window.attachEvent('onpopstate', handler);
|
11
|
+
}
|
12
|
+
}
|
13
|
+
end
|
14
|
+
@handlers_setup = true
|
15
|
+
end
|
16
|
+
|
17
|
+
def current_path
|
18
|
+
@current_path ||= `decodeURI(window.location.pathname + window.location.search)`
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_accessor :history
|
22
|
+
|
23
|
+
def [](name)
|
24
|
+
(@histories ||= {})[name]
|
25
|
+
end
|
26
|
+
|
27
|
+
def []=(name, value)
|
28
|
+
(@histories ||= {})[name] = value
|
29
|
+
end
|
30
|
+
|
31
|
+
def window_history_pop_handler(event)
|
32
|
+
return if `event.state === undefined`
|
33
|
+
if `event.state == null` # happens when popping off outer dialog
|
34
|
+
puts "pop handler pops off last value"
|
35
|
+
old_history = @history
|
36
|
+
@saved_history = @history # for safari's sake
|
37
|
+
@saved_path = @current_path
|
38
|
+
@saved_history_length = `ReactRouter.History.length`
|
39
|
+
@current_path = ""
|
40
|
+
@history = nil
|
41
|
+
`ReactRouter.History.length = 0`
|
42
|
+
old_history.on_state_change.call(:inactive) if old_history.on_state_change
|
43
|
+
else
|
44
|
+
puts "pop handler #{`event.state.history_id`}, #{`ReactRouter.History.length`} -> #{`event.state.history_length`}, #{`event.state.path`}"
|
45
|
+
old_history = @history
|
46
|
+
old_history_length = `ReactRouter.History.length`
|
47
|
+
@current_path = `event.state.path`
|
48
|
+
@history= History[`event.state.history_id`]
|
49
|
+
`ReactRouter.History.length = event.state.history_length`
|
50
|
+
if old_history != @history
|
51
|
+
if `ReactRouter.History.length` > old_history_length
|
52
|
+
puts "activating "
|
53
|
+
@history.on_state_change.call(:active) if @history.on_state_change
|
54
|
+
else
|
55
|
+
puts "deactivating"
|
56
|
+
old_history.on_state_change.call(:inactive) if old_history.on_state_change
|
57
|
+
end
|
58
|
+
end
|
59
|
+
@history.notify_listeners(:pop)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def push_path(path)
|
64
|
+
puts "pushing path #{path}"
|
65
|
+
unless @history # needed because safari strangly pops off outer most history on initialization
|
66
|
+
@history = @saved_history
|
67
|
+
@current_path = @saved_path
|
68
|
+
`ReactRouter.History.length = #{@saved_history_length}`
|
69
|
+
@history.on_state_change.call(:active) if @history.on_state_change
|
70
|
+
end
|
71
|
+
`window.history.pushState({ path: path, history_id: #{@history.name}, history_length: (ReactRouter.History.length += 1)}, '', path);`
|
72
|
+
@current_path = path
|
73
|
+
@history.notify_listeners(:push)
|
74
|
+
end
|
75
|
+
|
76
|
+
def replace_path(path)
|
77
|
+
puts "replacing path #{path}"
|
78
|
+
unless @history # needed because safari strangly pops off outer most history on initialization
|
79
|
+
@history = @saved_history
|
80
|
+
@current_path = @saved_path
|
81
|
+
`ReactRouter.History.length = #{@saved_history_length}`
|
82
|
+
@history.on_state_change.call(:active) if @history.on_state_change
|
83
|
+
end
|
84
|
+
`window.history.replaceState({ path: path, history_id: #{@history.name}, history_length: ReactRouter.History.length}, '', path);`
|
85
|
+
@current_path = path
|
86
|
+
@history.notify_listeners(:replace)
|
87
|
+
end
|
88
|
+
|
89
|
+
def pop_path
|
90
|
+
`window.history.go(-1)`
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
attr_reader :location
|
95
|
+
attr_reader :on_state_change
|
96
|
+
attr_reader :name
|
97
|
+
|
98
|
+
def to_s
|
99
|
+
"History<#{@name}>"
|
100
|
+
end
|
101
|
+
|
102
|
+
def initialize(name, preactivate_path = nil, &on_state_change)
|
103
|
+
@name = name
|
104
|
+
if History[@name]
|
105
|
+
raise "a history location named #{@name} already exists"
|
106
|
+
else
|
107
|
+
History[@name] = self
|
108
|
+
end
|
109
|
+
@on_state_change = on_state_change
|
110
|
+
@initial_path = @preactivate_path = preactivate_path
|
111
|
+
self.class.setup_handler
|
112
|
+
@listeners = []
|
113
|
+
@location = {
|
114
|
+
addChangeListener: lambda { |listener| @listeners << listener unless @listeners.include? listener} ,
|
115
|
+
removeChangeListener: lambda { |listener| @listeners.delete(listener) },
|
116
|
+
push: lambda { |path| self.class.push_path(path) },
|
117
|
+
pop: lambda { self.class.pop_path },
|
118
|
+
replace: lambda { |path| self.class.replace_path path },
|
119
|
+
getCurrentPath: lambda { (@preactivate_path || self.class.current_path)},
|
120
|
+
toString: lambda { '<HistoryLocation>'}
|
121
|
+
}.to_n
|
122
|
+
end
|
123
|
+
|
124
|
+
def activate(initial_path = nil)
|
125
|
+
puts "activating #{self}"
|
126
|
+
@preactivate_path = nil
|
127
|
+
initial_path ||= @initial_path || self.class.current_path
|
128
|
+
current_history = self.class.history
|
129
|
+
self.class.history = self
|
130
|
+
@starting_history_length = `ReactRouter.History.length` if current_history != self
|
131
|
+
if @name == "MainApp"
|
132
|
+
self.class.replace_path initial_path
|
133
|
+
else
|
134
|
+
self.class.push_path initial_path
|
135
|
+
end
|
136
|
+
@on_state_change.call(:active) if @on_state_change and current_history != self
|
137
|
+
self
|
138
|
+
end
|
139
|
+
|
140
|
+
def deactivate
|
141
|
+
puts "deactivate go(#{@starting_history_length-`ReactRouter.History.length`})"
|
142
|
+
`window.history.go(#{@starting_history_length}-ReactRouter.History.length)`
|
143
|
+
self
|
144
|
+
end
|
145
|
+
|
146
|
+
def notify_listeners(type)
|
147
|
+
puts "!! #{self}.notify_listeners(#{type}) listeners_count: #{@listeners.count}, path: #{self.class.current_path}"
|
148
|
+
@listeners.each { |listener| `listener.call(#{@location}, {path: #{self.class.current_path}, type: type})` }
|
149
|
+
rescue Exception => e
|
150
|
+
`debugger`
|
151
|
+
nil
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
module React
|
2
|
+
module Router
|
3
|
+
class AbortTransition < Exception
|
4
|
+
end
|
5
|
+
|
6
|
+
class RR < React::NativeLibrary
|
7
|
+
imports ReactRouter
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.included(base)
|
11
|
+
base.class_eval do
|
12
|
+
|
13
|
+
include React::Component
|
14
|
+
include React::IsomorphicHelpers
|
15
|
+
|
16
|
+
native_mixin `ReactRouter.Navigation`
|
17
|
+
native_mixin `ReactRouter.State`
|
18
|
+
|
19
|
+
class << self
|
20
|
+
|
21
|
+
attr_accessor :router_context
|
22
|
+
|
23
|
+
def get_path
|
24
|
+
path = `#{@router_component}.native.getPath()`
|
25
|
+
path = nil if `typeof path === 'undefined'`
|
26
|
+
path
|
27
|
+
end
|
28
|
+
|
29
|
+
def replace_with(route_or_path, params = nil, query = nil)
|
30
|
+
`#{@router_component}.native.replaceWith.apply(self.native, #{[route_or_path, params, query].compact})`
|
31
|
+
end
|
32
|
+
|
33
|
+
def transition_to(route_or_path, params = {}, query = {})
|
34
|
+
params = @router_component.params[:router_state][:params].merge params
|
35
|
+
query = @router_component.params[:router_state][:query].merge query
|
36
|
+
`#{@router_component}.native.transitionTo.apply(self.native, #{[route_or_path, params.to_n, query.to_n]})`
|
37
|
+
end
|
38
|
+
|
39
|
+
def link(opts={}, &block)
|
40
|
+
params = opts.delete(:params) || {}
|
41
|
+
query = opts.delete(:query) || {}
|
42
|
+
to = opts.delete(:to)
|
43
|
+
React::RenderingContext.render("a", opts, &block).on(:click) { transition_to(to, params, query) }
|
44
|
+
end
|
45
|
+
|
46
|
+
def url_param_methods
|
47
|
+
@url_param_methods ||= []
|
48
|
+
end
|
49
|
+
|
50
|
+
attr_accessor :evaluated_url_params
|
51
|
+
attr_accessor :current_url_params
|
52
|
+
|
53
|
+
def router_param(name, opts = {}, &block)
|
54
|
+
|
55
|
+
# create a method visible both the instance and the class
|
56
|
+
# that gets passed the named router parameter (typically a db :id) and returns a
|
57
|
+
# meaningful value (typically a database record)
|
58
|
+
|
59
|
+
method_name = opts[:as] || name
|
60
|
+
|
61
|
+
class << self
|
62
|
+
define_method method_name do
|
63
|
+
if @router_component
|
64
|
+
block.call @router_component.url_params[name]
|
65
|
+
else
|
66
|
+
raise "Call to #{self.name}.#{method_name} when #{self.name} is not mounted"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
define_method method_name do
|
72
|
+
self.class.send method_name
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
static_call_back "willTransitionTo" do |transition, params, query, callback|
|
80
|
+
params = Hash.new(params)
|
81
|
+
query = Hash.new(query)
|
82
|
+
transition = `transition.path`
|
83
|
+
puts "willTransitionTo(#{transition}, #{params}, #{query}) for router #{self.object_id}, router_component = #{@router_component.object_id}"
|
84
|
+
begin
|
85
|
+
if self.respond_to? :will_transition_to
|
86
|
+
result = will_transition_to transition, params, query if self.respond_to? :will_transition_to
|
87
|
+
if result.is_a? Promise
|
88
|
+
result.then { |r| callback(r) }
|
89
|
+
else
|
90
|
+
callback.call()
|
91
|
+
end
|
92
|
+
else
|
93
|
+
callback.call()
|
94
|
+
end
|
95
|
+
rescue AbortTransition
|
96
|
+
raise "transition aborted"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
before_first_mount do |context|
|
101
|
+
|
102
|
+
@evaluated_url_params = {}
|
103
|
+
@current_url_params = {}
|
104
|
+
if !self.instance_methods.include?(:show) # if there is no show method this is NOT a top level router so we assume routing will begin elsewhere
|
105
|
+
@routing = true
|
106
|
+
elsif `typeof ReactRouter === 'undefined'`
|
107
|
+
if on_opal_client?
|
108
|
+
message = "ReactRouter not defined in browser assets - you must manually include it in your assets"
|
109
|
+
else
|
110
|
+
message = "ReactRouter not defined in components.js.rb assets manifest - you must manually include it in your assets"
|
111
|
+
end
|
112
|
+
`console.error(message)`
|
113
|
+
@routing = true
|
114
|
+
else
|
115
|
+
@routing = false
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
export_component
|
120
|
+
|
121
|
+
param :router_state # optional because it is not initially passed in but we add it when running the router
|
122
|
+
param :query
|
123
|
+
param :params
|
124
|
+
|
125
|
+
def url_params(params = params)
|
126
|
+
(params && (params[:params] || (params[:router_state] && params[:router_state][:params]))) || {}
|
127
|
+
end
|
128
|
+
|
129
|
+
def render
|
130
|
+
if self.class.routing?
|
131
|
+
if self.class.router_context
|
132
|
+
`self.native.context.router = #{self.class.router_context}`
|
133
|
+
else
|
134
|
+
self.class.router_context = `self.native.context.router`
|
135
|
+
end
|
136
|
+
self.class.instance_variable_set(:@router_component, self) # this was done after mount, but I think it works here
|
137
|
+
show
|
138
|
+
elsif on_opal_server?
|
139
|
+
self.class.routing!
|
140
|
+
routes = self.class.build_routes(true)
|
141
|
+
%x{
|
142
|
+
ReactRouter.run(#{routes}, window.reactrb_router_static_location, function(root, state) {
|
143
|
+
self.native.props.router_state = state
|
144
|
+
self.root = React.createElement(root, self.native.props);
|
145
|
+
});
|
146
|
+
}
|
147
|
+
React::Element.new(@root, 'Root', {}, nil)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.routing?
|
152
|
+
@routing
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.routing!
|
156
|
+
was_routing = @routing
|
157
|
+
@routing = true
|
158
|
+
was_routing
|
159
|
+
end
|
160
|
+
|
161
|
+
# override self.location to provide application specific location handlers
|
162
|
+
|
163
|
+
def location
|
164
|
+
(@location ||= History.new("MainApp")).activate.location # must be called MainApp for now to avoid doing an extra state push (ugh)
|
165
|
+
end
|
166
|
+
|
167
|
+
after_mount do
|
168
|
+
if !self.class.routing!
|
169
|
+
puts "after mount outside of ruby router #{self.object_id}, my class router is #{self.class.object_id}"
|
170
|
+
dom_node = if `typeof React.findDOMNode === 'undefined'`
|
171
|
+
`#{self}.native.getDOMNode` # v0.12.0
|
172
|
+
else
|
173
|
+
`React.findDOMNode(#{self}.native)` # v0.13.0
|
174
|
+
end
|
175
|
+
routes = self.class.build_routes(true)
|
176
|
+
%x{
|
177
|
+
ReactRouter.run(#{routes}, #{location}, function(root, state) {
|
178
|
+
self.native.props.router_state = state
|
179
|
+
React.render(React.createElement(root, self.native.props), #{dom_node});
|
180
|
+
});
|
181
|
+
}
|
182
|
+
elsif respond_to? :show
|
183
|
+
puts "after mount inside of ruby router #{self.object_id}, my class router is #{self.class.object_id}"
|
184
|
+
#self.class.instance_variable_set(:@router_component, self) # this was done here but was moved to the first render
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def self.routes(opts = {}, &block)
|
189
|
+
@routes_opts = opts
|
190
|
+
@routes_block = block
|
191
|
+
end
|
192
|
+
|
193
|
+
def self.routes_block
|
194
|
+
@routes_block
|
195
|
+
end
|
196
|
+
|
197
|
+
def self.build_routes(generate_node = nil)
|
198
|
+
#raise "You must define a routes block in a router component" unless @routes_block
|
199
|
+
routes_opts = @routes_opts.dup
|
200
|
+
routes_opts[:handler] ||= self
|
201
|
+
route(routes_opts, generate_node, &@routes_block)
|
202
|
+
end
|
203
|
+
|
204
|
+
def self.route(opts = {}, generate_node = nil, &block)
|
205
|
+
block ||= opts[:handler].routes_block if opts[:handler].respond_to? :routes_block
|
206
|
+
opts = opts.dup
|
207
|
+
opts[:handler] = React::API.create_native_react_class(opts[:handler])
|
208
|
+
(generate_node ? RR::Route_as_node(opts, &block) : RR::Route(opts, &block))
|
209
|
+
rescue Exception => e
|
210
|
+
React::IsomorphicHelpers.log("Could not define route #{opts}: #{e}", :error)
|
211
|
+
end
|
212
|
+
|
213
|
+
def self.default_route(ops = {}, &block)
|
214
|
+
RR::DefaultRoute(opts, &block)
|
215
|
+
end
|
216
|
+
|
217
|
+
def self.redirect(opts = {}, &block)
|
218
|
+
RR::Redirect(opts, &block)
|
219
|
+
end
|
220
|
+
|
221
|
+
def self.not_found(opts={}, &block)
|
222
|
+
opts[:handler] = React::API.create_native_react_class(opts[:handler])
|
223
|
+
RR::NotFoundRoute(opts, &block)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module React
|
2
|
+
module Router
|
3
|
+
class WindowLocation
|
4
|
+
include React::IsomorphicHelpers
|
5
|
+
|
6
|
+
before_first_mount do |context|
|
7
|
+
context.eval("window.reactrb_router_static_location = '#{context.controller.request.path}?#{context.controller.request.query_string}'")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
if RUBY_ENGINE == 'opal'
|
2
|
+
require 'reactrb'
|
3
|
+
require 'reactrb-router/component'
|
4
|
+
require 'reactrb-router/history'
|
5
|
+
require 'reactrb-router/router'
|
6
|
+
require 'reactrb-router/version'
|
7
|
+
else
|
8
|
+
require 'opal'
|
9
|
+
require 'reactrb'
|
10
|
+
require 'reactrb-router/window_location'
|
11
|
+
require 'reactrb-router/version'
|
12
|
+
|
13
|
+
Opal.append_path File.expand_path('../', __FILE__).untaint
|
14
|
+
Opal.append_path File.expand_path('../../vendor', __FILE__).untaint
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: reactrb-router
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.8.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Adam George
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-06-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.8'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.8'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: reactrb
|
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
|
+
description: Adds the ability to write and use the react-router in Ruby through Opal
|
56
|
+
email:
|
57
|
+
- adamgeorge.31@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- LICENSE
|
63
|
+
- README.md
|
64
|
+
- Rakefile
|
65
|
+
- lib/reactrb-router.rb
|
66
|
+
- lib/reactrb-router/component.rb
|
67
|
+
- lib/reactrb-router/history.rb
|
68
|
+
- lib/reactrb-router/router.rb
|
69
|
+
- lib/reactrb-router/version.rb
|
70
|
+
- lib/reactrb-router/window_location.rb
|
71
|
+
homepage:
|
72
|
+
licenses: []
|
73
|
+
metadata: {}
|
74
|
+
post_install_message:
|
75
|
+
rdoc_options: []
|
76
|
+
require_paths:
|
77
|
+
- lib
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
requirements: []
|
89
|
+
rubyforge_project:
|
90
|
+
rubygems_version: 2.5.1
|
91
|
+
signing_key:
|
92
|
+
specification_version: 4
|
93
|
+
summary: react-router for Opal, part of the reactrb gem family
|
94
|
+
test_files: []
|