super_form 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: