nice_form 1.0.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 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
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+
4
+ Style/StringLiterals:
5
+ Enabled: true
6
+ EnforcedStyle: double_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ Enabled: true
10
+ EnforcedStyle: double_quotes
11
+
12
+ Layout/LineLength:
13
+ Max: 120
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2022-05-20
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ gem "rake", "~> 13.0"
8
+ gem "rspec", "~> 3.0"
9
+ gem "rubocop", "~> 1.21"
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,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NiceForm
4
+ VERSION = "1.0.0"
5
+ end
data/lib/nice_form.rb ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ require "active_support/concern"
3
+
4
+ require_relative "nice_form/version"
5
+ require_relative "nice_form/config"
6
+ require_relative "nice_form/base"
7
+
8
+ module NiceForm
9
+ end
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: []