hyperactiveform 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,110 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>
7
+ Top Level Namespace
8
+
9
+ &mdash; Documentation by YARD 0.9.37
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" />
16
+
17
+ <script type="text/javascript">
18
+ pathId = "";
19
+ relpath = '';
20
+ </script>
21
+
22
+
23
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
24
+
25
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
26
+
27
+
28
+ </head>
29
+ <body>
30
+ <div class="nav_wrap">
31
+ <iframe id="nav" src="class_list.html?1"></iframe>
32
+ <div id="resizer"></div>
33
+ </div>
34
+
35
+ <div id="main" tabindex="-1">
36
+ <div id="header">
37
+ <div id="menu">
38
+
39
+ <a href="_index.html">Index</a> &raquo;
40
+
41
+
42
+ <span class="title">Top Level Namespace</span>
43
+
44
+ </div>
45
+
46
+ <div id="search">
47
+
48
+ <a class="full_list_link" id="class_list_link"
49
+ href="class_list.html">
50
+
51
+ <svg width="24" height="24">
52
+ <rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
53
+ <rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
54
+ <rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
55
+ </svg>
56
+ </a>
57
+
58
+ </div>
59
+ <div class="clear"></div>
60
+ </div>
61
+
62
+ <div id="content"><h1>Top Level Namespace
63
+
64
+
65
+
66
+ </h1>
67
+ <div class="box_info">
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+ </div>
80
+
81
+ <h2>Defined Under Namespace</h2>
82
+ <p class="children">
83
+
84
+
85
+ <strong class="modules">Modules:</strong> <span class='object_link'><a href="HyperActiveForm.html" title="HyperActiveForm (module)">HyperActiveForm</a></span>
86
+
87
+
88
+
89
+
90
+ </p>
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+ </div>
101
+
102
+ <div id="footer">
103
+ Generated on Sat Dec 14 13:22:03 2024 by
104
+ <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
105
+ 0.9.37 (ruby-3.3.1).
106
+ </div>
107
+
108
+ </div>
109
+ </body>
110
+ </html>
data/examples/basic.md ADDED
@@ -0,0 +1,85 @@
1
+ # Basic form example
2
+
3
+ ## Form
4
+ ```ruby
5
+ class ContactForm < ApplicationForm
6
+ proxy_for Contact, :@contact
7
+
8
+ attribute :first_name
9
+ attribute :last_name
10
+ attribute :birth_date, :date
11
+
12
+ validates :first_name, presence: true
13
+ validates :last_name, presence: true
14
+ validates :birth_date, presence: true
15
+
16
+
17
+ def setup(contact)
18
+ @contact = contact
19
+ self.first_name = @contact.first_name
20
+ self.last_name = @contact.last_name
21
+ self.birth_date = @contact.birth_date
22
+ ## You could also do :
23
+ # self.attributes = @contact.attributes.slice(:first_name, :last_name, birth_date)
24
+ end
25
+
26
+ def perform
27
+ @contact.update!(
28
+ first_name:,
29
+ last_name:,
30
+ birth_date:
31
+ )
32
+ end
33
+ end
34
+ ```
35
+
36
+ ## Controller
37
+ ```ruby
38
+ class ContactsController
39
+ def new
40
+ @form = ContactForm.new(Contact.new)
41
+ end
42
+
43
+ def create
44
+ @form = ContactForm.new(Contact.new)
45
+
46
+ if @form.submit(params[:contact])
47
+ redirect_to root_path, notice: "Contact created"
48
+ else
49
+ render :new, status: :unprocessable_entity
50
+ end
51
+ end
52
+
53
+ def edit
54
+ @contact = Contact.find(params[:id])
55
+ @form = ContactForm.new(@contact)
56
+ end
57
+
58
+ def update
59
+ @contact = Contact.find(params[:id])
60
+ @form = ContactForm.new(@contact)
61
+
62
+ if @form.submit(params[:contact])
63
+ redirect_to root_path, notice: "Contact updated"
64
+ else
65
+ render :edit, status: :unprocessable_entity
66
+ end
67
+ end
68
+ end
69
+ ```
70
+
71
+ ## Views
72
+ ### new.html.erb and edit.html.erb
73
+ ```erb
74
+ <%= render "form", form: @form %>
75
+ ```
76
+
77
+ ### _form.html.erb
78
+ ```erb
79
+ <%= form_with(model: form) do |f| %>
80
+ <%= f.text_field :first_name, placeholder: "First name" %>
81
+ <%= f.text_field :last_name, placeholder: "Last name" %>
82
+ <%= f.date_field :birth_date %>
83
+ <%= f.submit "Save" %>
84
+ <% end %>
85
+ ```
@@ -4,11 +4,26 @@ require "active_model"
4
4
  require "action_controller"
5
5
 
6
6
  module HyperActiveForm
7
+ ##
8
+ # Base class for HyperActiveForm objects
9
+ #
10
+ # HyperActiveForm objects are simple ActiveModel objects that encapsulate
11
+ # form logic and validations. They are designed to be subclassed and
12
+ # customized to fit the needs of your application.
13
+ #
7
14
  class Base
