base_form 0.1.1

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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e17e616e68d129b4df560fbeefdc03a4ba59b6d9
4
+ data.tar.gz: 8d8abb13435b58487acc419ae77f4bdd4ca7f5fb
5
+ SHA512:
6
+ metadata.gz: 92584db37f2726475eb530296972649793e615c45d69ee7f14e867af6aa51565fe76129c0d748aa8dec9967b5bf47c9daf47c2b12b00adc62178310680a6732a
7
+ data.tar.gz: 2215a7f4b46b1d2648979e2a7b3ce9bc1a78dc9082561d5912dae0a3451bfbbf8c9ffcbf2c74cf3eac26415b497aa0fbf686bd8a7c30553ffc6c30208b9e1861
@@ -0,0 +1,20 @@
1
+ Copyright 2016 andrerpbts
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,190 @@
1
+ # BaseForm
2
+ [![Code Climate](https://codeclimate.com/github/andrerpbts/base_form/badges/gpa.svg)](https://codeclimate.com/github/andrerpbts/base_form)
3
+ [![Test Coverage](https://codeclimate.com/github/andrerpbts/base_form/badges/coverage.svg)](https://codeclimate.com/github/andrerpbts/base_form/coverage)
4
+ [ ![Codeship Status for andrerpbts/base_form](https://app.codeship.com/projects/8d9be4e0-811f-0134-3da7-7e60ebb19227/status?branch=master)](https://app.codeship.com/projects/182157)
5
+
6
+ A simple and small Form Objects Rails plugin for ActiveRecord based projects.
7
+
8
+ ## Installation
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'base_form'
13
+ ```
14
+
15
+ And then execute:
16
+ ```bash
17
+ $ bundle
18
+ ```
19
+
20
+ Or install it yourself as:
21
+ ```bash
22
+ $ gem install base_form
23
+ ```
24
+
25
+ ## Usage
26
+ In a development day-to-day basis, we commonly are confronted with situation where we
27
+ need to save data in more than one database table, running it's own validations, and
28
+ the validations of the all context together. In most cases a Form Object is a perfect
29
+ solution to deliver those records in a fun and maintenable code.
30
+
31
+ Actually, there's a lot of another gems to do that, like the great
32
+ [reform](https://github.com/apotonick/reform) or
33
+ [activeform-rails](https://github.com/GCorbel/activeform-rails), which are a more complete
34
+ solution for this problem. But, if you are looking for something lighter, maybe this
35
+ gem could fit well for you.
36
+
37
+ That said, let me show some simple examples to you. Let's suppose you want to create a
38
+ signup form (you can check this example in the dummy app on this gem specs), with
39
+ receiving a user email, a user password, a user password confirmation, and a plan. In your
40
+ signup form, you need to create an account for this user, associate it to a entrance plan
41
+ if it's not given, and make this user as an owner of this recently created account. Of course,
42
+ in this case, a simple user model saving will not be sufficient to save all those data and
43
+ accomplish with the requested business logic. So, you go to the Form Objects way,
44
+ installs this gem, creates your Ruby class to handle all this logic:
45
+
46
+ ```ruby
47
+ class SignupForm < BaseForm::Form
48
+
49
+ end
50
+ ```
51
+
52
+ With this empty class created, the easy way to start may be adding the attributes expected
53
+ in this form, like:
54
+
55
+ ```ruby
56
+ class SignupForm < BaseForm::Form
57
+ attribute :email
58
+ attribute :password
59
+ attribute :password_confirmation
60
+ attribute :plan, Plan, default: proc { Plan.default }
61
+ end
62
+ ```
63
+
64
+ Note, if you don't specifies a Plan to this form, it will call a default value, which in
65
+ this case is calling a proc that will call a `default` method in `Plan` model and probably
66
+ this will return the default plan instance, fetched from the database or something like that.
67
+
68
+ Let's put some form specific validation here. For example, we don't want the Plan being forced
69
+ with an empty string for example:
70
+
71
+ ```ruby
72
+ class SignupForm < BaseForm::Form
73
+ # ... attributes, validations ...
74
+
75
+ validates :plan, presence: true
76
+ end
77
+ ```
78
+
79
+ Now you may be asking: What about email and password? Did not should be validated as well?
80
+ Well, you could, in fact, perform all validations in this form, but sometimes this can happen.
81
+ Then, I'm showing here the case that `User` model has those validations. Don't be mad ok? :)
82
+
83
+ The form validations are the first validations tha are performed before it try to persist
84
+ something here. If this validation fails, for an example, the persist method will not even
85
+ be called, and the talk is over. Otherwise, it wil try to persist your logic, that we'll
86
+ implement next.
87
+
88
+ Ok, now, you need to set the records that you will persist here.
89
+ In this case is the `:user` you want to save, and the `:account` you will want to associate
90
+ to this user. So, you put it there (I recommend you let this in the top of the class to make
91
+ it clear):
92
+
93
+ ```ruby
94
+ class SignupForm < BaseForm::Form
95
+ use_form_records :user, :account
96
+
97
+ # ... attributes, validations ...
98
+ end
99
+ ```
100
+
101
+ This line will automatically generate `attr_readers` to each record there, and will add this
102
+ symbols in an array called `form_records` in your class. To understand it better, let's talk
103
+ about the `persist` implementation itself.
104
+
105
+ By the rule, `persist` method is obligatory, and not implementing it, will cause your form
106
+ raise a `NotImplementedError` when calling `save` to it.
107
+
108
+ All things written inside `persist` method, will automatically run in a ActiveRecord transaction,
109
+ and if some record have its validation failed, this will perform a Rollback and deliver the form
110
+ to you with those erros grouped through `errors` method, like any AR model you are already
111
+ familiar with.
112
+
113
+ Let me stop to talk and show you something we can call as implementation of this:
114
+
115
+ ```ruby
116
+ class SignupForm < BaseForm::Form
117
+ # form records, attributes, validations, whatever
118
+
119
+ private # because isolation is still a necessary evil ;)
120
+
121
+ def persist
122
+ @account ||= Account.create plan: plan
123
+ @user ||= account.users.create user_params
124
+ end
125
+
126
+ def user_params
127
+ {
128
+ email: email,
129
+ password: password,
130
+ password_confirmation: password_confirmation,
131
+ account_owner: true
132
+ }
133
+ end
134
+ end
135
+ ```
136
+
137
+ So, here is the thing, see the variables names I've associated there, are the name os
138
+ form_records I've defined before. It tries to create an account, setting a plan to it
139
+ and then tries to create a user associated to this brand new account.
140
+
141
+ Is this `form_records` that will call each object associated here to check its errors,
142
+ and group it in `errors` object in your form itself in case of some validation brakes,
143
+ but if all is fine, the form instance is returned to you and you will be able to call
144
+ methods like `persisted?`, `account`, `user`, `valid?` and etc...
145
+
146
+ Are you still there? :D
147
+
148
+ Let's see this class complete?
149
+
150
+ ```ruby
151
+ class SignupForm < BaseForm
152
+ use_form_records :account, :user
153
+
154
+ attribute :email
155
+ attribute :password
156
+ attribute :password_confirmation
157
+ attribute :plan, Plan, default: proc { Plan.default }
158
+
159
+ validates :plan, presence: true
160
+
161
+ private
162
+
163
+ def persist
164
+ @account ||= Account.create plan: plan
165
+ @user ||= account.users.create user_params
166
+ end
167
+
168
+ def user_params
169
+ {
170
+ email: email,
171
+ password: password,
172
+ password_confirmation: password_confirmation,
173
+ account_owner: true
174
+ }
175
+ end
176
+ end
177
+ ```
178
+
179
+ Hmmmm, seems to be good enough. I hope this helps someone in the same way it
180
+ helped me. Thanks!
181
+
182
+ ## Contributing
183
+ - Fork it
184
+ - Make your implementations
185
+ - Send me a pull request
186
+
187
+ Thank you!
188
+
189
+ ## License
190
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,18 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'BaseForm'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ require 'bundler/gem_tasks'
18
+
@@ -0,0 +1,10 @@
1
+ # require dependencies gems
2
+ require 'active_support'
3
+ require 'active_model'
4
+ require 'virtus'
5
+
6
+ # require lib files
7
+ require 'base_form/form'
8
+
9
+ module BaseForm
10
+ end
@@ -0,0 +1,80 @@
1
+ module BaseForm
2
+ # This class is the main core functionality, by being an inheritable
3
+ # class, which controls the form attributes assignments, validations
4
+ # and persisting.
5
+ #
6
+ # Basically you should create your own Form Object Class, and inherit
7
+ # this class (BaseForm::Form). After that you should put all records
8
+ # in the `@form_records` variable, through `use_form_records` class
9
+ # method. In your `persist` method implementation, just create the
10
+ # record objects associating it to each variable in `form_records.`
11
+ class Form
12
+ include ActiveModel::Model
13
+ include Virtus.model
14
+
15
+ class << self
16
+ attr_reader :form_records
17
+
18
+ protected
19
+
20
+ def use_form_records(*records)
21
+ attr_reader(*records)
22
+
23
+ @form_records = records
24
+ end
25
+ end
26
+
27
+ def self.save(*params)
28
+ new(*params).save
29
+ end
30
+
31
+ # This method will make the things happen. It'll try run validations
32
+ # set in your form class, and if it passes, it'll run your persist
33
+ # instructions in a block of ActiveRecord transaction. If some
34
+ # record fails it's persistence/validation, then a rollback will be
35
+ # raised, the form will return those errors grouped in it. Otherwise
36
+ # everything is commited and `persisted?` method will return true.
37
+ def save
38
+ perform_in_transaction { persist } if valid?
39
+
40
+ self
41
+ end
42
+
43
+ def valid?
44
+ errors.empty? && super
45
+ end
46
+
47
+ def persisted?
48
+ @persisted
49
+ end
50
+
51
+ protected
52
+
53
+ def persist
54
+ raise NotImplementedError
55
+ end
56
+
57
+ def perform_in_transaction
58
+ ActiveRecord::Base.transaction do
59
+ yield if block_given?
60
+
61
+ records_errors.each { |error| add_errors_for(error) }
62
+ raise ActiveRecord::Rollback if errors.any?
63
+
64
+ @persisted = true
65
+ end
66
+ end
67
+
68
+ def records_errors
69
+ self.class.form_records.map do |form_record|
70
+ send(form_record).try(:errors)
71
+ end.compact.flatten
72
+ end
73
+
74
+ def add_errors_for(error)
75
+ error.each do |attribute, message|
76
+ errors.add attribute, message
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,3 @@
1
+ module BaseForm
2
+ VERSION = '0.1.1'
3
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: base_form
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - andrerpbts
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-10-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '3.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '3.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: virtus
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ description: BaseForm is a small and simple Rails plugin to work with Form Objects
42
+ email:
43
+ - andrerpbts@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - MIT-LICENSE
49
+ - README.md
50
+ - Rakefile
51
+ - lib/base_form.rb
52
+ - lib/base_form/form.rb
53
+ - lib/base_form/version.rb
54
+ homepage: https://github.com/andrerpbts/base_form
55
+ licenses:
56
+ - MIT
57
+ metadata: {}
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubyforge_project:
74
+ rubygems_version: 2.5.1
75
+ signing_key:
76
+ specification_version: 4
77
+ summary: A simple and small form objects Rails plugin
78
+ test_files: []
79
+ has_rdoc: