Skip to content

Commit 04be26b

Browse files
committed
creating README
1 parent f01e921 commit 04be26b

File tree

6 files changed

+198
-25
lines changed

6 files changed

+198
-25
lines changed

Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ PLATFORMS
197197
DEPENDENCIES
198198
hyperactiveform!
199199
rake (~> 13.0)
200-
rspec (~> 3.0)
200+
rspec (~> 3.13)
201201

202202
BUNDLED WITH
203203
2.5.18

README.md

Lines changed: 192 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,212 @@
1-
# Hyperactiveform
1+
# ✨ HyperActiveForm ✨
22

3-
TODO: Delete this and the text below, and describe your gem
3+
HyperActiveForm is a simple form object implementation for Rails.
44

5-
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/hyperactiveform`. To experiment with that code, run `bin/console` for an interactive prompt.
5+
Form objects are objects that encapsulate form logic and validations, they allow to extract the business logic out of the controller and models into specialized objects.
6+
7+
HyperActiveForm's form objects mimic the ActiveModel API, so they work out of the box with Rails' form helpers, and allow you to use the ActiveModel validations you already know.
8+
9+
This allows you to only keep strictly necessary validations in the model, and have business logic validations in the form object. This is especially useful when you want different validations to be applied depending on the context.
610

711
## Installation
812

9-
TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
13+
Add this line to your application's Gemfile:
14+
15+
```ruby
16+
gem 'hyperactiveform'
17+
```
18+
19+
And then execute:
20+
21+
$ bundle install
1022

11-
Install the gem and add to the application's Gemfile by executing:
23+
Run the install generator:
1224

13-
```bash
14-
bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
25+
$ rails generate hyperactiveform:install
26+
27+
this will create an ApplicationForm class in your app/forms directory. You can use it as a base class for your form objects.
28+
29+
## Usage
30+
31+
Here is an example of an `HyperActiveForm` form object:
32+
33+
```ruby
34+
class ProfileForm < ApplicationForm
35+
# proxy_for is used to delegate the model name to the class, and some methods to the object
36+
# this helps use `form_with` in views without having to specify the url
37+
proxy_for User, :@user
38+
39+
# Define the form fields, using ActiveModel::Attributes
40+
attribute :first_name
41+
attribute :last_name
42+
attribute :birth_date, :date
43+
44+
# Define the validations, using ActiveModel::Validations
45+
validates :first_name, presence: true
46+
validates :last_name, presence: true
47+
validates :birth_date, presence: true
48+
49+
# Pre-fill the form if needed
50+
def setup(user)
51+
@user = user
52+
self.first_name = user.first_name
53+
self.last_name = user.last_name
54+
self.birth_date = user.birth_date
55+
end
56+
57+
# Perform the form logic
58+
def perform
59+
@user.update!(
60+
first_name: first_name,
61+
last_name: last_name,
62+
birth_date: birth_date
63+
)
64+
end
65+
end
1566
```
1667