8
15
  include ActiveModel::Model
9
16
  include ActiveModel::Attributes
10
17
  include ActiveModel::Validations
18
+ extend ActiveModel::Callbacks
11
19
 
20
+ define_model_callbacks :assign_form_attributes, :submit
21
+
22
+ # Defines to which object the form should delegate the active model methods
23
+ # This is useful so `form_for`/`form_with` can automatically deduce the url and method to use
24
+ #
25
+ # @param klass [Class] the class of the object to proxy
26
+ # @param object [Object] where to delegate the object to, for example: `:@user`
12
27
  def self.proxy_for(klass, object)
13
28
  delegate :new_record?, :persisted?, :id, to: object
14
29
  singleton_class.delegate :model_name, to: klass
@@ -25,24 +40,47 @@ module HyperActiveForm
25
40
  raise NotImplementedError
26
41
  end
27
42
 
43
+ # Assigns the attributes of the form from the params
44
+ # This method is called by the `submit` method, but can also be called
45
+ # directly if you need to assign the attributes without submitting the form
46
+ # for example if you want to refresh the form with new data
47
+ #
48
+ # @param params [Hash] the params to assign the attributes from
28
49
  def assign_form_attributes(params)
29
- params = ActionController::Parameters.new(params) unless params.is_a?(ActionController::Parameters)
30
- attribute_names.each do |attribute|
31
- public_send(:"#{attribute}=", params&.dig(attribute)) if params&.key?(attribute)
50
+ run_callbacks :assign_form_attributes do
51
+ params = ActionController::Parameters.new(params) unless params.is_a?(ActionController::Parameters)
52
+ attribute_names.each do |attribute|
53
+ default_value = self.class._default_attributes[attribute]&.value_before_type_cast
54
+ public_send(:"#{attribute}=", params&.dig(attribute) || default_value)
55
+ end
32
56
  end
33
57
  end
34
58
 
59
+ # Submits the form, assigning the attributes from the params,
60
+ # running validations and calling the `perform` method if the form is valid
61
+ #
62
+ # @param params [Hash] the params to assign the attributes from
63
+ # @return [Boolean] true if the form is valid and the `perform` method returned something truthy
35
64
  def submit(params)
36
- assign_form_attributes(params)
37
- !!(valid? && perform)
65
+ run_callbacks :submit do
66
+ assign_form_attributes(params)
67
+ !!(valid? && perform)
68
+ end
38
69
  rescue HyperActiveForm::CancelFormSubmit
39
70
  false
40
71
  end
41
72
 
73
+ # Same as `submit` but raises a `FormDidNotSubmitError` if the form is not valid
74
+ #
75
+ # @param params [Hash] the params to assign the attributes from
76
+ # @return [Boolean] true if the form is valid and the `perform` method returned something truthy
42
77
  def submit!(params)
43
78
  submit(params) || raise(HyperActiveForm::FormDidNotSubmitError)
44
79
  end
45
80
 
81
+ # Adds the errors from a model to the form
82
+ #
83
+ # @param model [ActiveModel::Model] the model to add the errors from
46
84
  def add_errors_from(model)
47
85
  model.errors.each do |error|
48
86
  Array.wrap(error.message).each { |e| errors.add(error.attribute, e) }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HyperActiveForm
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hyperactiveform
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adrien Siami
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-11-30 00:00:00.000000000 Z
11
+ date: 2024-12-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '3.13'
41
+ - !ruby/object:Gem::Dependency
42
+ name: yard
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'
41
55
  description: Encapsulate form logic and validations in a simple object
42
56
  email:
43
57
  - adrien@siami.fr
@@ -50,6 +64,27 @@ files:
50
64
  - LICENSE.txt
51
65
  - README.md
52
66
  - Rakefile
67
+ - docs/HyperActiveForm.html
68
+ - docs/HyperActiveForm/Base.html
69
+ - docs/HyperActiveForm/CancelFormSubmit.html
70
+ - docs/HyperActiveForm/FormDidNotSubmitError.html
71
+ - docs/HyperActiveForm/Generators.html
72
+ - docs/HyperActiveForm/Generators/InstallGenerator.html
73
+ - docs/_index.html
74
+ - docs/class_list.html
75
+ - docs/css/common.css
76
+ - docs/css/full_list.css
77
+ - docs/css/style.css
78
+ - docs/file.README.html
79
+ - docs/file_list.html
80
+ - docs/frames.html
81
+ - docs/index.html
82
+ - docs/js/app.js
83
+ - docs/js/full_list.js
84
+ - docs/js/jquery.js
85
+ - docs/method_list.html
86
+ - docs/top-level-namespace.html
87
+ - examples/basic.md
53
88
  - gemfiles/Gemfile.rails-7.1.x
54
89
  - gemfiles/Gemfile.rails-7.2.x
55
90
  - gemfiles/Gemfile.rails-8.x