polutan 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +81 -0
- data/LICENSE.txt +21 -0
- data/README.md +113 -0
- data/Rakefile +4 -0
- data/SUMMARY.md +5 -0
- data/bin/console +7 -0
- data/bin/loc +3 -0
- data/bin/setup +8 -0
- data/bin/standardize +4 -0
- data/lib/polutan/config.rb +14 -0
- data/lib/polutan/possibility.rb +28 -0
- data/lib/polutan/version.rb +5 -0
- data/lib/polutan.rb +22 -0
- metadata +163 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 922efa48e9d130348c44b65161804fc2c9ec71a395e3544e37d3c7b9e96ad454
|
4
|
+
data.tar.gz: bb022c4abae1408f652ed500e6b2e73c98712bf08c3bf32fdc57f134d20c1c13
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3af8fc24dc11ffd2d1a0847b0dd0c52f7789a7dfc91d139bd8d47c5b43cca8a944d204a7242b90ed297e179bf92fcbe1b7d6ad25e2be2a2cbbac6240d663ff3a
|
7
|
+
data.tar.gz: 69e309e2f7ceb22b2180d972d508a419a6396be4b2eca398a44b017dc243930d89bb55a47d6c4f0f2e61b70717b5c38e04d5b5f230b9c077904d97f3ef31bddc
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
polutan (1.0.0)
|
5
|
+
activeentity (~> 6.1)
|
6
|
+
kredis (~> 0.2)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
activeentity (6.3.0)
|
12
|
+
activemodel (>= 6.0, < 8)
|
13
|
+
activesupport (>= 6.0, < 8)
|
14
|
+
activemodel (7.0.2.2)
|
15
|
+
activesupport (= 7.0.2.2)
|
16
|
+
activesupport (7.0.2.2)
|
17
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
18
|
+
i18n (>= 1.6, < 2)
|
19
|
+
minitest (>= 5.1)
|
20
|
+
tzinfo (~> 2.0)
|
21
|
+
ast (2.4.2)
|
22
|
+
coderay (1.1.3)
|
23
|
+
concurrent-ruby (1.1.9)
|
24
|
+
i18n (1.10.0)
|
25
|
+
concurrent-ruby (~> 1.0)
|
26
|
+
kredis (0.4.0)
|
27
|
+
activesupport (>= 6.0.0)
|
28
|
+
redis (~> 4.2)
|
29
|
+
magic_frozen_string_literal (1.2.0)
|
30
|
+
method_source (0.9.2)
|
31
|
+
minitest (5.15.0)
|
32
|
+
parallel (1.21.0)
|
33
|
+
parser (3.1.0.0)
|
34
|
+
ast (~> 2.4.1)
|
35
|
+
pry (0.12.2)
|
36
|
+
coderay (~> 1.1.0)
|
37
|
+
method_source (~> 0.9.0)
|
38
|
+
pry-nav (0.3.0)
|
39
|
+
pry (>= 0.9.10, < 0.13.0)
|
40
|
+
rainbow (3.1.1)
|
41
|
+
rake (13.0.6)
|
42
|
+
redis (4.6.0)
|
43
|
+
regexp_parser (2.2.1)
|
44
|
+
rexml (3.2.5)
|
45
|
+
rubocop (1.25.1)
|
46
|
+
parallel (~> 1.10)
|
47
|
+
parser (>= 3.1.0.0)
|
48
|
+
rainbow (>= 2.2.2, < 4.0)
|
49
|
+
regexp_parser (>= 1.8, < 3.0)
|
50
|
+
rexml
|
51
|
+
rubocop-ast (>= 1.15.1, < 2.0)
|
52
|
+
ruby-progressbar (~> 1.7)
|
53
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
54
|
+
rubocop-ast (1.15.2)
|
55
|
+
parser (>= 3.0.1.1)
|
56
|
+
rubocop-performance (1.13.2)
|
57
|
+
rubocop (>= 1.7.0, < 2.0)
|
58
|
+
rubocop-ast (>= 0.4.0)
|
59
|
+
ruby-progressbar (1.11.0)
|
60
|
+
standard (1.7.2)
|
61
|
+
rubocop (= 1.25.1)
|
62
|
+
rubocop-performance (= 1.13.2)
|
63
|
+
standardrb (1.0.1)
|
64
|
+
standard
|
65
|
+
tzinfo (2.0.4)
|
66
|
+
concurrent-ruby (~> 1.0)
|
67
|
+
unicode-display_width (2.1.0)
|
68
|
+
|
69
|
+
PLATFORMS
|
70
|
+
ruby
|
71
|
+
|
72
|
+
DEPENDENCIES
|
73
|
+
magic_frozen_string_literal (~> 1.2.0)
|
74
|
+
polutan!
|
75
|
+
pry (~> 0.12)
|
76
|
+
pry-nav (~> 0.3)
|
77
|
+
rake (~> 13.0, >= 13.0.3)
|
78
|
+
standardrb (~> 1.0)
|
79
|
+
|
80
|
+
BUNDLED WITH
|
81
|
+
2.1.4
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2022 Nervive Studio
|
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
|
13
|
+
all 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
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
# Polutan
|
2
|
+
|
3
|
+
The [polutan](https://github.com/nervive/polutan) gem offers Rails developers a way to **gather attributes** on an unsaved model **across multiple requests**. It's perfect for [StimulusReflex](https://docs.stimulusreflex.com/) users that are building faceted search interfaces, as well as [Optimism](https://optimism.nervive.studio/) users looking to implement real-time, per-attribute validation schemes.
|
4
|
+
|
5
|
+
Try a demo, here: 👉 [Under Line StimulusReflex](https://underline.nervive.studio/) 👈
|
6
|
+
|
7
|
+
[![GitHub stars](https://img.shields.io/github/stars/nervive/polutan?style=social)](https://github.com/nervive/polutan) [![GitHub forks](https://img.shields.io/github/forks/nervive/polutan?style=social)](https://github.com/nervive/polutan)
|
8
|
+
|
9
|
+
## Why use Polutan?
|
10
|
+
|
11
|
+
Many reactive UI concepts are a pain in the ass to implement using the classic Rails request/response pattern, which was created at a time before developers started using Ajax to update portions of a page. ActionController is designed to mutate state in response to form submissions, leading to abuse of the session object and awkward hacks to validate and persist models across multiple requests.
|
12
|
+
|
13
|
+
Polutan presents a flexible and lightweight mechanism to refine a model that persists its attributes across multiple updates, and even multiple servers.
|
14
|
+
|
15
|
+
## Is Polutan for you?
|
16
|
+
|
17
|
+
Do you ever find yourself:
|
18
|
+
|
19
|
+
* building complex search interfaces
|
20
|
+
* creating multi-stage data entry processes
|
21
|
+
* frustrated by the limitations of classic form submission
|
22
|
+
* wanting to save data even if the model is currently invalid
|
23
|
+
* reinventing the wheel every time you need field validation
|
24
|
+
|
25
|
+
If you answered yes to any of the above... you are every Rails developer, and you're not crazy. This functionality has been a blind-spot in the framework for a long time.
|
26
|
+
|
27
|
+
Yes, Polutan is for **you**.
|
28
|
+
|
29
|
+
## Key features and advantages
|
30
|
+
|
31
|
+
* A natural fit with [StimulusReflex](https://docs.stimulusreflex.com/) and [Stimulus](https://stimulus.hotwire.dev/)
|
32
|
+
* No reliance on sessions, so it works across servers
|
33
|
+
* Easy to learn, quick to implement
|
34
|
+
* Supports model attributes with defaults, arrays and dirty checking
|
35
|
+
* Model validations and errors
|
36
|
+
* No need to mess around with temporary records
|
37
|
+
|
38
|
+
## How does Polutan work?
|
39
|
+
|
40
|
+
First, set up an Polutan class that defines some [attributes](https://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html#method-i-attribute). Your class will inherit from `Possibility`, which is aptly-named.
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
class ExampleModel < Possibility
|
44
|
+
attribute :name, :string
|
45
|
+
attribute :age, :integer, default: 21
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
Then create an instance and assign it to an instance variable in the controller responsible for your initial page load:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
class ExampleController < ApplicationController
|
53
|
+
def index
|
54
|
+
@af = ExampleModel.new
|
55
|
+
end
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
Emit the instance id as a data attribute on every element which can update your model:
|
60
|
+
|
61
|
+
```text
|
62
|
+
Name: <input type="text" data-af="<%= @af.id %>" data-reflex="input->Example#name" /><br/>
|
63
|
+
Age: <input type="text" data-af="<%= @af.id %>" data-reflex="input->Example#age" placeholder="<%= @id.age %>" />
|
64
|
+
```
|
65
|
+
|
66
|
+
Since all attributes are gathered and sent to the server during a Reflex operation, it's easy to retrieve the instance id from the Reflex element accessor and use it to call up the correct Polutan object and make changes to it:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
class ExampleReflex < ApplicationReflex
|
70
|
+
def name
|
71
|
+
model = ExampleModel.find(element.dataset.af)
|
72
|
+
model[:name] = element.value
|
73
|
+
end
|
74
|
+
|
75
|
+
def age
|
76
|
+
model = ExampleModel.find(element.dataset.af)
|
77
|
+
model[:age] = element.value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
The current state of the attributes is persisted every time you set the value of an attribute using bracket notation. You can use standard setter assignments, but the model state will not be persisted until you manually call `save`:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
model[:name] = "Helen" # saved
|
86
|
+
model.name = "Helen" # not saved
|
87
|
+
model.save # saved
|
88
|
+
```
|
89
|
+
|
90
|
+
{% hint style="warning" %}
|
91
|
+
Polutan class attributes are persisted in Redis via the excellent [Kredis](https://github.com/rails/kredis) gem, which must be set up and running in your project before you can use Polutan.
|
92
|
+
{% endhint %}
|
93
|
+
|
94
|
+
Polutan is based on [Active Entity](https://github.com/jasl/activeentity). It is similar to using [ActiveModel::Model](https://api.rubyonrails.org/classes/ActiveModel/Model.html), except that it has full support for [Attributes](https://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html#method-i-attribute), including arrays and nested attributes. Polutan classes behave like ActiveModel classes, so you can inspect `valid?` and the `errors` accessor.
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
class ExampleModel < Possibility
|
98
|
+
attribute :name, :string
|
99
|
+
validates :name, presence: true
|
100
|
+
end
|
101
|
+
|
102
|
+
model = ExampleModel.new
|
103
|
+
model.valid? # false
|
104
|
+
model.errors # @errors=[#<ActiveModel::Error attribute=name, type=blank, options={}>]
|
105
|
+
```
|
106
|
+
|
107
|
+
{% hint style="info" %}
|
108
|
+
Unlike an ActiveRecord model, Polutan instances can persist their attributes even if the attributes are currently invalid. This design allows you to resolve any errors present, even if it takes several distinct operations to do so.
|
109
|
+
{% endhint %}
|
110
|
+
|
111
|
+
{% hint style="success" %}
|
112
|
+
Once the state of your attributes is valid, you can pass the `attributes` from your Polutan model right into the constructor of a real ActiveRecord model. It should work perfectly.
|
113
|
+
{% endhint %}
|
data/Rakefile
ADDED
data/SUMMARY.md
ADDED
data/bin/console
ADDED
data/bin/loc
ADDED
data/bin/setup
ADDED
data/bin/standardize
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
class Possibility < ActiveEntity::Base
|
2
|
+
attr_accessor :id
|
3
|
+
|
4
|
+
def initialize(attributes={})
|
5
|
+
super
|
6
|
+
unless @id
|
7
|
+
@id = SecureRandom.uuid
|
8
|
+
save
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def []=(attr_name, value)
|
13
|
+
super
|
14
|
+
save
|
15
|
+
end
|
16
|
+
|
17
|
+
def save
|
18
|
+
Kredis.json("#{self.class.name}:#{@id}").value = self.attributes.to_json
|
19
|
+
changes_applied
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.find(id)
|
23
|
+
raise ArgumentError unless id
|
24
|
+
json = Kredis.json("#{name}:#{id}").value
|
25
|
+
raise ActiveRecord::RecordNotFound unless json
|
26
|
+
new json.merge(id: id)
|
27
|
+
end
|
28
|
+
end
|
data/lib/polutan.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# require "rails/engine"
|
4
|
+
# require "singleton"
|
5
|
+
# require "active_support/all"
|
6
|
+
|
7
|
+
require "active_entity/railtie"
|
8
|
+
require "polutan/version"
|
9
|
+
require "polutan/possibility"
|
10
|
+
|
11
|
+
module Polutan
|
12
|
+
# class Engine < Rails::Engine
|
13
|
+
# end
|
14
|
+
|
15
|
+
# def self.config
|
16
|
+
# Polutan::Config.instance
|
17
|
+
# end
|
18
|
+
|
19
|
+
# def self.configure
|
20
|
+
# yield config
|
21
|
+
# end
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: polutan
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nervive
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-02-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: kredis
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activeentity
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '6.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '6.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: magic_frozen_string_literal
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.2.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.2.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.12'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.12'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry-nav
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.3'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.3'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '13.0'
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 13.0.3
|
93
|
+
type: :development
|
94
|
+
prerelease: false
|
95
|
+
version_requirements: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - "~>"
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '13.0'
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 13.0.3
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: standardrb
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '1.0'
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - "~>"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '1.0'
|
117
|
+
description:
|
118
|
+
email:
|
119
|
+
- hello@nervive.studio
|
120
|
+
executables: []
|
121
|
+
extensions: []
|
122
|
+
extra_rdoc_files: []
|
123
|
+
files:
|
124
|
+
- Gemfile
|
125
|
+
- Gemfile.lock
|
126
|
+
- LICENSE.txt
|
127
|
+
- README.md
|
128
|
+
- Rakefile
|
129
|
+
- SUMMARY.md
|
130
|
+
- bin/console
|
131
|
+
- bin/loc
|
132
|
+
- bin/setup
|
133
|
+
- bin/standardize
|
134
|
+
- lib/polutan.rb
|
135
|
+
- lib/polutan/config.rb
|
136
|
+
- lib/polutan/possibility.rb
|
137
|
+
- lib/polutan/version.rb
|
138
|
+
homepage: https://polutan.nervive.studio/
|
139
|
+
licenses:
|
140
|
+
- MIT
|
141
|
+
metadata:
|
142
|
+
source_code_uri: https://github.com/nervive/polutan
|
143
|
+
documentation_uri: https://polutan.nervive.studio/
|
144
|
+
post_install_message:
|
145
|
+
rdoc_options: []
|
146
|
+
require_paths:
|
147
|
+
- lib
|
148
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
requirements: []
|
159
|
+
rubygems_version: 3.1.6
|
160
|
+
signing_key:
|
161
|
+
specification_version: 4
|
162
|
+
summary: A Redis-backed virtual ActiveModel, full of possibilities.
|
163
|
+
test_files: []
|