17-
If bundler is not being used to manage dependencies, install the gem by executing:
68+
The controller would look like this:
69+
70+
```ruby
71+
class UsersController < ApplicationController
72+
73+
def edit
74+
@form = ProfileForm.new(user: current_user)
75+
end
1876

19-
```bash
20-
gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
77+
def update
78+
@form = ProfileForm.new(user: current_user)
79+
if @form.submit(params[:user])
80+
redirect_to root_path, notice: "Profile updated"
81+
else
82+
render :edit, status: :unprocessable_entity
83+
end
84+
end
2185
```
2286

23-
## Usage
87+
And the view would look like this:
88+
89+
```erb
90+
<%= form_with(model: @form) do |form| %>
91+
<%= form.text_field :first_name %>
92+
<%= form.text_field :last_name %>
93+
<%= form.date_field :birth_date %>
94+
95+
<%= form.submit %>
96+
<% end %>
97+
```
98+
99+
### Understanding `proxy_for`
24100

25-
TODO: Write usage instructions here
101+
`HyperActiveForm` mimics a model object, you can use `proxy_for` to tell it which class and object to delegate to.
26102

27-
## Development
103+
When using `form_for` or `form_with`, Rails will choose the URL and method based on the object, according to the persisted state of the object and its model name.
28104

29-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
105+
The first argument of `proxy_for` is the class of the object, and the second argument is the name of the instance variable that holds the object.
30106

31-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
107+
```ruby
108+
class ProfileForm < ApplicationForm
109+
proxy_for User, :@user # Will delegate to @user
110+
end
111+
```
32112

33-
## Contributing
113+
If you pass an url and method yourself, you don't need to use `proxy_for`.
114+
115+
### Understanding `setup`
116+
117+
`setup` is called just after the form is initialized, and is used to pre-fill the form with data from the object.
118+
119+
`setup` will receive the same arguments as the initializer, so you can use it to pass any data you need to the form.
120+
121+
```ruby
122+
class ProfileForm < ApplicationForm
123+
def setup(user)
124+
@user = user
125+
self.first_name = user.first_name
126+
self.last_name = user.last_name
127+
self.birth_date = user.birth_date
128+
end
129+
end
130+
```
34131

35-
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/hyperactiveform.
132+
### Understanding `perform`
133+
134+
When using `submit` or `submit!`, `HyperActiveForm` will first assign the form attributes to the object, then perform the validations, then call `perform` on the object if the form is valid.
135+
136+
The `perform` method is where you should do the actual form logic, like updating the object or creating a new one.
137+
138+
If the return value of `perform` is not truthy, `HyperActiveForm` will consider the form encountered an error and `submit` will return `false`, or `submit!` will raise a `HyperActiveForm::FormDidNotSubmitError`.
139+
140+
At any point during the form processing, you can raise `HyperActiveForm::CancelForm` to cancel the form submission, this is the same as returning `false`.
141+
142+
## Understanding add_errors_from
143+
144+
`HyperActiveForm` provides a method to add errors from a model and apply them fo the form.
145+
146+
This is useful when the underlying model has validations that are not set up in the form object, and you want them to be applied to the form.
147+
148+
```ruby
149+
class User < ApplicationRecord
150+
validates :first_name, presence: true
151+
end
152+
153+
class ProfileForm < ApplicationForm
154+
proxy_for User, :@user
155+
attribute :first_name
156+
157+
def setup(user)
158+
@user = user
159+
self.first_name = user.first_name
160+
end
161+
162+
def perform
163+
@user.update!(first_name: first_name) || add_errors_from(@user)
164+
end
165+
end
166+
167+
### Not all forms map to a single model
168+
169+
The power of `HyperActiveForm` is that you can use it to create forms that don't map to a single model.
170+
171+
Some forms can be used to create several models at once. Doig so without form objects can be tedious especially with nested attributes.
172+
173+
Some forms dont map to any model at all, like a simple contact form that only sends an email and saves nothing in the database, or a sign in form that would only validate the credentials and return the instance of the connected user.
174+
175+
One great example of such forms are search forms. You can use a form object to encapsulate the search logic :
176+
177+
```ruby
178+
class UserSearchForm < ApplicationForm
179+
attribute :name
180+
attribute :email
181+
attribute :min_age, :integer
182+
183+
attr_reader :results # So the controller can access the results
184+
185+
def perform
186+
@results = User.all
187+
188+
if name.present?
189+
@results = @results.where(name: name)
190+
end
191+
if email.present?
192+
@results = @results.where(email: email)
193+
end
194+
if age.present?
195+
@results = @results.where("age >= ?", age)
196+
end
197+
198+
true
199+
end
200+
end
201+
```
36202
37-
## License
203+
And in the controller:
38204
39-
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
205+
```ruby
206+
class UsersController < ApplicationController
207+
def index
208+
@form = UserSearchForm.new
209+
@form.submit!(params[:user])
210+
@users = @form.results
211+
end
212+
end

hyperactiveform.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
1616

1717
spec.metadata["homepage_uri"] = spec.homepage
1818
spec.metadata["source_code_uri"] = spec.homepage
19-
#spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
19+
spec.metadata["changelog_uri"] = "https://github.com/Intrepidd/hyperactiveform/blob/main/CHANGELOG.md"
2020

2121
# Specify which files should be added to the gem when it is released.
2222
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.

lib/hyper_active_form.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# frozen_string_literal: true
22

33
module HyperActiveForm
4-
class CancelFormSubmitError < StandardError; end
4+
class CancelFormSubmit < StandardError; end
55
class FormDidNotSubmitError < StandardError; end
66
end
77

lib/hyper_active_form/base.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def assign_form_attributes(params)
3535
def submit(params)
3636
assign_form_attributes(params)
3737
!!(valid? && perform)
38-
rescue HyperActiveForm::CancelFormSubmitError
38+
rescue HyperActiveForm::CancelFormSubmit
3939
false
4040
end
4141

spec/hyper_active_form/base_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,9 @@ def perform
6868
end
6969
end
7070

71-
context "when the form raises a CancelFormSubmitError" do
71+
context "when the form raises a CancelFormSubmit" do
7272
it "returns false" do
73-
expect(callable).to receive(:call).and_raise(HyperActiveForm::CancelFormSubmitError)
73+
expect(callable).to receive(:call).and_raise(HyperActiveForm::CancelFormSubmit)
7474
form = dummy_class.new(name: "John", age: 20, callable:)
7575
expect(form.submit(name: "Fred", age: 19)).to eq(false)
7676
end

0 commit comments

Comments
 (0)