nice_form 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/.rspec +3 -0
- data/.rubocop.yml +13 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +9 -0
- data/README.md +179 -0
- data/Rakefile +12 -0
- data/lib/nice_form/acts_like.rb +37 -0
- data/lib/nice_form/base.rb +115 -0
- data/lib/nice_form/config.rb +27 -0
- data/lib/nice_form/instantiation.rb +70 -0
- data/lib/nice_form/primary_key.rb +38 -0
- data/lib/nice_form/version.rb +5 -0
- data/lib/nice_form.rb +9 -0
- metadata +100 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 72f4c95c9bf001b15641096f7319debd084001453ec44d2d2f542364fc2b49c2
|
4
|
+
data.tar.gz: f5a2b2e1c06c38ed2021e0c4a3291294aa25e985b4be7b7a2efff855ab33a0c0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 35428d57f07eca576a171a9fdc8ab2c819de10da76ac60f81f028ff88c0debb5b439319ad0cdd2cbebd3d9a90ef8f9508c2f3df9600403112a70a5683030fa1e
|
7
|
+
data.tar.gz: 36c61bcb52645737f4f1043dae8f9efdea5bbc8a9b5aa9e057ffc03fd9b03f015548a7262f6659f7fe78adabb4b5d02e1a98dbb5b25ee6832707ccffce3a3d79
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
# NiceForm
|
2
|
+
|
3
|
+
A nice form object for Rails that makes sense.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Install the gem and add to the application's Gemfile by executing:
|
8
|
+
|
9
|
+
$ bundle add nice_form
|
10
|
+
|
11
|
+
You might want to create an application-wide form:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
class ApplicationForm < NiceForm::Base; end
|
15
|
+
```
|
16
|
+
|
17
|
+
Or configure the default primary key settings if you're using something other than `id`:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
NiceForm.configure do |config|
|
21
|
+
# name, type
|
22
|
+
config.primary_key = [:uuid, :string]
|
23
|
+
end
|
24
|
+
```
|
25
|
+
|
26
|
+
You can also set this on a form-by-form basis.
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
It's just like Active Model, but with some nice-to-haves.
|
31
|
+
|
32
|
+
### Basic usage
|
33
|
+
|
34
|
+
Treat it like an Active Model object:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
class PostForm < NiceForm::Base
|
38
|
+
self.acts_like = :post
|
39
|
+
|
40
|
+
attribute :subject
|
41
|
+
attribute :context, :text
|
42
|
+
attribute :published_at, :datetime
|
43
|
+
attribute :requires_review, :boolean
|
44
|
+
end
|
45
|
+
```
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
class PostsController < ApplicationController
|
49
|
+
def new
|
50
|
+
@form = PostForm.new
|
51
|
+
end
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
### From params
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
class PostsController < ApplicationController
|
59
|
+
def create
|
60
|
+
@form = PostForm.new(post_params)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def post_params
|
66
|
+
params.require(:post).permit(:subject, :content, :published_at, :requires_review)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
### From a model
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
class PostsController < ApplicationController
|
75
|
+
def edit
|
76
|
+
post = Post.find(params[:id])
|
77
|
+
@form = PostForm.from_model(post) # { id: 1, subject: "hello" ... }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
#### Map additional attributes to a form object
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
class PostsController < ApplicationController
|
86
|
+
def edit
|
87
|
+
post = Post.find(params[:id])
|
88
|
+
@form = PostForm.from_model(post)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
class PostForm < NiceForm::Base
|
95
|
+
self.acts_like = :post
|
96
|
+
|
97
|
+
attribute :subject
|
98
|
+
attribute :context, :text
|
99
|
+
attribute :published_at, :datetime
|
100
|
+
attribute :requires_review, :boolean
|
101
|
+
|
102
|
+
def map_model(model)
|
103
|
+
model.requires_review = model.reviews.last.status == 'review'
|
104
|
+
end
|
105
|
+
end
|
106
|
+
```
|
107
|
+
|
108
|
+
### Change the param key
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
Rails.application.routes.draw do
|
112
|
+
resources :posts, param_name: :uuid
|
113
|
+
end
|
114
|
+
```
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
class PostsController < ApplicationController
|
118
|
+
def edit
|
119
|
+
post = Post.find(params[:uuid])
|
120
|
+
@form = PostForm.from_model(post)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
```
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
class PostForm < NiceForm::Base
|
127
|
+
self.acts_like = :post
|
128
|
+
|
129
|
+
self.primary_key :uuid, :string
|
130
|
+
|
131
|
+
# ...
|
132
|
+
end
|
133
|
+
```
|
134
|
+
|
135
|
+
## Validations
|
136
|
+
|
137
|
+
Again, just like an Active Model object:
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
class PostForm < NiceForm::Base
|
141
|
+
self.acts_like = :post
|
142
|
+
|
143
|
+
attribute :subject
|
144
|
+
|
145
|
+
validates :subject, presence: true
|
146
|
+
end
|
147
|
+
```
|
148
|
+
|
149
|
+
## External context
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
class PostsController < ApplicationController
|
153
|
+
def new
|
154
|
+
@form = PostForm.new.with_context(user: current_staff_user)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
```
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
class PostForm < NiceForm::Base
|
161
|
+
self.acts_like = :post
|
162
|
+
|
163
|
+
attribute :category_id, :integer
|
164
|
+
attribute :subject
|
165
|
+
attribute :context, :text
|
166
|
+
attribute :published_at, :datetime
|
167
|
+
|
168
|
+
validates :category_id, with: :ensure_category_id!
|
169
|
+
|
170
|
+
private
|
171
|
+
|
172
|
+
def ensure_category_id!
|
173
|
+
unless CategoryUser.where(category_id: category_id, user: context[:user]).exists?
|
174
|
+
self.errors.add(:category_id, "is invalid")
|
175
|
+
throw(:abort)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NiceForm
|
4
|
+
# Allows you to define the model name of the form
|
5
|
+
#
|
6
|
+
# class CatForm < NiceForm::Base
|
7
|
+
# self.acts_like = :cat
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# Automatically infers the name if not set.
|
11
|
+
module ActsLike
|
12
|
+
def self.included(klass)
|
13
|
+
klass.extend ClassMethods
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
def acts_like=(val)
|
18
|
+
@acts_like_model = val.to_s.underscore.to_sym
|
19
|
+
end
|
20
|
+
|
21
|
+
def acts_like_model_name
|
22
|
+
@acts_like_model || inferred_model_name
|
23
|
+
end
|
24
|
+
|
25
|
+
def inferred_model_name
|
26
|
+
class_name = name.demodulize
|
27
|
+
return :form if class_name == "Form"
|
28
|
+
|
29
|
+
class_name.delete_suffix("Form").underscore.to_sym
|
30
|
+
end
|
31
|
+
|
32
|
+
def model_name
|
33
|
+
ActiveModel::Name.new(self, nil, acts_like_model_name.to_s.camelize)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_model'
|
4
|
+
|
5
|
+
require_relative "instantiation"
|
6
|
+
require_relative "acts_like"
|
7
|
+
require_relative "primary_key"
|
8
|
+
|
9
|
+
module NiceForm
|
10
|
+
class Base
|
11
|
+
extend ActiveModel::Naming
|
12
|
+
extend ActiveModel::Translation
|
13
|
+
|
14
|
+
include ActiveModel::Attributes
|
15
|
+
include ActiveModel::AttributeAssignment
|
16
|
+
include ActiveModel::Callbacks
|
17
|
+
include ActiveModel::Conversion
|
18
|
+
include ActiveModel::Validations
|
19
|
+
|
20
|
+
include ActsLike
|
21
|
+
include Instantiation
|
22
|
+
include PrimaryKey
|
23
|
+
|
24
|
+
define_model_callbacks :validation, only: :before
|
25
|
+
|
26
|
+
attr_reader :context
|
27
|
+
|
28
|
+
def self.inherited(klass)
|
29
|
+
super
|
30
|
+
klass.primary_key(*NiceForm.config.primary_key)
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(attributes = {})
|
34
|
+
assign_attributes(attributes) if attributes
|
35
|
+
super()
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.inspect
|
39
|
+
attr_list = attribute_types.map { |name, type| "#{name}: #{type.type}" } * ", "
|
40
|
+
"#{self.name}(#{attr_list})"
|
41
|
+
end
|
42
|
+
|
43
|
+
def inspect
|
44
|
+
inspection = if defined?(@attributes) && @attributes
|
45
|
+
self.class.attribute_names.filter_map do |name|
|
46
|
+
"#{name}: #{attribute_for_inspect(name)}" if _has_attribute?(name)
|
47
|
+
end.join(", ")
|
48
|
+
else
|
49
|
+
"not initialized"
|
50
|
+
end
|
51
|
+
|
52
|
+
"#<#{self.class} #{inspection}>"
|
53
|
+
end
|
54
|
+
|
55
|
+
def attributes
|
56
|
+
attrs = [self.class.form_primary_key]
|
57
|
+
|
58
|
+
attrs << if ::NiceForm.config.primary_key.is_a?(Array)
|
59
|
+
::NiceForm.config.primary_key[0].to_s
|
60
|
+
else
|
61
|
+
::NiceForm::Config.primary_key.to_s
|
62
|
+
end
|
63
|
+
|
64
|
+
super.except(*attrs)
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_model
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_param
|
72
|
+
attribute(self.class.form_primary_key)
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_key
|
76
|
+
key = attribute(self.class.form_primary_key)
|
77
|
+
return nil if key.nil?
|
78
|
+
|
79
|
+
[key]
|
80
|
+
end
|
81
|
+
|
82
|
+
def persisted?
|
83
|
+
to_param.present?
|
84
|
+
end
|
85
|
+
|
86
|
+
def with_context(attrs = {})
|
87
|
+
@context = attrs
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def format_for_inspect(value)
|
93
|
+
if value.nil?
|
94
|
+
value.inspect
|
95
|
+
elsif value.is_a?(String) && value.length > 50
|
96
|
+
"#{value[0, 50]}...".inspect
|
97
|
+
elsif value.is_a?(Date) || value.is_a?(Time)
|
98
|
+
%("#{value.to_fs(:inspect)}")
|
99
|
+
else
|
100
|
+
value.inspect
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def attribute_for_inspect(attr_name)
|
105
|
+
attr_name = attr_name.to_s
|
106
|
+
attr_name = self.class.attribute_aliases[attr_name] || attr_name
|
107
|
+
value = _read_attribute(attr_name)
|
108
|
+
format_for_inspect(value)
|
109
|
+
end
|
110
|
+
|
111
|
+
def _has_attribute?(attr_name)
|
112
|
+
@attributes.key?(attr_name)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NiceForm
|
4
|
+
class Configuration
|
5
|
+
attr_accessor :primary_key
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@primary_key = %i[id integer]
|
9
|
+
end
|
10
|
+
|
11
|
+
def primary_key=(val)
|
12
|
+
@primary_key = if val.is_a?(Array)
|
13
|
+
val
|
14
|
+
else
|
15
|
+
[val, :integer]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.configure
|
21
|
+
yield config
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.config
|
25
|
+
@config ||= Configuration.new
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NiceForm
|
4
|
+
module Instantiation
|
5
|
+
def self.included(klass)
|
6
|
+
klass.include FromParams
|
7
|
+
klass.include FromHash
|
8
|
+
klass.include FromModel
|
9
|
+
klass.include FromJson
|
10
|
+
end
|
11
|
+
|
12
|
+
module FromHash
|
13
|
+
def self.included(klass)
|
14
|
+
klass.extend ClassMethods
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
def from_hash(hash)
|
19
|
+
from_params(hash)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module FromParams
|
25
|
+
def self.included(klass)
|
26
|
+
klass.extend ClassMethods
|
27
|
+
end
|
28
|
+
|
29
|
+
module ClassMethods
|
30
|
+
def from_params(params, additional_params = {})
|
31
|
+
instance = new
|
32
|
+
instance.assign_attributes(params.merge(additional_params))
|
33
|
+
instance
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module FromModel
|
39
|
+
def self.included(klass)
|
40
|
+
klass.extend ClassMethods
|
41
|
+
end
|
42
|
+
|
43
|
+
module ClassMethods
|
44
|
+
def from_model(model)
|
45
|
+
instance = new
|
46
|
+
instance.class.attribute_names.each do |attr|
|
47
|
+
instance.public_send("#{attr}=", model.public_send(attr)) if model.respond_to?(attr)
|
48
|
+
end
|
49
|
+
instance.map_model(model)
|
50
|
+
instance
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# assign attributes to self
|
55
|
+
def map_model(model); end
|
56
|
+
end
|
57
|
+
|
58
|
+
module FromJson
|
59
|
+
def self.included(klass)
|
60
|
+
klass.extend ClassMethods
|
61
|
+
end
|
62
|
+
|
63
|
+
module ClassMethods
|
64
|
+
def from_json(string)
|
65
|
+
from_params(JSON.parse(string))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NiceForm
|
4
|
+
module PrimaryKey
|
5
|
+
def self.included(klass)
|
6
|
+
klass.extend ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def primary_key(name, type = :integer)
|
11
|
+
if name.is_a?(Array)
|
12
|
+
type = name[1]
|
13
|
+
name = name[0]
|
14
|
+
end
|
15
|
+
undefine_primary_key(form_primary_key)
|
16
|
+
define_primary_key(name, type)
|
17
|
+
end
|
18
|
+
|
19
|
+
def form_primary_key
|
20
|
+
@form_primary_key ||= define_primary_key(NiceForm.config.primary_key[0], NiceForm.config.primary_key[1])
|
21
|
+
end
|
22
|
+
|
23
|
+
def undefine_primary_key(name)
|
24
|
+
if attribute_names.include?(name.to_s)
|
25
|
+
undef_method(name.to_s) if respond_to?(name.to_s)
|
26
|
+
undef_method("#{name}=") if respond_to?("#{name}=")
|
27
|
+
attribute_types.delete(name.to_s)
|
28
|
+
_default_attributes.send(:attributes).delete(name.to_s)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def define_primary_key(name, type = :integer)
|
33
|
+
attribute name, type
|
34
|
+
@form_primary_key = name.to_s
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/nice_form.rb
ADDED
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nice_form
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Partytray
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-05-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activemodel
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '5.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '5.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: A form object.
|
56
|
+
email:
|
57
|
+
- 97254326+Partytray@users.noreply.github.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".rspec"
|
63
|
+
- ".rubocop.yml"
|
64
|
+
- CHANGELOG.md
|
65
|
+
- Gemfile
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- lib/nice_form.rb
|
69
|
+
- lib/nice_form/acts_like.rb
|
70
|
+
- lib/nice_form/base.rb
|
71
|
+
- lib/nice_form/config.rb
|
72
|
+
- lib/nice_form/instantiation.rb
|
73
|
+
- lib/nice_form/primary_key.rb
|
74
|
+
- lib/nice_form/version.rb
|
75
|
+
homepage: https://github.com/partytray/nice_form
|
76
|
+
licenses: []
|
77
|
+
metadata:
|
78
|
+
homepage_uri: https://github.com/partytray/nice_form
|
79
|
+
source_code_uri: https://github.com/partytray/nice_form
|
80
|
+
changelog_uri: https://github.com/partytray/nice_form
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 2.4.2
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubygems_version: 3.3.11
|
97
|
+
signing_key:
|
98
|
+
specification_version: 4
|
99
|
+
summary: A form object.
|
100
|
+
test_files: []
|