super_form 0.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a5f0e95c7ffd7c811db1288e98272f0544bcacaa
4
+ data.tar.gz: f5e6d6330f0524b513fdf6cf4b4305cf90e32a1d
5
+ SHA512:
6
+ metadata.gz: 7617b22b0f8b2530b9ec7a08cc3ba3e79f7b87f3526a6f3ecadea596b9ccb2ccc45121afd5ada9a95b57ff87d42707638e3818b49c618c2e6e1d5cb1019c502c
7
+ data.tar.gz: af71f75d3eb608ba7975890d02c9379a2f398c567720cc1e4e2997b5814101e58342acffbb6a5715b3a0f5828a7a62e69f5e1ea642fb1021a37b06b54d0ceb8b
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in super_form.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Thiago A. Silva
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,12 @@
1
+ SuperForm
2
+ ---------
3
+
4
+ SuperForm adopts the concept of forms, fields and fieldsets, making it easy to define fields containing
5
+ bundled validations and attributes. Form fields will be truly object oriented; that will make your code more DRY,
6
+ so you won't need to define the same validations and attributes over and over again. SuperForm also contains
7
+ fields for mundane concepts (like email and phone) - but you can still use it to code specific fields for
8
+ your application. Forms are flexible, and allow you to provide specific validations and attributes. Also, SuperForm
9
+ aims to supply a handy form builder for these fields.
10
+
11
+ This is still a work in progress, an early prototype. No release has been made yet. I'm working towards
12
+ the first release.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/autospec ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'autospec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rspec-core', 'autospec')
data/bin/rspec ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rspec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rspec-core', 'rspec')
data/lib/attribute.rb ADDED
@@ -0,0 +1,6 @@
1
+ module Attribute
2
+ autoload :CPF, 'attribute/cpf'
3
+ autoload :CNPJ, 'attribute/cnpj'
4
+ autoload :Telephone, 'attribute/telephone'
5
+ autoload :PersonType, 'attribute/person_type'
6
+ end
@@ -0,0 +1,10 @@
1
+ require 'virtus'
2
+ require 'cnpj'
3
+
4
+ module Attribute
5
+ class CNPJ < Virtus::Attribute
6
+ def coerce(value)
7
+ ::CNPJ.new(value).stripped
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ require 'virtus'
2
+
3
+ module Attribute
4
+ class CPF < Virtus::Attribute
5
+ def coerce(value)
6
+ ::CPF.new(value).stripped
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'virtus'
2
+
3
+ module Attribute
4
+ class PersonType < Virtus::Attribute
5
+ def coerce(value)
6
+ ::PersonType.new(value).value
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'virtus'
2
+
3
+ module Attribute
4
+ class Telephone < Virtus::Attribute
5
+ def coerce(value)
6
+ ::Telephone.new(value).stripped if value
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ class CnpjValidator < ActiveModel::EachValidator
2
+ def validate_each(record, attribute, value)
3
+ return if value.blank?
4
+
5
+ unless CNPJ.new(value).valid?
6
+ default_message = I18n.t('activemodel.errors.messages.cnpj', 'invalid CNPJ', default: 'invalid CNPJ')
7
+ record.errors[attribute] << (options[:message] || default_message)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ class CpfValidator < ActiveModel::EachValidator
2
+ def validate_each(record, attribute, value)
3
+ return if value.blank?
4
+
5
+ unless CPF.new(value).valid?
6
+ default_message = I18n.t('activemodel.errors.messages.cpf', 'invalid CPF', default: 'invalid CPF')
7
+ record.errors[attribute] << (options[:message] || default_message)
8
+ end
9
+ end
10
+ end
data/lib/field.rb ADDED
@@ -0,0 +1,11 @@
1
+ module Field
2
+ autoload :Error, 'field/error'
3
+ autoload :Base, 'field/base'
4
+ autoload :Text, 'field/text'
5
+ autoload :Password, 'field/password'
6
+ autoload :Email, 'field/email'
7
+ autoload :CPF, 'field/cpf'
8
+ autoload :CNPJ, 'field/cnpj'
9
+ autoload :Telephone, 'field/telephone'
10
+ autoload :PersonType, 'field/person_type'
11
+ end
data/lib/field/base.rb ADDED
@@ -0,0 +1,35 @@
1
+ module Field
2
+ class Base
3
+ attr_accessor :name
4
+ attr_accessor :form
5
+ attr_accessor :value
6
+ attr_reader :fieldset
7
+
8
+ def initialize(name, fieldset)
9
+ @name = name
10
+ @fieldset = fieldset
11
+ end
12
+
13
+ def add_attributes(klass, options)
14
+ klass.attribute name, self.attribute
15
+ end
16
+
17
+ def add_validations(klass, options)
18
+ if options.any?
19
+ klass.send(:validates, name, options)
20
+ end
21
+ end
22
+
23
+ def form?
24
+ false
25
+ end
26
+
27
+ def attribute
28
+ String
29
+ end
30
+
31
+ def output
32
+ value
33
+ end
34
+ end
35
+ end
data/lib/field/cnpj.rb ADDED
@@ -0,0 +1,35 @@
1
+ require 'cnpj'
2
+ require 'cnpj_validator'
3
+
4
+ module Field
5
+ class CNPJ < Base
6
+ def add_validations(klass, options)
7
+ klass.validates name, cnpj: true
8
+
9
+ if options[:uniqueness]
10
+ unless options[:uniqueness].is_a?(Hash) && options[:uniqueness][:model]
11
+ raise Field::Error, "Must specify a model to validate uniqueness"
12
+ end
13
+
14
+ required = {
15
+ attribute: name,
16
+ allow_nil: true,
17
+ allow_blank: true
18
+ }
19
+
20
+ klass.validates name, uniqueness: options[:uniqueness].merge(required)
21
+ options.reject! { |k| k == :uniqueness }
22
+ end
23
+
24
+ super
25
+ end
26
+
27
+ def attribute
28
+ ::Attribute::CNPJ
29
+ end
30
+
31
+ def output
32
+ ::CNPJ.new(value).formatted
33
+ end
34
+ end
35
+ end
data/lib/field/cpf.rb ADDED
@@ -0,0 +1,35 @@
1
+ require 'cpf'
2
+ require 'cpf_validator'
3
+
4
+ module Field
5
+ class CPF < ::Field::Base
6
+ def add_validations(klass, options)
7
+ klass.validates name, cpf: true
8
+
9
+ if options[:uniqueness]
10
+ unless options[:uniqueness].is_a?(Hash) && options[:uniqueness][:model]
11
+ raise Field::Error, "Must specify a model to validate uniqueness"
12
+ end
13
+
14
+ required = {
15
+ attribute: name,
16
+ allow_nil: true,
17
+ allow_blank: true
18
+ }
19
+
20
+ klass.validates name, uniqueness: options[:uniqueness].merge(required)
21
+ options.reject! { |k| k == :uniqueness }
22
+ end
23
+
24
+ super
25
+ end
26
+
27
+ def attribute
28
+ ::Attribute::CPF
29
+ end
30
+
31
+ def output
32
+ ::CPF.new(value).formatted
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,32 @@
1
+ require 'validates_email_format_of'
2
+
3
+ module Field
4
+ class Email < Base
5
+ def add_validations(klass, options)
6
+ klass.validates name, length: { maximum: 155 }
7
+ klass.validates name, email_format: {
8
+ message: I18n.t('activemodel.errors.messages.email'),
9
+ allow_nil: true,
10
+ allow_blank: true
11
+ }
12
+
13
+ if options[:uniqueness]
14
+ unless options[:uniqueness].is_a?(Hash) && options[:uniqueness][:model]
15
+ raise Field::Error, "Must specify a model to validate uniqueness"
16
+ end
17
+
18
+ required = {
19
+ case_sensitive: false,
20
+ attribute: name,
21
+ allow_nil: true,
22
+ allow_blank: true
23
+ }
24
+
25
+ klass.validates name, uniqueness: options[:uniqueness].merge(required)
26
+ options.reject! { |k| k == :uniqueness }
27
+ end
28
+
29
+ super
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ module Field
2
+ class Error < ::StandardError; end
3
+ end
@@ -0,0 +1,21 @@
1
+ module Field
2
+ class Password < Base
3
+ def add_attributes(klass, options)
4
+ klass.attribute :"#{name}_confirmation", String
5
+
6
+ super
7
+ end
8
+
9
+ def add_validations(klass, options)
10
+ klass.validates name, presence: true, if: ->(f){ !f.to_param }
11
+ klass.validates name, confirmation: true, if: ->(f){ !f.to_param }
12
+ klass.validates name, length: { minimum: 5, maximum: 15 }, allow_blank: true
13
+
14
+ super
15
+ end
16
+
17
+ def output
18
+ nil
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ require 'person_type'
2
+
3
+ module Field
4
+ class PersonType < Base
5
+ def attribute
6
+ ::Attribute::PersonType
7
+ end
8
+
9
+ def output
10
+ ::PersonType.new(value).description
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ require 'telephone'
2
+ require 'telephone_validator'
3
+
4
+ module Field
5
+ class Telephone < Base
6
+ def add_validations(klass, options)
7
+ klass.validates name, telephone: true
8
+
9
+ super
10
+ end
11
+
12
+ def attribute
13
+ ::Attribute::Telephone
14
+ end
15
+
16
+ def output
17
+ return unless value
18
+ ::Telephone.new(value).formatted
19
+ end
20
+ end
21
+ end
data/lib/field/text.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'field/base'
2
+
3
+ module Field
4
+ class Text < ::Field::Base; end
5
+ end
@@ -0,0 +1,51 @@
1
+ class PersonType
2
+ TYPES = [NATURAL = 'natural_person', LEGAL = 'legal_entity']
3
+
4
+ attr_writer :value
5
+
6
+ def self.description(value, default = '')
7
+ description = case value
8
+ when NATURAL then 'Natural person'
9
+ when LEGAL then 'Legal entity'
10
+ else default
11
+ end
12
+
13
+ I18n.t(value, default: description)
14
+ end
15
+
16
+ def self.descriptions
17
+ TYPES.each_with_object({}) do |type, hash|
18
+ hash[type] = description(type)
19
+ end
20
+ end
21
+
22
+ def self.to_collection
23
+ descriptions.map { |k, v| [v, k] }
24
+ end
25
+
26
+ def initialize(value)
27
+ @value = value
28
+ end
29
+
30
+ def value
31
+ @value if valid?
32
+ end
33
+
34
+ def description(default = '')
35
+ self.class.description(@value, default)
36
+ end
37
+
38
+ def valid?
39
+ TYPES.include? @value
40
+ end
41
+
42
+ def to_s
43
+ value
44
+ end
45
+
46
+ class Attribute < Virtus::Attribute
47
+ def coerce(value)
48
+ ::PersonType.new(value).value
49
+ end
50
+ end
51
+ end
data/lib/super_form.rb ADDED
@@ -0,0 +1,216 @@
1
+ require "super_form/version"
2
+
3
+ require 'field'
4
+ require 'attribute'
5
+ require 'virtus'
6
+ require 'active_support/inflector'
7
+ require 'active_support/concern'
8
+ require 'active_model'
9
+
10
+ autoload :UniquenessValidator, 'uniqueness_validator'
11
+
12
+ module SuperForm
13
+ def self.included(klass)
14
+ virtus = if @virtus_options
15
+ Virtus.model(@virtus_options)
16
+ else
17
+ Virtus.model
18
+ end
19
+
20
+ klass.include virtus
21
+ klass.include ActiveModel::Conversion
22
+ klass.include ActiveModel::Validations
23
+ klass.extend ActiveModel::Naming
24
+ klass.extend ActiveModel::Callbacks
25
+ klass.extend ClassMethods
26
+
27
+ add_callbacks(klass)
28
+ add_constructor(klass)
29
+
30
+ klass.send(:attr_reader, :fieldsets)
31
+
32
+ @virtus_options = nil
33
+ end
34
+
35
+ def self.add_constructor(klass)
36
+ klass.class_eval do
37
+ alias_method :original_initializer, :initialize
38
+
39
+ def initialize(*args)
40
+ setup
41
+ original_initializer(*args)
42
+ end
43
+ end
44
+ end
45
+
46
+ def self.add_callbacks(klass)
47
+ klass.class_eval do
48
+ alias_method :ar_valid?, :valid?
49
+
50
+ def valid?
51
+ run_callbacks :validation do
52
+ ar_valid?
53
+ end
54
+ end
55
+
56
+ define_model_callbacks :validation, :save
57
+ end
58
+ end
59
+
60
+ def self.base(virtus_options = {})
61
+ @virtus_options = virtus_options
62
+ Form
63
+ end
64
+
65
+ def setup
66
+ initialize_fields
67
+ instance_eval(&self.class.setup) if self.class.setup
68
+ end
69
+
70
+ def persisted?
71
+ false
72
+ end
73
+
74
+ def fields
75
+ @fields
76
+ end
77
+
78
+ def field(name)
79
+ if self.class.form?(name)
80
+ send(name)
81
+ else
82
+ @fields.fetch(name)
83
+ end
84
+ end
85
+
86
+ def form?
87
+ true
88
+ end
89
+
90
+ def form_label
91
+ self.class.name.to_s.gsub(/Form$/, '').humanize
92
+ end
93
+
94
+ def save
95
+ if valid?
96
+ run_callbacks :save do
97
+ persist!
98
+ end
99
+ true
100
+ else
101
+ false
102
+ end
103
+ end
104
+
105
+ private
106
+
107
+ def initialize_fields
108
+ @fields = {}
109
+ @fieldsets = {}
110
+
111
+ # take care of branches here
112
+ self.class.fields.each do |id, field|
113
+ if self.class.form?(id)
114
+ @fields[id] = field
115
+
116
+ fieldset = self.class.child_form_fieldsets[id]
117
+ (@fieldsets[fieldset] ||= []) << @fields[id]
118
+ else
119
+ @fields[id] = field.dup
120
+ (@fieldsets[field.fieldset] ||= []) << @fields[id]
121
+ end
122
+ end
123
+ end
124
+
125
+ module ClassMethods
126
+ def setup(&block)
127
+ @setup = block if block
128
+ @setup
129
+ end
130
+
131
+ def form?(field_id = nil)
132
+ field_id.nil? ? true : child_form_fieldsets.has_key?(field_id)
133
+ end
134
+
135
+ # fieldset may be extracted as a domain object
136
+ def fieldset(id)
137
+ initialize_fieldset(id)
138
+ yield
139
+ close_fieldset
140
+ end
141
+
142
+ def child_form_fieldsets
143
+ @child_form_fieldsets ||= {}
144
+ end
145
+
146
+ # take care of branches here
147
+ def field(name, field_class, options = {})
148
+ if field_class.ancestors.include? SuperForm
149
+ child_form_fieldsets[name] = current_fieldset_name
150
+ attribute name, field_class
151
+
152
+ define_method(name) do
153
+ var = :"@#{name.to_s}"
154
+ value = instance_variable_get(var)
155
+
156
+ unless value
157
+ value = instance_variable_set(var, field_class.new)
158
+ end
159
+
160
+ value
161
+ end
162
+
163
+ create_child_validation(name)
164
+ field = name
165
+ else
166
+ field = field_class.new(name, current_fieldset_name)
167
+
168
+ field.add_validations(self, options)
169
+ field.add_attributes(self, options)
170
+
171
+ alias_method :"original_#{name.to_s}=", "#{name}="
172
+
173
+ define_method "#{name}=" do |value|
174
+ field = self.field(name)
175
+ field.value = value if field
176
+
177
+ send "original_#{name.to_s}=", value
178
+ end
179
+ end
180
+
181
+ fields[name] = field
182
+ end
183
+
184
+ def fields
185
+ @fields ||= {}
186
+ end
187
+
188
+ private
189
+
190
+ def initialize_fieldset(id)
191
+ @current = id
192
+ end
193
+
194
+ def close_fieldset
195
+ @current = nil
196
+ end
197
+
198
+ def current_fieldset_name
199
+ @current ||= default_fieldset_name
200
+ end
201
+
202
+ def default_fieldset_name
203
+ :general
204
+ end
205
+
206
+ def create_child_validation(name)
207
+ validate :"ensure_valid_#{name.to_s}"
208
+
209
+ define_method "ensure_valid_#{name.to_s}" do
210
+ unless send(name).send(:valid?)
211
+ errors.add(:base, "Invalid #{name.to_s}")
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,3 @@
1
+ module SuperForm
2
+ VERSION = "0.0.1"
3
+ end
data/lib/tasks/.keep ADDED
File without changes
data/lib/telephone.rb ADDED
@@ -0,0 +1,25 @@
1
+ class Telephone
2
+ FORMAT = /\A(\d{2})(\d{4})(\d{4})\Z/
3
+
4
+ attr_accessor :number
5
+
6
+ def initialize(number)
7
+ @number = number
8
+ end
9
+
10
+ def stripped
11
+ @number.gsub /[^\d]/, ''
12
+ end
13
+
14
+ def formatted
15
+ stripped.gsub FORMAT, "(\\1) \\2-\\3"
16
+ end
17
+
18
+ def valid?
19
+ stripped.match FORMAT
20
+ end
21
+
22
+ def to_s
23
+ formatted
24
+ end
25
+ end
@@ -0,0 +1,10 @@
1
+ class TelephoneValidator < ActiveModel::EachValidator
2
+ def validate_each(record, attribute, value)
3
+ return if value.blank?
4
+
5
+ unless Telephone.new(value).valid?
6
+ default_message = I18n.t('activemodel.errors.messages.telephone', default: 'invalid telephone')
7
+ record.errors[attribute] << (options[:message] || default_message)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,44 @@
1
+ class UniquenessValidator < ActiveRecord::Validations::UniquenessValidator
2
+ def setup(klass)
3
+ super
4
+ @klass = options[:model] if options[:model]
5
+ end
6
+
7
+ def validate_each(record, attribute, value)
8
+ # UniquenessValidator can't be used outside of ActiveRecord instances, here
9
+ # we return the exact same error, unless the 'model' option is given.
10
+ if ! options[:model] && ! record.class.ancestors.include?(ActiveRecord::Base)
11
+ raise ArgumentError, "Unknown validator: 'UniquenessValidator'"
12
+
13
+ # If we're inside an ActiveRecord class, and `model` isn't set, use the
14
+ # default behaviour of the validator.
15
+ elsif ! options[:model]
16
+ super
17
+
18
+ # Custom validator options. The validator can be called in any class, as
19
+ # long as it includes `ActiveModel::Validations`. You can tell the validator
20
+ # which ActiveRecord based class to check against, using the `model`
21
+ # option. Also, if you are using a different attribute name, you can set the
22
+ # correct one for the ActiveRecord class using the `attribute` option.
23
+ else
24
+ record_org, attribute_org = record, attribute
25
+
26
+ attribute = options[:attribute].to_sym if options[:attribute]
27
+
28
+ if options[:model]
29
+ record = options[:model].new(attribute => value)
30
+ else
31
+ model_name = options.fetch(:model_name) { record_org.class.to_s.downcase.split('::').last.gsub(/form$/, '') }
32
+ record = record_org.send(model_name)
33
+ end
34
+ record = record_org.send(options[:model].to_s.downcase)
35
+
36
+ super
37
+
38
+ if record.errors[attribute_org].any?
39
+ record_org.errors.add(attribute_org, :taken,
40
+ options.except(:case_sensitive, :scope).merge(value: value))
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,98 @@
1
+ require 'spec_helper'
2
+ require 'super_form'
3
+ require 'active_model'
4
+
5
+ module Field
6
+ class Null < Base; end
7
+ end
8
+
9
+ class DummyForm
10
+ include SuperForm
11
+ end
12
+
13
+ describe SuperForm do
14
+ before(:all) do
15
+ DummyForm.class_eval do
16
+ fieldset :general do
17
+ field :name, Field::Text, presence: true
18
+ end
19
+
20
+ fieldset :toys do
21
+ field :ball, Field::Null
22
+ field :car, Field::Text
23
+ end
24
+ end
25
+
26
+ @dummy_form = DummyForm.new
27
+ end
28
+
29
+ context 'when a field is defined at class level' do
30
+ before(:all) { @fields = DummyForm.fields }
31
+
32
+ it 'stores the basic field objects at class level' do
33
+ expect(@fields.fetch(:name)).to be_instance_of Field::Text
34
+ expect(@fields.fetch(:ball)).to be_instance_of Field::Null
35
+ expect(@fields.fetch(:car)).to be_instance_of Field::Text
36
+ end
37
+
38
+ it 'assigns the fieldset to the field' do
39
+ expect(@fields.fetch(:name).fieldset).to eq :general
40
+ expect(@fields.fetch(:ball).fieldset).to eq :toys
41
+ expect(@fields.fetch(:car).fieldset).to eq :toys
42
+ end
43
+ end
44
+
45
+ context 'when a form object is created' do
46
+ before(:each) { @dummy_form = DummyForm.new }
47
+
48
+ # this can get better, but for now it's ok
49
+ it 'creates virtus attributes' do
50
+ expect(@dummy_form).to respond_to(:name)
51
+ expect(@dummy_form).to respond_to(:name=)
52
+ expect(@dummy_form).to respond_to(:ball)
53
+ expect(@dummy_form).to respond_to(:ball=)
54
+ expect(@dummy_form).to respond_to(:car)
55
+ expect(@dummy_form).to respond_to(:car=)
56
+ end
57
+
58
+ it 'assigns the fieldset id to the field' do
59
+ expect(@dummy_form.field(:name).fieldset).to eq :general
60
+ expect(@dummy_form.field(:ball).fieldset).to eq :toys
61
+ expect(@dummy_form.field(:car).fieldset).to eq :toys
62
+ end
63
+
64
+ it 'defines fieldsets containing the bundled fields' do
65
+ general = @dummy_form.fieldsets.fetch(:general)
66
+
67
+ expect(general.first).to be_instance_of Field::Text
68
+ expect(general.first.name).to eq :name
69
+
70
+ toys = @dummy_form.fieldsets.fetch(:toys)
71
+
72
+ expect(toys.first).to be_instance_of Field::Null
73
+ expect(toys.first.name).to eq :ball
74
+
75
+ expect(toys.last).to be_instance_of Field::Text
76
+ expect(toys.last.name).to eq :car
77
+ end
78
+
79
+ it 'fieldsets fields are the same instance of fields' do
80
+ @dummy_form.fieldsets.each do |id, fields|
81
+ fields.each do |field|
82
+ expect(field).to eq @dummy_form.field(field.name)
83
+ end
84
+ end
85
+ end
86
+
87
+ it 'is ready for validation' do
88
+ expect(@dummy_form).to_not be_valid
89
+ expect(@dummy_form.errors[:name]).to eq ["can't be blank"]
90
+ end
91
+
92
+ context 'when assigning a new attribute' do
93
+ it 'assigns the value of the attribute to the field' do
94
+ pending
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,17 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ config.run_all_when_everything_filtered = true
10
+ config.filter_run :focus
11
+
12
+ # Run specs in random order to surface order dependencies. If you find an
13
+ # order dependency and want to debug it, you can fix the order by providing
14
+ # the seed, which is printed after each run.
15
+ # --seed 1234
16
+ config.order = 'random'
17
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'super_form/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "super_form"
8
+ spec.version = SuperForm::VERSION
9
+ spec.authors = ["Thiago A. Silva"]
10
+ spec.email = ["thiagoaraujos@gmail.com"]
11
+ spec.summary = 'Object oriented forms for Rails'
12
+ spec.description = 'Super Form adopts the concept of forms and fields, making it easy to define fields containing bundled validations and attributes. Also, it aims to supply a handy form builder for these fields.'
13
+ spec.homepage = "http://github.com/thiagoa/super_form"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "sqlite3", '1.3.8'
22
+ spec.add_development_dependency 'virtus', '1.0.1'
23
+ spec.add_development_dependency 'pry-rails', '0.3.2'
24
+ spec.add_development_dependency 'rspec', '2.14.1'
25
+ spec.add_development_dependency 'simplecov', '0.8.2'
26
+ spec.add_development_dependency 'rake'
27
+ spec.add_development_dependency 'capybara', '2.2.0'
28
+ spec.add_development_dependency 'activemodel', '4.0.2'
29
+ spec.add_development_dependency 'activesupport', '4.0.2'
30
+ spec.add_development_dependency 'cpf_cnpj', '0.2.0'
31
+ spec.add_development_dependency 'validates_email_format_of', '1.5.3'
32
+ end
metadata ADDED
@@ -0,0 +1,240 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: super_form
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Thiago A. Silva
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sqlite3
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.3.8
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 1.3.8
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.1
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 1.0.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry-rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 0.3.2
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 0.3.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 2.14.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 2.14.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '='
74
+ - !ruby/object:Gem::Version
75
+ version: 0.8.2
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '='
81
+ - !ruby/object:Gem::Version
82
+ version: 0.8.2
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: capybara
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '='
102
+ - !ruby/object:Gem::Version
103
+ version: 2.2.0
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '='
109
+ - !ruby/object:Gem::Version
110
+ version: 2.2.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: activemodel
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '='
116
+ - !ruby/object:Gem::Version
117
+ version: 4.0.2
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '='
123
+ - !ruby/object:Gem::Version
124
+ version: 4.0.2
125
+ - !ruby/object:Gem::Dependency
126
+ name: activesupport
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '='
130
+ - !ruby/object:Gem::Version
131
+ version: 4.0.2
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '='
137
+ - !ruby/object:Gem::Version
138
+ version: 4.0.2
139
+ - !ruby/object:Gem::Dependency
140
+ name: cpf_cnpj
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '='
144
+ - !ruby/object:Gem::Version
145
+ version: 0.2.0
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '='
151
+ - !ruby/object:Gem::Version
152
+ version: 0.2.0
153
+ - !ruby/object:Gem::Dependency
154
+ name: validates_email_format_of
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - '='
158
+ - !ruby/object:Gem::Version
159
+ version: 1.5.3
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - '='
165
+ - !ruby/object:Gem::Version
166
+ version: 1.5.3
167
+ description: Super Form adopts the concept of forms and fields, making it easy to
168
+ define fields containing bundled validations and attributes. Also, it aims to supply
169
+ a handy form builder for these fields.
170
+ email:
171
+ - thiagoaraujos@gmail.com
172
+ executables:
173
+ - autospec
174
+ - rspec
175
+ extensions: []
176
+ extra_rdoc_files: []
177
+ files:
178
+ - ".gitignore"
179
+ - ".rspec"
180
+ - Gemfile
181
+ - LICENSE.txt
182
+ - README.md
183
+ - Rakefile
184
+ - bin/autospec
185
+ - bin/rspec
186
+ - lib/attribute.rb
187
+ - lib/attribute/cnpj.rb
188
+ - lib/attribute/cpf.rb
189
+ - lib/attribute/person_type.rb
190
+ - lib/attribute/telephone.rb
191
+ - lib/cnpj_validator.rb
192
+ - lib/cpf_validator.rb
193
+ - lib/field.rb
194
+ - lib/field/base.rb
195
+ - lib/field/cnpj.rb
196
+ - lib/field/cpf.rb
197
+ - lib/field/email.rb
198
+ - lib/field/error.rb
199
+ - lib/field/password.rb
200
+ - lib/field/person_type.rb
201
+ - lib/field/telephone.rb
202
+ - lib/field/text.rb
203
+ - lib/person_type.rb
204
+ - lib/super_form.rb
205
+ - lib/super_form/version.rb
206
+ - lib/tasks/.keep
207
+ - lib/telephone.rb
208
+ - lib/telephone_validator.rb
209
+ - lib/uniqueness_validator.rb
210
+ - spec/lib/super_form_spec.rb
211
+ - spec/spec_helper.rb
212
+ - super_form.gemspec
213
+ homepage: http://github.com/thiagoa/super_form
214
+ licenses:
215
+ - MIT
216
+ metadata: {}
217
+ post_install_message:
218
+ rdoc_options: []
219
+ require_paths:
220
+ - lib
221
+ required_ruby_version: !ruby/object:Gem::Requirement
222
+ requirements:
223
+ - - ">="
224
+ - !ruby/object:Gem::Version
225
+ version: '0'
226
+ required_rubygems_version: !ruby/object:Gem::Requirement
227
+ requirements:
228
+ - - ">="
229
+ - !ruby/object:Gem::Version
230
+ version: '0'
231
+ requirements: []
232
+ rubyforge_project:
233
+ rubygems_version: 2.2.0
234
+ signing_key:
235
+ specification_version: 4
236
+ summary: Object oriented forms for Rails
237
+ test_files:
238
+ - spec/lib/super_form_spec.rb
239
+ - spec/spec_helper.rb
240
+ has_rdoc: