optimism 0.2.12 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +65 -65
- data/README.md +2 -2
- data/SUMMARY.md +1 -0
- data/authentication.md +149 -0
- data/lib/optimism.rb +36 -27
- data/lib/optimism/version.rb +1 -1
- data/optimism.gemspec +8 -8
- data/reference.md +2 -2
- data/setup.md +16 -2
- metadata +23 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86be504b270b8c5ec388d09a1bdceaf7a415becb0847e911cd0a4f31ed756145
|
4
|
+
data.tar.gz: 351510dbb07f54523d3bf16e2c9412f1c9c9edd8423dfa371713d49988936765
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89dd0cfe1095d4d1a63233195e2d9c69ab04298995406f3e035880cfd81d531a9b0407204ad875867340f0a3e03e5d5ac8bb286dd90dd47bd0b3e895d209cc6c
|
7
|
+
data.tar.gz: 11721e72984cbeeae273a9a5096a4b743b21415b2bb5ef543cb00cb0059841b1522b6cf9ff6b22aeebb8fb3324026833b64399aee6f38749e63e0bd2944c9016
|
data/Gemfile.lock
CHANGED
@@ -1,72 +1,72 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
optimism (0.
|
5
|
-
cable_ready (
|
6
|
-
rack
|
7
|
-
rails (>= 5.2)
|
4
|
+
optimism (0.3.1)
|
5
|
+
cable_ready (>= 4)
|
6
|
+
rack (~> 2.0)
|
7
|
+
rails (>= 6, >= 5.2)
|
8
8
|
|
9
9
|
GEM
|
10
10
|
remote: https://rubygems.org/
|
11
11
|
specs:
|
12
|
-
actioncable (6.0.
|
13
|
-
actionpack (= 6.0.
|
12
|
+
actioncable (6.0.3.1)
|
13
|
+
actionpack (= 6.0.3.1)
|
14
14
|
nio4r (~> 2.0)
|
15
15
|
websocket-driver (>= 0.6.1)
|
16
|
-
actionmailbox (6.0.
|
17
|
-
actionpack (= 6.0.
|
18
|
-
activejob (= 6.0.
|
19
|
-
activerecord (= 6.0.
|
20
|
-
activestorage (= 6.0.
|
21
|
-
activesupport (= 6.0.
|
16
|
+
actionmailbox (6.0.3.1)
|
17
|
+
actionpack (= 6.0.3.1)
|
18
|
+
activejob (= 6.0.3.1)
|
19
|
+
activerecord (= 6.0.3.1)
|
20
|
+
activestorage (= 6.0.3.1)
|
21
|
+
activesupport (= 6.0.3.1)
|
22
22
|
mail (>= 2.7.1)
|
23
|
-
actionmailer (6.0.
|
24
|
-
actionpack (= 6.0.
|
25
|
-
actionview (= 6.0.
|
26
|
-
activejob (= 6.0.
|
23
|
+
actionmailer (6.0.3.1)
|
24
|
+
actionpack (= 6.0.3.1)
|
25
|
+
actionview (= 6.0.3.1)
|
26
|
+
activejob (= 6.0.3.1)
|
27
27
|
mail (~> 2.5, >= 2.5.4)
|
28
28
|
rails-dom-testing (~> 2.0)
|
29
|
-
actionpack (6.0.
|
30
|
-
actionview (= 6.0.
|
31
|
-
activesupport (= 6.0.
|
29
|
+
actionpack (6.0.3.1)
|
30
|
+
actionview (= 6.0.3.1)
|
31
|
+
activesupport (= 6.0.3.1)
|
32
32
|
rack (~> 2.0, >= 2.0.8)
|
33
33
|
rack-test (>= 0.6.3)
|
34
34
|
rails-dom-testing (~> 2.0)
|
35
35
|
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
36
|
-
actiontext (6.0.
|
37
|
-
actionpack (= 6.0.
|
38
|
-
activerecord (= 6.0.
|
39
|
-
activestorage (= 6.0.
|
40
|
-
activesupport (= 6.0.
|
36
|
+
actiontext (6.0.3.1)
|
37
|
+
actionpack (= 6.0.3.1)
|
38
|
+
activerecord (= 6.0.3.1)
|
39
|
+
activestorage (= 6.0.3.1)
|
40
|
+
activesupport (= 6.0.3.1)
|
41
41
|
nokogiri (>= 1.8.5)
|
42
|
-
actionview (6.0.
|
43
|
-
activesupport (= 6.0.
|
42
|
+
actionview (6.0.3.1)
|
43
|
+
activesupport (= 6.0.3.1)
|
44
44
|
builder (~> 3.1)
|
45
45
|
erubi (~> 1.4)
|
46
46
|
rails-dom-testing (~> 2.0)
|
47
47
|
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
48
|
-
activejob (6.0.
|
49
|
-
activesupport (= 6.0.
|
48
|
+
activejob (6.0.3.1)
|
49
|
+
activesupport (= 6.0.3.1)
|
50
50
|
globalid (>= 0.3.6)
|
51
|
-
activemodel (6.0.
|
52
|
-
activesupport (= 6.0.
|
53
|
-
activerecord (6.0.
|
54
|
-
activemodel (= 6.0.
|
55
|
-
activesupport (= 6.0.
|
56
|
-
activestorage (6.0.
|
57
|
-
actionpack (= 6.0.
|
58
|
-
activejob (= 6.0.
|
59
|
-
activerecord (= 6.0.
|
51
|
+
activemodel (6.0.3.1)
|
52
|
+
activesupport (= 6.0.3.1)
|
53
|
+
activerecord (6.0.3.1)
|
54
|
+
activemodel (= 6.0.3.1)
|
55
|
+
activesupport (= 6.0.3.1)
|
56
|
+
activestorage (6.0.3.1)
|
57
|
+
actionpack (= 6.0.3.1)
|
58
|
+
activejob (= 6.0.3.1)
|
59
|
+
activerecord (= 6.0.3.1)
|
60
60
|
marcel (~> 0.3.1)
|
61
|
-
activesupport (6.0.
|
61
|
+
activesupport (6.0.3.1)
|
62
62
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
63
63
|
i18n (>= 0.7, < 2)
|
64
64
|
minitest (~> 5.1)
|
65
65
|
tzinfo (~> 1.1)
|
66
|
-
zeitwerk (~> 2.2)
|
66
|
+
zeitwerk (~> 2.2, >= 2.2.2)
|
67
67
|
ast (2.4.0)
|
68
68
|
builder (3.2.4)
|
69
|
-
cable_ready (4.
|
69
|
+
cable_ready (4.1.2)
|
70
70
|
rails (>= 5.2)
|
71
71
|
coderay (1.1.2)
|
72
72
|
concurrent-ruby (1.1.6)
|
@@ -77,7 +77,7 @@ GEM
|
|
77
77
|
i18n (1.8.2)
|
78
78
|
concurrent-ruby (~> 1.0)
|
79
79
|
jaro_winkler (1.5.4)
|
80
|
-
loofah (2.
|
80
|
+
loofah (2.5.0)
|
81
81
|
crass (~> 1.0.2)
|
82
82
|
nokogiri (>= 1.5.9)
|
83
83
|
mail (2.7.1)
|
@@ -85,10 +85,10 @@ GEM
|
|
85
85
|
marcel (0.3.3)
|
86
86
|
mimemagic (~> 0.3.2)
|
87
87
|
method_source (0.9.2)
|
88
|
-
mimemagic (0.3.
|
88
|
+
mimemagic (0.3.5)
|
89
89
|
mini_mime (1.0.2)
|
90
90
|
mini_portile2 (2.4.0)
|
91
|
-
minitest (5.14.
|
91
|
+
minitest (5.14.1)
|
92
92
|
nio4r (2.5.2)
|
93
93
|
nokogiri (1.10.9)
|
94
94
|
mini_portile2 (~> 2.4.0)
|
@@ -103,29 +103,29 @@ GEM
|
|
103
103
|
rack (2.2.2)
|
104
104
|
rack-test (1.1.0)
|
105
105
|
rack (>= 1.0, < 3)
|
106
|
-
rails (6.0.
|
107
|
-
actioncable (= 6.0.
|
108
|
-
actionmailbox (= 6.0.
|
109
|
-
actionmailer (= 6.0.
|
110
|
-
actionpack (= 6.0.
|
111
|
-
actiontext (= 6.0.
|
112
|
-
actionview (= 6.0.
|
113
|
-
activejob (= 6.0.
|
114
|
-
activemodel (= 6.0.
|
115
|
-
activerecord (= 6.0.
|
116
|
-
activestorage (= 6.0.
|
117
|
-
activesupport (= 6.0.
|
106
|
+
rails (6.0.3.1)
|
107
|
+
actioncable (= 6.0.3.1)
|
108
|
+
actionmailbox (= 6.0.3.1)
|
109
|
+
actionmailer (= 6.0.3.1)
|
110
|
+
actionpack (= 6.0.3.1)
|
111
|
+
actiontext (= 6.0.3.1)
|
112
|
+
actionview (= 6.0.3.1)
|
113
|
+
activejob (= 6.0.3.1)
|
114
|
+
activemodel (= 6.0.3.1)
|
115
|
+
activerecord (= 6.0.3.1)
|
116
|
+
activestorage (= 6.0.3.1)
|
117
|
+
activesupport (= 6.0.3.1)
|
118
118
|
bundler (>= 1.3.0)
|
119
|
-
railties (= 6.0.
|
119
|
+
railties (= 6.0.3.1)
|
120
120
|
sprockets-rails (>= 2.0.0)
|
121
121
|
rails-dom-testing (2.0.3)
|
122
122
|
activesupport (>= 4.2.0)
|
123
123
|
nokogiri (>= 1.6)
|
124
124
|
rails-html-sanitizer (1.3.0)
|
125
125
|
loofah (~> 2.3)
|
126
|
-
railties (6.0.
|
127
|
-
actionpack (= 6.0.
|
128
|
-
activesupport (= 6.0.
|
126
|
+
railties (6.0.3.1)
|
127
|
+
actionpack (= 6.0.3.1)
|
128
|
+
activesupport (= 6.0.3.1)
|
129
129
|
method_source
|
130
130
|
rake (>= 0.8.7)
|
131
131
|
thor (>= 0.20.3, < 2.0)
|
@@ -155,12 +155,12 @@ GEM
|
|
155
155
|
standard
|
156
156
|
thor (1.0.1)
|
157
157
|
thread_safe (0.3.6)
|
158
|
-
tzinfo (1.2.
|
158
|
+
tzinfo (1.2.7)
|
159
159
|
thread_safe (~> 0.1)
|
160
160
|
unicode-display_width (1.6.1)
|
161
161
|
websocket-driver (0.7.1)
|
162
162
|
websocket-extensions (>= 0.1.0)
|
163
|
-
websocket-extensions (0.1.
|
163
|
+
websocket-extensions (0.1.5)
|
164
164
|
zeitwerk (2.3.0)
|
165
165
|
|
166
166
|
PLATFORMS
|
@@ -169,10 +169,10 @@ PLATFORMS
|
|
169
169
|
DEPENDENCIES
|
170
170
|
bundler (~> 2.0)
|
171
171
|
optimism!
|
172
|
-
pry
|
173
|
-
pry-nav
|
172
|
+
pry (~> 0.12.2)
|
173
|
+
pry-nav (~> 0.3.0)
|
174
174
|
rake (~> 13.0)
|
175
|
-
standardrb
|
175
|
+
standardrb (~> 1.0.0)
|
176
176
|
|
177
177
|
BUNDLED WITH
|
178
|
-
2.
|
178
|
+
2.1.4
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Optimism
|
2
2
|
|
3
|
-
[![GitHub stars](https://img.shields.io/github/stars/leastbad/optimism?style=social)](https://github.com/leastbad/optimism) [![GitHub forks](https://img.shields.io/github/forks/leastbad/optimism?style=social)](https://github.com/leastbad/optimism) [![Twitter follow](https://img.shields.io/twitter/follow/theleastbad?style=social)](https://twitter.com/theleastbad)
|
3
|
+
[![GitHub stars](https://img.shields.io/github/stars/leastbad/optimism?style=social)](https://github.com/leastbad/optimism) [![GitHub forks](https://img.shields.io/github/forks/leastbad/optimism?style=social)](https://github.com/leastbad/optimism) [![Twitter follow](https://img.shields.io/twitter/follow/theleastbad?style=social)](https://twitter.com/theleastbad) [![Discord](https://img.shields.io/discord/681373845323513862)](https://discord.gg/GnweR3)
|
4
4
|
|
5
5
|
The missing drop-in solution for realtime remote form validation in Rails.
|
6
6
|
|
@@ -8,7 +8,7 @@ The missing drop-in solution for realtime remote form validation in Rails.
|
|
8
8
|
|
9
9
|
[Optimism](https://github.com/leastbad/optimism) is an MIT-licensed [Ruby on Rails](https://rubyonrails.org/) gem that makes it easy to give your users instant constructive feedback if they enter invalid data into your application. Instead of dumping a list of errors at the top of your interface, Optimism provides specific instructions directly beside or below individual input elements.
|
10
10
|
|
11
|
-
You can try a [live demo](https://optimism-demo.herokuapp.com) right now.
|
11
|
+
You can try a 👉 [live demo](https://optimism-demo.herokuapp.com) 👈 right now.
|
12
12
|
|
13
13
|
![](.gitbook/assets/fill_forms.svg)
|
14
14
|
|
data/SUMMARY.md
CHANGED
data/authentication.md
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
---
|
2
|
+
description: Practice safe optimism
|
3
|
+
---
|
4
|
+
|
5
|
+
# Authentication
|
6
|
+
|
7
|
+
If you're just trying to bootstrap a proof-of-concept application on your local workstation, you don't technically have to worry about giving ActionCable the ability to distinguish between multiple concurrent users. However, **the moment you deploy to a host with more than one person accessing your app, you'll find that you're sharing a session and seeing other people's updates**. That isn't what most developers have in mind.
|
8
|
+
|
9
|
+
## Encrypted Session Cookies
|
10
|
+
|
11
|
+
You can use your default Rails encrypted cookie-based sessions to isolate your users into their own sessions. This works great even if your application doesn't have a login system.
|
12
|
+
|
13
|
+
{% code title="app/controllers/application\_controller.rb" %}
|
14
|
+
```ruby
|
15
|
+
class ApplicationController < ActionController::Base
|
16
|
+
before_action :set_action_cable_identifier
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def set_action_cable_identifier
|
21
|
+
cookies.encrypted[:session_id] = session.id.to_s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
```
|
25
|
+
{% endcode %}
|
26
|
+
|
27
|
+
{% code title="app/channels/application\_cable/connection.rb" %}
|
28
|
+
```ruby
|
29
|
+
module ApplicationCable
|
30
|
+
class Connection < ActionCable::Connection::Base
|
31
|
+
identified_by :session_id
|
32
|
+
|
33
|
+
def connect
|
34
|
+
self.session_id = cookies.encrypted[:session_id]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
```
|
39
|
+
{% endcode %}
|
40
|
+
|
41
|
+
We need to instruct ActionCable to stream updates on a per-session basis:
|
42
|
+
|
43
|
+
{% code title="app/channels/optimism_channel.rb" %}
|
44
|
+
```ruby
|
45
|
+
class OptimismChannel < ApplicationCable::Channel
|
46
|
+
def subscribed
|
47
|
+
stream_for session_id
|
48
|
+
end
|
49
|
+
end
|
50
|
+
```
|
51
|
+
{% endcode %}
|
52
|
+
|
53
|
+
Finally, we need to give Optimism the ability to broadcast updates to the correct channel subscription. We will override Optimism's default "OptimismChannel" with a lambda that returns the subscription identifier. You might already have other values in your initializer, or it might not yet exist at all:
|
54
|
+
|
55
|
+
{% code title="config/initializers/optimism.rb" %}
|
56
|
+
```ruby
|
57
|
+
Optimism.configure do |config|
|
58
|
+
config.channel = ->(context) { OptimismChannel.broadcasting_for(context.session.id) }
|
59
|
+
end
|
60
|
+
```
|
61
|
+
{% endcode %}
|
62
|
+
|
63
|
+
## User-based Authentication
|
64
|
+
|
65
|
+
Many Rails apps use the current\_user convention or more recently, the [Current](https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html) object to provide a global user context. This gives access to the user scope from _almost_ all parts of your application.
|
66
|
+
|
67
|
+
{% code title="app/controllers/application\_controller.rb " %}
|
68
|
+
```ruby
|
69
|
+
class ApplicationController < ActionController::Base
|
70
|
+
before_action :set_action_cable_identifier
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def set_action_cable_identifier
|
75
|
+
cookies.encrypted[:user_id] = current_user&.id
|
76
|
+
end
|
77
|
+
end
|
78
|
+
```
|
79
|
+
{% endcode %}
|
80
|
+
|
81
|
+
{% code title="app/channels/application\_cable/connection.rb " %}
|
82
|
+
```ruby
|
83
|
+
module ApplicationCable
|
84
|
+
class Connection < ActionCable::Connection::Base
|
85
|
+
identified_by :current_user
|
86
|
+
|
87
|
+
def connect
|
88
|
+
user_id = cookies.encrypted[:user_id]
|
89
|
+
return reject_unauthorized_connection if user_id.nil?
|
90
|
+
user = User.find_by(id: user_id)
|
91
|
+
return reject_unauthorized_connection if user.nil?
|
92
|
+
self.current_user = user
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
```
|
97
|
+
{% endcode %}
|
98
|
+
|
99
|
+
We need to instruct ActionCable to stream updates on a per-session basis:
|
100
|
+
|
101
|
+
{% code title="app/channels/optimism_channel.rb" %}
|
102
|
+
```ruby
|
103
|
+
class OptimismChannel < ApplicationCable::Channel
|
104
|
+
def subscribed
|
105
|
+
stream_for current_user
|
106
|
+
end
|
107
|
+
end
|
108
|
+
```
|
109
|
+
{% endcode %}
|
110
|
+
|
111
|
+
Finally, we need to give Optimism the ability to broadcast updates to the correct channel subscription. We will override Optimism's default "OptimismChannel" with a lambda that returns the subscription identifier. You might already have other values in your initializer, or it might not yet exist at all:
|
112
|
+
|
113
|
+
{% code title="config/initializers/optimism.rb" %}
|
114
|
+
```ruby
|
115
|
+
Optimism.configure do |config|
|
116
|
+
config.channel = ->(context) { OptimismChannel.broadcasting_for(context.current_user) }
|
117
|
+
end
|
118
|
+
```
|
119
|
+
{% endcode %}
|
120
|
+
|
121
|
+
## Devise-based Authentication
|
122
|
+
|
123
|
+
If you're using the versatile [Devise](https://github.com/plataformatec/devise) authentication library, your configuration is *identical* to the User-Based Authentication above, **except** for how ActionCable looks up a user:
|
124
|
+
|
125
|
+
{% code title="app/channels/application\_cable/connection.rb" %}
|
126
|
+
```ruby
|
127
|
+
module ApplicationCable
|
128
|
+
class Connection < ActionCable::Connection::Base
|
129
|
+
identified_by :current_user
|
130
|
+
|
131
|
+
def connect
|
132
|
+
self.current_user = find_verified_user
|
133
|
+
end
|
134
|
+
|
135
|
+
protected
|
136
|
+
|
137
|
+
def find_verified_user
|
138
|
+
if current_user = env["warden"].user
|
139
|
+
current_user
|
140
|
+
else
|
141
|
+
reject_unauthorized_connection
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
```
|
147
|
+
{% endcode %}
|
148
|
+
|
149
|
+
|
data/lib/optimism.rb
CHANGED
@@ -6,7 +6,7 @@ module Optimism
|
|
6
6
|
include CableReady::Broadcaster
|
7
7
|
class << self
|
8
8
|
mattr_accessor :channel, :form_class, :error_class, :disable_submit, :suffix, :emit_events, :add_css, :inject_inline, :container_selector, :error_selector, :form_selector, :submit_selector
|
9
|
-
self.channel = "OptimismChannel"
|
9
|
+
self.channel = ->(context) { "OptimismChannel" }
|
10
10
|
self.form_class = "invalid"
|
11
11
|
self.error_class = "error"
|
12
12
|
self.disable_submit = false
|
@@ -26,39 +26,44 @@ module Optimism
|
|
26
26
|
|
27
27
|
def broadcast_errors(model, attributes)
|
28
28
|
return unless model&.errors&.messages
|
29
|
-
resource = model.class.to_s.
|
29
|
+
resource = model.class.to_s.underscore
|
30
30
|
form_selector, submit_selector = Optimism.form_selector.sub("RESOURCE", resource), Optimism.submit_selector.sub("RESOURCE", resource)
|
31
31
|
attributes = case attributes
|
32
32
|
when ActionController::Parameters, Hash, ActiveSupport::HashWithIndifferentAccess
|
33
|
-
attributes.to_h
|
33
|
+
attributes.to_h
|
34
34
|
when String, Symbol
|
35
|
-
|
35
|
+
{ attributes.to_s => nil }
|
36
36
|
when Array
|
37
|
-
attributes.flatten.
|
37
|
+
attributes.flatten.each.with_object({}) { |attr, obj| obj[attr.to_s] = nil }
|
38
38
|
else
|
39
39
|
raise Exception.new "attributes must be a Hash (Parameters, Indifferent or standard), Array, Symbol or String"
|
40
40
|
end
|
41
|
+
model.valid? if model.errors.empty?
|
41
42
|
process_resource(model, attributes, [resource])
|
42
43
|
if model.errors.any?
|
43
|
-
cable_ready[Optimism.channel].dispatch_event(name: "optimism:form:invalid", detail: {resource: resource}) if Optimism.emit_events
|
44
|
-
cable_ready[Optimism.channel].add_css_class(selector: form_selector, name: Optimism.form_class) if Optimism.form_class.present?
|
45
|
-
cable_ready[Optimism.channel].set_attribute(selector: submit_selector, name: "disabled") if Optimism.disable_submit
|
44
|
+
cable_ready[Optimism.channel[self]].dispatch_event(name: "optimism:form:invalid", detail: {resource: resource}) if Optimism.emit_events
|
45
|
+
cable_ready[Optimism.channel[self]].add_css_class(selector: form_selector, name: Optimism.form_class) if Optimism.form_class.present?
|
46
|
+
cable_ready[Optimism.channel[self]].set_attribute(selector: submit_selector, name: "disabled") if Optimism.disable_submit
|
46
47
|
else
|
47
|
-
cable_ready[Optimism.channel].dispatch_event(name: "optimism:form:valid", detail: {resource: resource}) if Optimism.emit_events
|
48
|
-
cable_ready[Optimism.channel].remove_css_class(selector: form_selector, name: Optimism.form_class) if Optimism.form_class.present?
|
49
|
-
cable_ready[Optimism.channel].remove_attribute(selector: submit_selector, name: "disabled") if Optimism.disable_submit
|
48
|
+
cable_ready[Optimism.channel[self]].dispatch_event(name: "optimism:form:valid", detail: {resource: resource}) if Optimism.emit_events
|
49
|
+
cable_ready[Optimism.channel[self]].remove_css_class(selector: form_selector, name: Optimism.form_class) if Optimism.form_class.present?
|
50
|
+
cable_ready[Optimism.channel[self]].remove_attribute(selector: submit_selector, name: "disabled") if Optimism.disable_submit
|
50
51
|
end
|
51
52
|
cable_ready.broadcast
|
52
|
-
head :ok
|
53
|
+
head :ok if defined?(head)
|
53
54
|
end
|
54
55
|
|
55
56
|
def process_resource(model, attributes, ancestry)
|
56
|
-
attributes.each do |attribute|
|
57
|
+
attributes.keys.each do |attribute|
|
57
58
|
if attribute.ends_with?("_attributes")
|
58
59
|
resource = attribute[0..-12]
|
59
|
-
|
60
|
-
|
61
|
-
|
60
|
+
association = model.send(resource.to_sym)
|
61
|
+
if association.respond_to? :each_with_index
|
62
|
+
association.each_with_index do |nested, index|
|
63
|
+
process_resource(nested, attributes[attribute][index.to_s], ancestry + [resource, index]) if attributes[attribute].key?(index.to_s)
|
64
|
+
end
|
65
|
+
else
|
66
|
+
process_resource(association, attributes[attribute], ancestry + [resource])
|
62
67
|
end
|
63
68
|
else
|
64
69
|
process_attribute(model, attribute, ancestry.dup)
|
@@ -68,17 +73,21 @@ module Optimism
|
|
68
73
|
|
69
74
|
def process_attribute(model, attribute, ancestry)
|
70
75
|
resource = ancestry.shift
|
71
|
-
|
76
|
+
if ancestry.size == 1
|
77
|
+
resource += "_#{ancestry.shift}_attributes"
|
78
|
+
else
|
79
|
+
resource += "_#{ancestry.shift}_attributes_#{ancestry.shift}" until ancestry.empty?
|
80
|
+
end
|
72
81
|
container_selector, error_selector = Optimism.container_selector.sub("RESOURCE", resource).sub("ATTRIBUTE", attribute), Optimism.error_selector.sub("RESOURCE", resource).sub("ATTRIBUTE", attribute)
|
73
|
-
if model.errors.messages.map(&:first).include?(attribute.to_sym)
|
74
|
-
message = "#{model.errors.full_message(attribute.
|
75
|
-
cable_ready[Optimism.channel].dispatch_event(name: "optimism:attribute:invalid", detail: {resource: resource, attribute: attribute, text: message}) if Optimism.emit_events
|
76
|
-
cable_ready[Optimism.channel].add_css_class(selector: container_selector, name: Optimism.error_class) if Optimism.add_css
|
77
|
-
cable_ready[Optimism.channel].text_content(selector: error_selector, text: message) if Optimism.inject_inline
|
82
|
+
if model.errors.any? && model.errors.messages.map(&:first).include?(attribute.to_sym)
|
83
|
+
message = "#{model.errors.full_message(attribute.to_sym, model.errors.messages[attribute.to_sym].first)}#{Optimism.suffix}"
|
84
|
+
cable_ready[Optimism.channel[self]].dispatch_event(name: "optimism:attribute:invalid", detail: {resource: resource, attribute: attribute, text: message}) if Optimism.emit_events
|
85
|
+
cable_ready[Optimism.channel[self]].add_css_class(selector: container_selector, name: Optimism.error_class) if Optimism.add_css
|
86
|
+
cable_ready[Optimism.channel[self]].text_content(selector: error_selector, text: message) if Optimism.inject_inline
|
78
87
|
else
|
79
|
-
cable_ready[Optimism.channel].dispatch_event(name: "optimism:attribute:valid", detail: {resource: resource, attribute: attribute}) if Optimism.emit_events
|
80
|
-
cable_ready[Optimism.channel].remove_css_class(selector: container_selector, name: Optimism.error_class) if Optimism.add_css
|
81
|
-
cable_ready[Optimism.channel].text_content(selector: error_selector, text: "") if Optimism.inject_inline
|
88
|
+
cable_ready[Optimism.channel[self]].dispatch_event(name: "optimism:attribute:valid", detail: {resource: resource, attribute: attribute}) if Optimism.emit_events
|
89
|
+
cable_ready[Optimism.channel[self]].remove_css_class(selector: container_selector, name: Optimism.error_class) if Optimism.add_css
|
90
|
+
cable_ready[Optimism.channel[self]].text_content(selector: error_selector, text: "") if Optimism.inject_inline
|
82
91
|
end
|
83
92
|
end
|
84
93
|
end
|
@@ -90,7 +99,7 @@ module ActionView::Helpers
|
|
90
99
|
end
|
91
100
|
|
92
101
|
def container_id_for(attribute)
|
93
|
-
Optimism.container_selector.sub("RESOURCE", object_name.delete("]").tr("[", "_")).sub("ATTRIBUTE", attribute.to_s)[1..-1]
|
102
|
+
Optimism.container_selector.sub("RESOURCE", object_name.to_s.delete("]").tr("[", "_")).sub("ATTRIBUTE", attribute.to_s)[1..-1]
|
94
103
|
end
|
95
104
|
|
96
105
|
def error_for(attribute, **options)
|
@@ -98,7 +107,7 @@ module ActionView::Helpers
|
|
98
107
|
end
|
99
108
|
|
100
109
|
def error_id_for(attribute)
|
101
|
-
Optimism.error_selector.sub("RESOURCE", object_name.delete("]").tr("[", "_")).sub("ATTRIBUTE", attribute.to_s)[1..-1]
|
110
|
+
Optimism.error_selector.sub("RESOURCE", object_name.to_s.delete("]").tr("[", "_")).sub("ATTRIBUTE", attribute.to_s)[1..-1]
|
102
111
|
end
|
103
112
|
end
|
104
113
|
end
|
data/lib/optimism/version.rb
CHANGED
data/optimism.gemspec
CHANGED
@@ -20,12 +20,12 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
21
|
spec.require_paths = ["lib"]
|
22
22
|
|
23
|
-
spec.add_development_dependency "bundler", "
|
24
|
-
spec.add_development_dependency "rake", "
|
25
|
-
spec.add_development_dependency "pry", "
|
26
|
-
spec.add_development_dependency "pry-nav", "
|
27
|
-
spec.add_development_dependency "standardrb", "
|
28
|
-
spec.add_dependency "rack", "
|
29
|
-
spec.add_dependency "rails", ">= 5.2"
|
30
|
-
spec.add_dependency "cable_ready", ">= 4
|
23
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
24
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
25
|
+
spec.add_development_dependency "pry", "~> 0.12.2"
|
26
|
+
spec.add_development_dependency "pry-nav", "~> 0.3.0"
|
27
|
+
spec.add_development_dependency "standardrb", "~> 1.0.0"
|
28
|
+
spec.add_dependency "rack", "~> 2.0"
|
29
|
+
spec.add_dependency "rails", [">= 5.2", ">= 6"]
|
30
|
+
spec.add_dependency "cable_ready", ">= 4"
|
31
31
|
end
|
data/reference.md
CHANGED
@@ -93,7 +93,7 @@ Optimism is configurable via an optional initializer file. As with all initializ
|
|
93
93
|
{% code title="config/initializers/optimism.rb" %}
|
94
94
|
```ruby
|
95
95
|
Optimism.configure do |config|
|
96
|
-
config.channel = "OptimismChannel"
|
96
|
+
config.channel = ->(context) { "OptimismChannel" }
|
97
97
|
config.form_class = "invalid"
|
98
98
|
config.error_class = "error"
|
99
99
|
config.disable_submit = false
|
@@ -109,7 +109,7 @@ end
|
|
109
109
|
```
|
110
110
|
{% endcode %}
|
111
111
|
|
112
|
-
**channel**: The ActionCable channel created by the `rake optimism:install` setup task.
|
112
|
+
**channel**: The ActionCable channel created by the `rake optimism:install` setup task. Good enough to get you up and running in development, you will need to pull your desired identifier from the context Optimism is running in and let the `broadcasting_for` method on `OptimismChannel` call the shots. Find out more on the [authentication](https://optimism.leastbad.com/authentication) page.
|
113
113
|
|
114
114
|
**form\_class**: The CSS class that will be applied to the form if the id has been properly set eg. `posts_form` \(following the simple pattern **resources\_form**\). If form\_class is set to false or nil, no CSS class will be applied.
|
115
115
|
|
data/setup.md
CHANGED
@@ -18,7 +18,21 @@ The terminal commands above will ensure that Optimism is installed. It creates t
|
|
18
18
|
|
19
19
|
![](.gitbook/assets/setup.svg)
|
20
20
|
|
21
|
-
|
21
|
+
# Authentication
|
22
|
+
|
23
|
+
{% hint style="info" %}
|
24
|
+
If you're just experimenting with Optimism or trying to bootstrap a proof-of-concept application on your local workstation, you can actually skip this section until you're planning to deploy.
|
25
|
+
{% endhint %}
|
26
|
+
|
27
|
+
Out of the box, ActionCable doesn't give Optimism the ability to distinguish between multiple concurrent users looking at the same page.
|
28
|
+
|
29
|
+
**If you deploy to a host with more than one person accessing your app, you'll find that you're sharing a session and seeing other people's updates.** That isn't what most developers have in mind!
|
30
|
+
|
31
|
+
When the time comes, it's easy to configure your application to support authenticating users by their Rails session or current_user scope. Just check out the Authentication page and choose your own adventure.
|
32
|
+
|
33
|
+
{% page-ref page="authentication.md" %}
|
34
|
+
|
35
|
+
# Logging
|
22
36
|
|
23
37
|
In the default _debug_ log level, ActionCable emits particularly verbose log messages. You can optionally discard everything but exceptions by switching to the _warn_ log level, as is common in development environments:
|
24
38
|
|
@@ -29,7 +43,7 @@ config.log_level = :warn
|
|
29
43
|
```
|
30
44
|
{% endcode %}
|
31
45
|
|
32
|
-
|
46
|
+
# Troubleshooting
|
33
47
|
|
34
48
|
{% hint style="info" %}
|
35
49
|
If _something_ goes wrong, it's often because of the **spring** gem. You can test this by temporarily setting the `DISABLE_SPRING=1` environment variable and restarting your server.
|
metadata
CHANGED
@@ -1,97 +1,97 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: optimism
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- leastbad
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-06-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '2.0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '2.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '13.0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '13.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: pry
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: 0.12.2
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 0.12.2
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: pry-nav
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: 0.3.0
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 0.3.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: standardrb
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - "
|
73
|
+
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: 1.0.0
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - "
|
80
|
+
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 1.0.0
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: rack
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: '2.0'
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - "
|
94
|
+
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '2.0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
@@ -101,6 +101,9 @@ dependencies:
|
|
101
101
|
- - ">="
|
102
102
|
- !ruby/object:Gem::Version
|
103
103
|
version: '5.2'
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '6'
|
104
107
|
type: :runtime
|
105
108
|
prerelease: false
|
106
109
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -108,20 +111,23 @@ dependencies:
|
|
108
111
|
- - ">="
|
109
112
|
- !ruby/object:Gem::Version
|
110
113
|
version: '5.2'
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '6'
|
111
117
|
- !ruby/object:Gem::Dependency
|
112
118
|
name: cable_ready
|
113
119
|
requirement: !ruby/object:Gem::Requirement
|
114
120
|
requirements:
|
115
121
|
- - ">="
|
116
122
|
- !ruby/object:Gem::Version
|
117
|
-
version: '4
|
123
|
+
version: '4'
|
118
124
|
type: :runtime
|
119
125
|
prerelease: false
|
120
126
|
version_requirements: !ruby/object:Gem::Requirement
|
121
127
|
requirements:
|
122
128
|
- - ">="
|
123
129
|
- !ruby/object:Gem::Version
|
124
|
-
version: '4
|
130
|
+
version: '4'
|
125
131
|
description: Realtime remote form input validations delivered via websockets
|
126
132
|
email:
|
127
133
|
- hello@leastbad.com
|
@@ -148,6 +154,7 @@ files:
|
|
148
154
|
- Rakefile
|
149
155
|
- SUMMARY.md
|
150
156
|
- advanced-usage.md
|
157
|
+
- authentication.md
|
151
158
|
- bin/console
|
152
159
|
- bin/loc
|
153
160
|
- bin/setup
|