Skip to content

Commit 068079b

Browse files
committed
creating README
1 parent f01e921 commit 068079b

File tree

6 files changed

+199
-25
lines changed

6 files changed

+199
-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: 193 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,213 @@
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 |f| %>
91+
<%= f.text_field :first_name %>
92+
<%= f.text_field :last_name %>
93+
<%= f.date_field :birth_date %>
94+
95+
<%= f.submit %>
96+
<% end %>
97+
```
98+
99+
### Understanding `proxy_for`
100+
101+
`HyperActiveForm` mimics a model object, you can use `proxy_for` to tell it which class and object to delegate to.
102+
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.
104+
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.
106+
107+
```ruby
108+
class ProfileForm < ApplicationForm
109+
proxy_for User, :@user # Will delegate to @user
110+
end
111+
```
112+
113+
If you pass an url and method yourself, you don't need to use `proxy_for`.
24114

25-
TODO: Write usage instructions here
115+
### Understanding `setup`
26116

27-
## Development
117+
`setup` is called just after the form is initialized, and is used to pre-fill the form with data from the object.
28118

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.
119+
`setup` will receive the same arguments as the initializer, so you can use it to pass any data you need to the form.
30120

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).
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+
```
131+
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`.
32141

33-
## Contributing
142+
## Understanding add_errors_from
34143

35-
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/hyperactiveform.
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+
168+
### Not all forms map to a single model
169+
170+
The power of `HyperActiveForm` is that you can use it to create forms that don't map to a single model.
171+
172+
Some forms can be used to create several models at once. Doing so without form objects can be tedious especially with nested attributes.
173+
174+
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.
175+
176+
One great example of such forms are search forms. You can use a form object to encapsulate the search logic :
177+
178+
```ruby
179+
class UserSearchForm < ApplicationForm
180+
attribute :name
181+
attribute :email
182+
attribute :min_age, :integer
183+
184+
attr_reader :results # So the controller can access the results
185+
186+
def perform
187+
@results = User.all
188+
189+
if name.present?
190+
@results = @results.where(name: name)
191+
end
192+
if email.present?
193+
@results = @results.where(email: email)
194+
end
195+
if age.present?
196+
@results = @results.where("age >= ?", age)
197+
end
198+
199+
true
200+
end
201+
end
202+
```
36203

37-
## License
204+
And in the controller:
38205

39-
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
206+
```ruby
207+
class UsersController < ApplicationController
208+
def index
209+
@form = UserSearchForm.new
210+
@form.submit!(params[:user])
211+
@users = @form.results
212+
end
213+
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)