formant 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0ef99a58902f6fdda0ef94e433385faa28d6d45d
4
+ data.tar.gz: 90212569c9a2edc2f82f6f1723fad875a8d36e3c
5
+ SHA512:
6
+ metadata.gz: f1a28c65af280f5b1b98c0dcfd4a931dca84efd2d827756c163913a1f066af2b5ec609ebc4a45af80efc75d383dc9f98250fdcf660b5fa513e517411a99ad003
7
+ data.tar.gz: ced2edd67ec3e4334be521f228e5445f8d0e8d7a0f09248171a4aef492d494b7ee2572f47436d85a0da33ea91ad19b3600cac4f9358883d3197a554904237fa7
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,13 @@
1
+ language: ruby
2
+ cache: bundler
3
+
4
+ rvm:
5
+ - 1.9.3
6
+ - 2.0.0
7
+ - 2.1
8
+ - 2.2.2
9
+ - jruby
10
+ - rbx-2
11
+
12
+ before_install: gem install bundler -v 1.10.6
13
+ script: 'bundle exec rake'
@@ -0,0 +1,14 @@
1
+ # Change Log
2
+ All notable changes to this project will be documented in this file.
3
+ This project adheres to [Semantic Versioning](http://semver.org/).
4
+
5
+ ## 0.1.1 2015-08-17
6
+ [changed] Make phony_rails gem a runtime dependency.
7
+ [changed] Internal test changes, switch to use activeSupport::TestCase.
8
+ [changed] Set random US timezone in test helper to help flush out timezone bugs.
9
+
10
+ ## 0.1.1 2015-08-17
11
+ [changed] Updated dependencies in gemspec.
12
+
13
+ ## 0.1.0 2015-08-17
14
+ Initial release.
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in formant.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Anthony Garcia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,329 @@
1
+ # Formant
2
+
3
+ [![Build Status](https://travis-ci.org/polypressure/formant.svg?branch=master)](https://travis-ci.org/polypressure/formant)
4
+ [![Test Coverage](https://codeclimate.com/github/polypressure/formant/badges/coverage.svg)](https://codeclimate.com/github/polypressure/formant/coverage)
5
+ [![Code Climate](https://codeclimate.com/github/polypressure/formant/badges/gpa.svg)](https://codeclimate.com/github/polypressure/formant)
6
+ [![security](https://hakiri.io/github/polypressure/formant/master.svg)](https://hakiri.io/github/polypressure/formant/master)
7
+ [![Dependency Status](https://gemnasium.com/polypressure/formant.svg)](https://gemnasium.com/polypressure/formant)
8
+
9
+
10
+
11
+ Formant is a tiny library that provides a simplified, minimalistic form object implementation for Rails applications. A form object is a simple, (mostly) plain-old Ruby object, separate from your ActiveRecord models, that lets you collect and validate input. A bit more specifically, Formant helps you to keep any input parsing, normalization, validation, and formatting logic related to form processing out of your ActiveRecord models, ensuring that they stay lean and focused on persistence.
12
+
13
+ Form objects also simplify the collection of input from complicated forms. With form objects, when collecting input that involves multiple ActiveRecord models, you can avoid having to use something like `accepts_nested_attributes_for`. Instead, you define a single form object containing all the necessary fields spanning multiple models. You use this single form object in place of the multiple ActiveRecord models within your view, form, and controller. You can then parse, normalize, and validate that input in one place, and pass/distribute the form field values to whatever number of model objects are required by your business logic.
14
+
15
+ With Formant, you can declaratively specify any special parsing and normalization logic to apply to the form field values upon input (i.e. right before validation). Some built-in examples of parsing/normalizing input include:
16
+
17
+ * Stripping leading and trailing whitespace, and squishing internal whitespace.
18
+ * Parsing date/time strings using the current timezone.
19
+ * Normalizing phone numbers into a consistent internal format, e.g. "+13125551212" for storage in the database.
20
+ * Normalizing and parsing currency strings into a BigDecimal.
21
+
22
+ Once parsed and normalized, Formant then applies any validation rules that you've specified, using the standard validation macros provided by ActiveRecord. These can be invoked as usual, i.e. with the `valid?`, `invalid?`, or `validate` methods.
23
+
24
+ Formant also lets you specify special formatting rules on fields upon output (typically, when redisplaying forms). Some built-in examples of formatting output include:
25
+
26
+ * Rendering date/time values using localized string formats.
27
+ * Displaying phone numbers in a standard format, e.g. "(312) 555-1212"
28
+ * Formatting a BigDecimal value as a currency/price string.
29
+ * Formatting a number delimited with commas and decimal points.
30
+
31
+
32
+
33
+
34
+ ## Installation
35
+
36
+ Add this line to your application's Gemfile:
37
+
38
+ ```ruby
39
+ gem 'formant'
40
+ ```
41
+
42
+ And then execute:
43
+
44
+ $ bundle
45
+
46
+ Or install it yourself as:
47
+
48
+ $ gem install formant
49
+
50
+
51
+ ### Configuration
52
+
53
+ Add the following to your `config/application.rb`:
54
+
55
+ ```ruby
56
+ module MyApplication
57
+ class Application < Rails::Application
58
+ ...
59
+ config.autoload_paths << Rails.root.join('forms')
60
+ end
61
+ end
62
+ ```
63
+
64
+ You can create a locale file containing date/time formats named `time_formats.en.yml`. In a standard Rails application, this file would go in the `config/locales` directory, and would look something like this:
65
+
66
+ ```ruby
67
+ en:
68
+ time:
69
+ formats:
70
+ day_date_time: '%a, %b %e, %l:%M %p'
71
+ time_day_date: '%l:%M %p - %a %b %e'
72
+ fullday_date_time: '%A, %B %e, %l:%M %p'
73
+
74
+ ```
75
+
76
+ ## Usage
77
+
78
+ Define your form as a subclass of `Formant::FormObject`, specify the fields using `attr_accessor`, then specify parsing, validation, and formatting rules. Your form objects should go in the app/forms directory:
79
+
80
+ ```ruby
81
+ class AppointmentForm < FormObject
82
+
83
+ attr_accessor(
84
+ :starts_at,
85
+ :first_name, :last_name,
86
+ :phone, :email,
87
+ :monthly_revenue,
88
+ :some_big_number
89
+ )
90
+
91
+ #
92
+ # Rules for any special parsing/transformation upon input…
93
+ #
94
+
95
+ # This parses datetime strings using the current Time.zone
96
+ # into a ActiveSupport::TimeWithZone object:
97
+ parse :starts_at, as: :datetime
98
+
99
+ # This normalizes phone numbers into a consistent internal format
100
+ # (with no dashes, dots, or other separators, and a leading "+1").
101
+ # For example, if the user passes in a phone number in the format
102
+ # "312-555-1212" or "(312)555.1212", it will be normalized
103
+ # into the format "+13125551212":
104
+ parse :phone, as: :phone_number
105
+
106
+ # This normalizes and parses a string price (possibly containing
107
+ # currency symbols, commas, and decimal points) into a BigDecimal
108
+ # value:
109
+ parse :monthly_revenue, as: :currency
110
+
111
+
112
+ # This strips leading and trailing whitespace from the field values:
113
+ parse :last_name, to: :strip_whitespace
114
+ parse :email, to: :strip_whitespace
115
+ # This also collapses multiple consecutive internal spaces into a single space:
116
+ parse :first_name, to: :strip_whitespace, squish: true
117
+
118
+
119
+ #
120
+ # Rules for any special formatting/transformation upon output
121
+ # or redisplay of the form. These will be triggered by invoking
122
+ # the FormObject#reformatted! method…
123
+ #
124
+
125
+ # This reformats the datetime in the :starts_at field using the
126
+ # format specified by the :day_date_time key, which can be defined
127
+ # in config/locales/time_formats.en.yml file:
128
+ reformat :starts_at, as: :datetime, format: :day_date_time
129
+
130
+ # This reformats the phone number in the :phone field into a
131
+ # standard format for the 'US'. For a phone number normalized
132
+ # to "+13125551212", the reformatted number is "(312) 555-1212":
133
+ reformat :phone, as: :phone_number, country_code: 'US'
134
+
135
+ # This reformats the BigDecimal in the :monthly_revenue field
136
+ # into a string, eg. "$10,252.32". You can pass it options as
137
+ # defined in http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#method-i-number_to_currency
138
+ reformat :monthly_revenue, as: :currency
139
+
140
+ # This reformats the number in the :some_big_number field into
141
+ # string delimited with commas and decimal points, e.g.
142
+ # "25,123.08". You can pass it options as # defined in
143
+ # http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#method-i-number_with_delimiter
144
+ reformat :some_big_number, as: :number_with_delimiter
145
+
146
+ #
147
+ # Validation rules, as usual:
148
+ #
149
+ validates :starts_at, presence: true
150
+ validate :in_future
151
+
152
+ validates :first_name, presence: true
153
+ validates :last_name, presence: true
154
+
155
+ validates_plausible_phone :phone, presence: true
156
+ validates :email, presence: true, email: true
157
+
158
+ def in_future
159
+ errors.add(:starts_at, "must be in the future") if starts_at && starts_at.past?
160
+ end
161
+
162
+ end
163
+ ```
164
+
165
+ In your controller, instantiate a FormObject by passing it the request params, then validate the input params by calling either `valid?` or `invalid?`. If validation succeeds, you can then pass the form field values to any models or business-logic objects. If validation fails, you can redisplay the form as usual, passing the form object (rather than the model object) to the view—just as you would with an ActiveRecord model:
166
+
167
+ ```ruby
168
+ class AppointmentsController < ApplicationController
169
+
170
+ def create
171
+ appointment_form = AppointmentForm.new(appointment_params)
172
+
173
+ #
174
+ # Ideally, this should be in a separate business-logic object,
175
+ # we're showing this logic inline within the controller for
176
+ # the sake of simplicity:
177
+ #
178
+ if appointment_form.invalid?
179
+ @appointment_form = appointment_form.reformatted!
180
+ render :new and return
181
+ end
182
+
183
+ client = Client.new(
184
+ first_name: appointment_form.first_name,
185
+ last_name: appointment_form.last_name,
186
+ phone: appointment_form.phone,
187
+ email: appointment_form.email
188
+ )
189
+
190
+ appointment = Appointment.new(
191
+ starts_at: appointment_form.starts_at
192
+ )
193
+
194
+ client.save!
195
+ appointment.save!
196
+
197
+ redirect_to appointments_path, notice: "Your appointment has been booked."
198
+ end
199
+
200
+ end
201
+ ```
202
+
203
+ Your form looks just as it normally would, but rather than using your ActiveRecord models directly, you use the form object instead. Here's an example written in the Slim templating language:
204
+
205
+ ```ruby
206
+ h4 Make an appointment
207
+
208
+ = form_for(@appointment_form, url: appointments_url) do |f|
209
+ = render "shared/validation_errors", errors: @appointment_form.errors
210
+
211
+ .row.collapse
212
+ .small-2.columns
213
+ = f.text_field :starts_at, placeholder: 'Date/Time', required: true
214
+ .small-2.columns
215
+ = f.text_field :first_name, placeholder: 'First Name', required: true
216
+ .small-2.columns
217
+ = f.text_field :last_name, placeholder: 'Last Name', required: true
218
+ .small-2.columns
219
+ = f.text_field :phone, placeholder: 'Phone', required: true
220
+ .small-2.columns
221
+ = f.text_field :email, placeholder: 'Email', required: true, type: 'email'
222
+
223
+ .row
224
+ .small-12.columns
225
+ = f.button 'Submit'
226
+ ```
227
+ You can see that you can access the fields and the validation errors as you usually would with an ActiveRecord model.
228
+
229
+ ### Other stuff
230
+
231
+ You can get a params hash of all the form object's attribute by calling `to_params`.
232
+
233
+ You can register callbacks to be invoked before and after validation:
234
+
235
+ ```ruby
236
+ class MyForm < Formant::FormObject
237
+ ...
238
+
239
+ before_validation :do_stuff_before_validation
240
+ after_validation :do_stuff_after_validation
241
+
242
+ ...
243
+
244
+ def do_stuff_before_validation
245
+ # Some pre-validation logic.
246
+ end
247
+
248
+ def do_stuff_after_validation
249
+ # Some post validation logic.
250
+ end
251
+
252
+ end
253
+
254
+ ```
255
+
256
+ ## Additional parsing and reformatting rules
257
+
258
+ Parsing and formatting rules can be added trivially. Here is an example for how you would add rules for converting a blank string into a nil:
259
+
260
+ ```ruby
261
+ #
262
+ #
263
+ module BlankAsNil
264
+
265
+ #
266
+ # All parse rule methods must have a name beginning with
267
+ # the prefix "parse_" and ending with the parse rule type.
268
+ # In this example, the parse rule type is "slug".
269
+ #
270
+ # The method signature should always be as follows: the first
271
+ # argument is field_value, in which Formant passes the unparsed
272
+ # value of the form field/attribute. The second argument
273
+ # contains a hash of any options required by your parse rule
274
+ # logic.
275
+ #
276
+ # Your parse method should return the parsed value of course.
277
+ # Formant takes care of assigning it to the field attribute.
278
+ #
279
+ def parse_blank_into_nil(field_value, options={})
280
+ field_value.nil? || (field_value.is_a?(String) && field_value !~ /\S/) ? nil : field_value
281
+ end
282
+
283
+
284
+ #
285
+ # For a reformatting rule, it's pretty much the same thing,
286
+ # except you replace the "parse_" prefix in the method name
287
+ # with "format_".
288
+ #
289
+ # This is an example for formatting numbers with a delimiter,
290
+ # assuming Formant didn't already provide this:
291
+ #
292
+ def format_number_with_delimiter(field_value, options={})
293
+ number_with_delimiter(field_value, options)
294
+ end
295
+
296
+ end
297
+
298
+ #
299
+ # You can then specify that the parse rule can be used as normal
300
+ # with the parse macro/directive:
301
+ #
302
+ class MyForm < Formant::FormObject
303
+ include MoreParsingAndFormattingRules
304
+
305
+ attr_accessor :title, :some_number
306
+
307
+ parse :title, as: :blank_into_nil
308
+ format :some_number, as: :number_with_delimiter
309
+ end
310
+
311
+
312
+
313
+ ```
314
+
315
+
316
+
317
+ ## Development
318
+
319
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
320
+
321
+ 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` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
322
+
323
+ ## Contributing
324
+
325
+ 1. Fork it ( https://github.com/polypressure/formant/fork )
326
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
327
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
328
+ 4. Push to the branch (`git push origin my-new-feature`)
329
+ 5. Create a new Pull Request
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ t.pattern = "test/*_test.rb"
7
+ end
8
+
9
+ task default: :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "formant"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'formant/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "formant"
8
+ spec.version = Formant::VERSION
9
+ spec.authors = ["Anthony Garcia"]
10
+ spec.email = ["polypressure@outlook.com"]
11
+
12
+ spec.summary = "Minimalist form object implementation."
13
+ spec.homepage = "https://github.com/polypressure/formant"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency 'phony_rails', '~> 0.12.9'
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.9"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "activemodel", "~>4.0"
26
+ spec.add_development_dependency "activesupport", "~>4.0"
27
+ spec.add_development_dependency "actionview", "~>4.0"
28
+ spec.add_development_dependency "minitest", "~> 5.5"
29
+ spec.add_development_dependency 'm', '~> 1.3', '>= 1.3.1'
30
+ spec.add_development_dependency 'minitest-reporters', '~> 1.0', '>= 1.0.19'
31
+ spec.add_development_dependency "codeclimate-test-reporter", '~> 0.4.7'
32
+
33
+ end
@@ -0,0 +1,186 @@
1
+ require "formant/version"
2
+
3
+ require 'active_model'
4
+ require 'active_support/all'
5
+ require 'action_view'
6
+ require 'phony_rails'
7
+
8
+
9
+ module Formant
10
+ include ActionView::Helpers::NumberHelper
11
+
12
+ #
13
+ # Base class for Form objects.
14
+ #
15
+ # See https://www.reinteractive.net/posts/158-form-objects-in-rails
16
+ # (among others) for more info on form objects for more details.
17
+ #
18
+ # This is a simplified implementation of form objects that focuses
19
+ # on collecting input in a PORO, specifying any special parsing
20
+ # and transformation on fields upon input (i.e., before validation),
21
+ # and special formatting/transformation on fields for display/output
22
+ # and redisplay of forms.
23
+ #
24
+ class FormObject
25
+
26
+ include ActiveModel::Model
27
+ include ActiveModel::Validations::Callbacks
28
+
29
+ before_validation :parse_fields!
30
+
31
+ class << self
32
+ attr_accessor :parse_fields, :format_fields
33
+ end
34
+
35
+ #
36
+ # Directive to add a parse rule for the specified attribute.
37
+ # Parse rules let you apply any special parsing/transformation
38
+ # logic on the form's attributes upon input. The parsing rules
39
+ # are applied automatically prior to validation.
40
+ #
41
+ # Usage example:
42
+ #
43
+ # class MyForm < FormObject
44
+ # ...
45
+ # parse :appointment_time, as: :datetime
46
+ # parse :phone, as: :phone_number
47
+ # parse :price, as: :currency
48
+ # parse :email, to: :strip_whitespace
49
+ # ...
50
+ # end
51
+ #
52
+ # The above example specifies that the appointment_time attribute
53
+ # should be parsed with the parse_datetime method (which converts
54
+ # the datetime string into an ActiveSupport::TimeWithZone object),
55
+ # and that the # phone attribute should be parsed with the
56
+ # parse_phone_number method (which normalizes the phone number into
57
+ # a standard format).
58
+ #
59
+ def self.parse(field_name, options={})
60
+ self.parse_fields ||= []
61
+ parse_type = options[:as] || options[:to]
62
+ raise "no parse type provided" if parse_type.blank?
63
+ self.parse_fields << [ field_name, "parse_#{parse_type}", options ]
64
+ end
65
+
66
+ #
67
+ # Directive to add a reformat rule for the specified attribute.
68
+ # These let you apply any special formatting/normalization
69
+ # logic on the form's attributes upon output. Typically you want
70
+ # to do this when you have to redisplay a form.
71
+ #
72
+ # The format rules are triggered when the FormObject#reformatted!
73
+ # method is invoked, which modifies the attribute in place.
74
+ #
75
+ # Usage example:
76
+ #
77
+ # class MyForm < FormObject
78
+ # ...
79
+ # reformat :appointment_time, as: :datetime, format: :day_date_time
80
+ # reformat :phone, as: :phone_number, country_code: 'US'
81
+ # ...
82
+ # end
83
+ #
84
+ # The above example specifies that the appointment_time attribute
85
+ # should be reformatted with the format_datetime method (using the format
86
+ # specified in the locale file with the :day_date_time key), and that
87
+ # the phone attribute should be parsed with the parse_phone_number
88
+ # method, and a fixed country_code of 'US'
89
+ #
90
+ def self.reformat(field_name, options={})
91
+ self.format_fields ||= []
92
+ self.format_fields << [ field_name, "format_#{options[:as]}", options ]
93
+ end
94
+
95
+ #
96
+ # Triger any formatting rules specified with the reformat directive.
97
+ # The attributes are reformatted and mutated in place.
98
+ #
99
+ # Returns an instance of the form object.
100
+ #
101
+ def reformatted!
102
+ self.class.format_fields.each do |field_name, format_method, options|
103
+ formatted_value = send(format_method, get_field(field_name), options)
104
+ set_field(field_name, formatted_value)
105
+ end
106
+ self
107
+ end
108
+
109
+ #
110
+ # Return all the attributes as a params hash.
111
+ #
112
+ def to_params
113
+ attrs = Hash.new
114
+ instance_variables.each do |ivar|
115
+ name = ivar[1..-1]
116
+ attrs[name.to_sym] = instance_variable_get(ivar) if respond_to? "#{name}="
117
+ end
118
+ attrs
119
+ end
120
+
121
+
122
+ private
123
+
124
+ def parse_datetime(field_value, options={})
125
+ Time.zone.parse(field_value || '')
126
+ end
127
+
128
+ def parse_phone_number(field_value, options={})
129
+ cc = options[:country_code] || 'US'
130
+ PhonyRails.normalize_number(field_value, country_code: cc)
131
+ end
132
+
133
+ def parse_currency(field_value, options={})
134
+ if field_value.is_a?(String)
135
+ field_value.gsub(/[^0-9\.]+/, '').to_d
136
+ elsif field_value.is_a?(Numeric)
137
+ field_value.to_d
138
+ else
139
+ field_value
140
+ end
141
+ end
142
+
143
+ def parse_strip_whitespace(field_value, options={})
144
+ return field_value unless field_value.is_a?(String)
145
+ options[:squish] ? field_value.squish : field_value.strip if field_value
146
+ end
147
+
148
+ def format_datetime(field_value, options={})
149
+ I18n.l(field_value, options).squish if field_value
150
+ end
151
+
152
+ def format_phone_number(field_value, options={})
153
+ field_value.phony_formatted(options) if field_value
154
+ end
155
+
156
+ def format_currency(field_value, options={})
157
+ ActionView::Base.new.number_to_currency(field_value, options) if field_value
158
+ end
159
+
160
+ def format_number_with_delimiter(field_value, options={})
161
+ ActionView::Base.new.number_with_delimiter(field_value, options)
162
+ end
163
+
164
+ def parse_fields!
165
+ self.class.parse_fields.each do |field_name, parse_method, options|
166
+ parsed_value = send(parse_method, get_field(field_name), options)
167
+ set_field(field_name, parsed_value)
168
+ end
169
+ true
170
+ end
171
+
172
+ def get_field(field_name)
173
+ instance_variable_get(as_sym(field_name))
174
+ end
175
+
176
+ def set_field(field_name, value)
177
+ instance_variable_set(as_sym(field_name), value)
178
+ end
179
+
180
+ def as_sym(field_name)
181
+ field = "@#{field_name}".to_sym
182
+ end
183
+
184
+ end
185
+
186
+ end
@@ -0,0 +1,3 @@
1
+ module Formant
2
+ VERSION = "0.1.2"
3
+ end
metadata ADDED
@@ -0,0 +1,209 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: formant
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Anthony Garcia
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-08-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: phony_rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.12.9
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.12.9
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.9'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activemodel
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '4.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: activesupport
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '4.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '4.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: actionview
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '4.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '4.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: minitest
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '5.5'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '5.5'
111
+ - !ruby/object:Gem::Dependency
112
+ name: m
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.3'
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: 1.3.1
121
+ type: :development
122
+ prerelease: false
123
+ version_requirements: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - "~>"
126
+ - !ruby/object:Gem::Version
127
+ version: '1.3'
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: 1.3.1
131
+ - !ruby/object:Gem::Dependency
132
+ name: minitest-reporters
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '1.0'
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: 1.0.19
141
+ type: :development
142
+ prerelease: false
143
+ version_requirements: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - "~>"
146
+ - !ruby/object:Gem::Version
147
+ version: '1.0'
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: 1.0.19
151
+ - !ruby/object:Gem::Dependency
152
+ name: codeclimate-test-reporter
153
+ requirement: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - "~>"
156
+ - !ruby/object:Gem::Version
157
+ version: 0.4.7
158
+ type: :development
159
+ prerelease: false
160
+ version_requirements: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - "~>"
163
+ - !ruby/object:Gem::Version
164
+ version: 0.4.7
165
+ description:
166
+ email:
167
+ - polypressure@outlook.com
168
+ executables: []
169
+ extensions: []
170
+ extra_rdoc_files: []
171
+ files:
172
+ - ".gitignore"
173
+ - ".travis.yml"
174
+ - CHANGELOG.md
175
+ - CODE_OF_CONDUCT.md
176
+ - Gemfile
177
+ - LICENSE.txt
178
+ - README.md
179
+ - Rakefile
180
+ - bin/console
181
+ - bin/setup
182
+ - formant.gemspec
183
+ - lib/formant.rb
184
+ - lib/formant/version.rb
185
+ homepage: https://github.com/polypressure/formant
186
+ licenses:
187
+ - MIT
188
+ metadata: {}
189
+ post_install_message:
190
+ rdoc_options: []
191
+ require_paths:
192
+ - lib
193
+ required_ruby_version: !ruby/object:Gem::Requirement
194
+ requirements:
195
+ - - ">="
196
+ - !ruby/object:Gem::Version
197
+ version: '0'
198
+ required_rubygems_version: !ruby/object:Gem::Requirement
199
+ requirements:
200
+ - - ">="
201
+ - !ruby/object:Gem::Version
202
+ version: '0'
203
+ requirements: []
204
+ rubyforge_project:
205
+ rubygems_version: 2.4.8
206
+ signing_key:
207
+ specification_version: 4
208
+ summary: Minimalist form object implementation.
209
+ test_files: []