optimism 0.2.13 → 0.3.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 +4 -4
- data/README.md +2 -2
- data/SUMMARY.md +1 -0
- data/authentication.md +149 -0
- data/lib/optimism/version.rb +1 -1
- data/lib/optimism.rb +23 -22
- data/optimism.gemspec +8 -8
- data/reference.md +2 -2
- data/setup.md +16 -2
- metadata +19 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2e971263a8aba08305a366ceaee0a6d33c3c8bdaab38e084581311ac13542cf5
|
4
|
+
data.tar.gz: e69c4fe7cb8b2cd277e021e9427f8500eca8298b0c0519b1e1dabacfa19ae828
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f83d1e769f160ad7042a39277d0ba31fd970dcba7ec9dd68ffe57dcb29a744f59df9690337096d6d5342f333edc655969737b631958a6251e4c0766546bc1af5
|
7
|
+
data.tar.gz: 8c95685178155f836c43b546e3e6fd34e37d18412ee61c187f2f0f58f4dfea28047e139739c52a3eaa0431d9ee90a6ca04e0c0202d64e4dec440491cdebdf16c
|
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) [Discord.](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/version.rb
CHANGED
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,34 +26,35 @@ 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
|
nested_models = model.send(resource.to_sym)
|
@@ -70,15 +71,15 @@ module Optimism
|
|
70
71
|
resource = ancestry.shift
|
71
72
|
resource += "_#{ancestry.shift}_attributes_#{ancestry.shift}" until ancestry.empty?
|
72
73
|
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
|
+
if model.errors.any? && model.errors.messages.map(&:first).include?(attribute.to_sym)
|
74
75
|
message = "#{model.errors.full_message(attribute.to_sym, model.errors.messages[attribute.to_sym].first)}#{Optimism.suffix}"
|
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
|
76
|
+
cable_ready[Optimism.channel[self]].dispatch_event(name: "optimism:attribute:invalid", detail: {resource: resource, attribute: attribute, text: message}) if Optimism.emit_events
|
77
|
+
cable_ready[Optimism.channel[self]].add_css_class(selector: container_selector, name: Optimism.error_class) if Optimism.add_css
|
78
|
+
cable_ready[Optimism.channel[self]].text_content(selector: error_selector, text: message) if Optimism.inject_inline
|
78
79
|
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
|
80
|
+
cable_ready[Optimism.channel[self]].dispatch_event(name: "optimism:attribute:valid", detail: {resource: resource, attribute: attribute}) if Optimism.emit_events
|
81
|
+
cable_ready[Optimism.channel[self]].remove_css_class(selector: container_selector, name: Optimism.error_class) if Optimism.add_css
|
82
|
+
cable_ready[Optimism.channel[self]].text_content(selector: error_selector, text: "") if Optimism.inject_inline
|
82
83
|
end
|
83
84
|
end
|
84
85
|
end
|
@@ -90,7 +91,7 @@ module ActionView::Helpers
|
|
90
91
|
end
|
91
92
|
|
92
93
|
def container_id_for(attribute)
|
93
|
-
Optimism.container_selector.sub("RESOURCE", object_name.delete("]").tr("[", "_")).sub("ATTRIBUTE", attribute.to_s)[1..-1]
|
94
|
+
Optimism.container_selector.sub("RESOURCE", object_name.to_s.delete("]").tr("[", "_")).sub("ATTRIBUTE", attribute.to_s)[1..-1]
|
94
95
|
end
|
95
96
|
|
96
97
|
def error_for(attribute, **options)
|
@@ -98,7 +99,7 @@ module ActionView::Helpers
|
|
98
99
|
end
|
99
100
|
|
100
101
|
def error_id_for(attribute)
|
101
|
-
Optimism.error_selector.sub("RESOURCE", object_name.delete("]").tr("[", "_")).sub("ATTRIBUTE", attribute.to_s)[1..-1]
|
102
|
+
Optimism.error_selector.sub("RESOURCE", object_name.to_s.delete("]").tr("[", "_")).sub("ATTRIBUTE", attribute.to_s)[1..-1]
|
102
103
|
end
|
103
104
|
end
|
104
105
|
end
|
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", "
|
30
|
-
spec.add_dependency "cable_ready", "
|
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"
|
30
|
+
spec.add_dependency "cable_ready", "~> 4.1"
|
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,125 +1,125 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: optimism
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
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-21 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
|
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
104
|
type: :runtime
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - "
|
108
|
+
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '5.2'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: cable_ready
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- - "
|
115
|
+
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
117
|
version: '4.1'
|
118
118
|
type: :runtime
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- - "
|
122
|
+
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '4.1'
|
125
125
|
description: Realtime remote form input validations delivered via websockets
|
@@ -148,6 +148,7 @@ files:
|
|
148
148
|
- Rakefile
|
149
149
|
- SUMMARY.md
|
150
150
|
- advanced-usage.md
|
151
|
+
- authentication.md
|
151
152
|
- bin/console
|
152
153
|
- bin/loc
|
153
154
|
- bin/setup
|