nice_form 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []