optimism 0.2.11 → 0.3.2
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/Gemfile.lock +64 -64
- 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 +2 -2
- data/reference.md +2 -2
- data/setup.md +16 -2
- metadata +15 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e9ef3c31e6d2b3a6505e1e75ccc288669585e79bac805929c958d6a34158b39f
|
|
4
|
+
data.tar.gz: a90c0cc4dd2ab94fc8871306b26b9d4384bc7c5311846521ee0f475ab0f8caa5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 192cd07601317a751e09fed5aedcf7eacfaa2efbb3a1bd9c09abb116b9d75a98700ce0e6a185ae08cc3c0a3310b544b8a769e02a83dfb94c80a8abc91c06c87e
|
|
7
|
+
data.tar.gz: 0373c6ed5264430d53b4c84a0361cddbe5c6e6b084b47a38b8990ea957c94d8210139b4668198ec3c8724bbd1ea709ef9a83c02432eedc3bc68e1a40f8a44d89
|
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,7 +155,7 @@ 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)
|
|
@@ -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
|
-
[](https://github.com/leastbad/optimism) [](https://github.com/leastbad/optimism) [](https://twitter.com/theleastbad)
|
|
3
|
+
[](https://github.com/leastbad/optimism) [](https://github.com/leastbad/optimism) [](https://twitter.com/theleastbad) [](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
|

|
|
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
|
@@ -26,6 +26,6 @@ Gem::Specification.new do |spec|
|
|
|
26
26
|
spec.add_development_dependency "pry-nav", "~> 0.3.0"
|
|
27
27
|
spec.add_development_dependency "standardrb", "~> 1.0.0"
|
|
28
28
|
spec.add_dependency "rack", "~> 2.0"
|
|
29
|
-
spec.add_dependency "rails", "
|
|
30
|
-
spec.add_dependency "cable_ready", "
|
|
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
|

|
|
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,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: optimism
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2
|
|
4
|
+
version: 0.3.2
|
|
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-05-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -98,30 +98,36 @@ dependencies:
|
|
|
98
98
|
name: rails
|
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
|
100
100
|
requirements:
|
|
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
|
|
107
110
|
requirements:
|
|
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
|