hyperactiveform 0.1.0 → 0.3.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.
@@ -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
+ ```
@@ -0,0 +1,9 @@
1
+ class FormGenerator < Rails::Generators::NamedBase
2
+ source_root File.expand_path("templates", __dir__)
3
+
4
+ def create_application_form
5
+ template 'form.rb', File.join('app/forms', class_path, "#{file_name}_form.rb")
6
+ end
7
+
8
+ hook_for :test_framework
9
+ end
@@ -0,0 +1,2 @@
1
+ class <%= class_name %>Form < ApplicationForm
2
+ end
@@ -1,5 +1,3 @@
1
- require "rails/generators"
2
-
3
1
  module HyperActiveForm
4
2
  module Generators
5
3
  class InstallGenerator < Rails::Generators::Base
@@ -0,0 +1,11 @@
1
+ module Rspec
2
+ module Generators
3
+ class FormGenerator < ::Rails::Generators::NamedBase
4
+ source_root File.expand_path("templates", __dir__)
5
+
6
+ def create_form_spec
7
+ template "form_spec.rb", File.join("spec/forms", class_path, "#{file_name}_form_spec.rb")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ require '<%= File.exist?('spec/rails_helper.rb') ? 'rails_helper' : 'spec_helper' %>'
2
+
3
+ RSpec.describe <%= class_name %>Form, type: :form do
4
+ pending "add some examples to (or delete) #{__FILE__}"
5
+ end
@@ -0,0 +1,11 @@
1
+ module TestUnit
2
+ module Generators
3
+ class FormGenerator < ::Rails::Generators::NamedBase
4
+ source_root File.expand_path("templates", __dir__)
5
+
6
+ def create_form_test
7
+ template "form_test.rb", File.join("test/forms", class_path, "#{file_name}_form_test.rb")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class <%= class_name %>FormTest < ActiveSupport::TestCase
4
+ # test "the truth" do
5
+ # assert true
6
+ # end
7
+ end
@@ -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.3.0"
5
5
  end
@@ -7,4 +7,3 @@ end
7
7
 
8
8
  require_relative "hyper_active_form/version"
9
9
  require_relative "hyper_active_form/base"
10
- require_relative "hyper_active_form/generators"
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.3.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-22 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,12 +64,39 @@ 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
91
+ - lib/generators/form/form_generator.rb
92
+ - lib/generators/form/templates/form.rb
93
+ - lib/generators/hyper_active_form/install/install_generator.rb
94
+ - lib/generators/rspec/form_generator.rb
95
+ - lib/generators/rspec/templates/form_spec.rb
96
+ - lib/generators/test_unit/form_generator.rb
97
+ - lib/generators/test_unit/templates/form_test.rb
56
98
  - lib/hyper_active_form.rb
57
99
  - lib/hyper_active_form/base.rb
58
- - lib/hyper_active_form/generators.rb
59
100
  - lib/hyper_active_form/version.rb
60
101
  - lib/hyperactiveform.rb
61
102
  - sig/hyperactiveform.rbs