reform-rails 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5853d1ea7ab67db2086f690ebc9d6f504d7f08a3
4
- data.tar.gz: c0e2c9c9889231f1a1165d6db1b5f55649824b11
3
+ metadata.gz: 508979fd5b00727656341a9e9f917d85c4510000
4
+ data.tar.gz: 9bfa49a04a089b30d05022211a5b7b8bbaff8706
5
5
  SHA512:
6
- metadata.gz: d5f107040f7c09367c1e7163c77c5173e066fa3dff6c19178f758d91fdadbcb211ab055608c57011f9dd9e92cdecea12ef240c74136532c0c2c54f11cc4be0d8
7
- data.tar.gz: 69aac408d0927681f4224e994c998d7bb0f0c859d8f2bdbf44033f6701cae7811f97708219c397381156afe1b860f57055b5cc7a3e075b5cc1900499848542c4
6
+ metadata.gz: 2382c4784a2d6e7072b1ca796aaf44099cb3d2cf29dfa42e963db73216fcb9cf025aa09a1cf15a5e3e373244c29de2685e3f3262998ddf0e7aae06ff9cf1b661
7
+ data.tar.gz: f08da6791134be71db3891e05629d639ab61550cb3074478e10970b7ba2b18b70593d5a372c825e343c039eca61ba74935d822465ddd3172715895bc2966202a
data/.gitignore CHANGED
@@ -7,3 +7,5 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ *.gem
11
+ test/dummy/log/*.log
@@ -1,4 +1,17 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.2.2
4
- before_install: gem install bundler -v 1.10.5
3
+ - 2.2.3
4
+ - 2.0.0
5
+ services:
6
+ - mongodb
7
+ gemfile:
8
+ - gemfiles/Gemfile.rails-4.2
9
+ - gemfiles/Gemfile.rails-4.1
10
+ - gemfiles/Gemfile.rails-4.0
11
+ - gemfiles/Gemfile.rails-3.2
12
+ - gemfiles/Gemfile.rails-3.1
13
+ # - gemfiles/Gemfile.rails-3.0
14
+ matrix:
15
+ fast_finish: true
16
+ before_install:
17
+ - gem install bundler
@@ -0,0 +1,3 @@
1
+ # 0.1.1
2
+
3
+ * First working release.
data/Gemfile CHANGED
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in reform-rails.gemspec
4
4
  gemspec
5
+
6
+ gem "reform", path: "../reform"
data/README.md CHANGED
@@ -1,8 +1,16 @@
1
1
  # Reform::Rails
2
2
 
3
+ [![Gitter Chat](https://badges.gitter.im/trailblazer/chat.svg)](https://gitter.im/trailblazer/chat)
4
+ [![TRB Newsletter](https://img.shields.io/badge/TRB-newsletter-lightgrey.svg)](http://trailblazer.to/newsletter/)
5
+ [![Build
6
+ Status](https://travis-ci.org/apotonick/reform-rails.svg)](https://travis-ci.org/apotonick/reform-rails)
7
+ [![Gem Version](https://badge.fury.io/rb/reform-rails.svg)](http://badge.fury.io/rb/reform-rails)
8
+
9
+ _Rails-support for Reform_.
10
+
3
11
  Loads Rails-specific Reform files and includes modules like `Reform::Form::ActiveModel` automatically.
4
12
 
5
- Simply don't include this gem if you don't want to use the standard Reform/Rails stack, for example because you're in a Lotus environment or you intend to use Veto validations.
13
+ Simply don't include this gem if you don't want to use the conventional Reform/Rails stack. For example in a Hanami environment or when using dry-validations, refrain from using this gem.
6
14
 
7
15
  ## Installation
8
16
 
@@ -12,6 +20,8 @@ Add this line to your application's Gemfile:
12
20
  gem 'reform-rails'
13
21
  ```
14
22
 
23
+ Reform-rails needs Reform >= 2.2.
24
+
15
25
  ## License
16
26
 
17
27
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
Binary file
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in reform.gemspec
4
+ gemspec :path => '../'
5
+
6
+ gem 'railties', '~> 3.1.0'
7
+ gem 'activerecord', '~> 3.1.0'
@@ -0,0 +1,153 @@
1
+ PATH
2
+ remote: ../
3
+ specs:
4
+ reform (2.1.0.rc1)
5
+ disposable (>= 0.2.0.rc1, < 0.3.0)
6
+ representable (>= 2.4.0.rc1, < 2.5.0)
7
+ uber (~> 0.0.11)
8
+
9
+ PATH
10
+ remote: ../../declarative
11
+ specs:
12
+ declarative (0.0.4)
13
+ uber (>= 0.0.15)
14
+
15
+ GEM
16
+ remote: http://rubygems.org/
17
+ specs:
18
+ actionmailer (3.1.12)
19
+ actionpack (= 3.1.12)
20
+ mail (~> 2.4.4)
21
+ actionpack (3.1.12)
22
+ activemodel (= 3.1.12)
23
+ activesupport (= 3.1.12)
24
+ builder (~> 3.0.0)
25
+ erubis (~> 2.7.0)
26
+ i18n (~> 0.6)
27
+ rack (~> 1.3.6)
28
+ rack-cache (~> 1.2)
29
+ rack-mount (~> 0.8.2)
30
+ rack-test (~> 0.6.1)
31
+ sprockets (~> 2.0.4)
32
+ activemodel (3.1.12)
33
+ activesupport (= 3.1.12)
34
+ builder (~> 3.0.0)
35
+ i18n (~> 0.6)
36
+ activerecord (3.1.12)
37
+ activemodel (= 3.1.12)
38
+ activesupport (= 3.1.12)
39
+ arel (~> 2.2.3)
40
+ tzinfo (~> 0.3.29)
41
+ activeresource (3.1.12)
42
+ activemodel (= 3.1.12)
43
+ activesupport (= 3.1.12)
44
+ activesupport (3.1.12)
45
+ multi_json (~> 1.0)
46
+ arel (2.2.3)
47
+ axiom-types (0.1.1)
48
+ descendants_tracker (~> 0.0.4)
49
+ ice_nine (~> 0.11.0)
50
+ thread_safe (~> 0.3, >= 0.3.1)
51
+ builder (3.0.4)
52
+ coercible (1.0.0)
53
+ descendants_tracker (~> 0.0.1)
54
+ descendants_tracker (0.0.4)
55
+ thread_safe (~> 0.3, >= 0.3.1)
56
+ disposable (0.2.0.rc1)
57
+ declarative (~> 0.0.4)
58
+ representable (>= 2.4.0.rc1, < 2.5.0)
59
+ uber
60
+ equalizer (0.0.11)
61
+ erubis (2.7.0)
62
+ hike (1.2.3)
63
+ i18n (0.7.0)
64
+ ice_nine (0.11.1)
65
+ json (1.8.3)
66
+ lotus-utils (0.5.2)
67
+ lotus-validations (0.3.3)
68
+ lotus-utils (~> 0.5)
69
+ mail (2.4.4)
70
+ i18n (>= 0.4.0)
71
+ mime-types (~> 1.16)
72
+ treetop (~> 1.4.8)
73
+ mime-types (1.25.1)
74
+ minitest (5.8.2)
75
+ mongoid (3.0.23)
76
+ activemodel (~> 3.1)
77
+ moped (~> 1.2)
78
+ origin (~> 1.0)
79
+ tzinfo (~> 0.3.22)
80
+ moped (1.5.3)
81
+ multi_json (1.11.2)
82
+ origin (1.1.0)
83
+ polyglot (0.3.5)
84
+ rack (1.3.10)
85
+ rack-cache (1.5.1)
86
+ rack (>= 0.4)
87
+ rack-mount (0.8.3)
88
+ rack (>= 1.0.0)
89
+ rack-ssl (1.3.4)
90
+ rack
91
+ rack-test (0.6.3)
92
+ rack (>= 1.0)
93
+ rails (3.1.12)
94
+ actionmailer (= 3.1.12)
95
+ actionpack (= 3.1.12)
96
+ activerecord (= 3.1.12)
97
+ activeresource (= 3.1.12)
98
+ activesupport (= 3.1.12)
99
+ bundler (~> 1.0)
100
+ railties (= 3.1.12)
101
+ railties (3.1.12)
102
+ actionpack (= 3.1.12)
103
+ activesupport (= 3.1.12)
104
+ rack-ssl (~> 1.3.2)
105
+ rake (>= 0.8.7)
106
+ rdoc (~> 3.4)
107
+ thor (~> 0.14.6)
108
+ rake (10.4.2)
109
+ rdoc (3.12.2)
110
+ json (~> 1.4)
111
+ representable (2.4.0.rc1)
112
+ declarative (~> 0.0.4)
113
+ uber (~> 0.0.15)
114
+ sprockets (2.0.5)
115
+ hike (~> 1.2)
116
+ rack (~> 1.0)
117
+ tilt (~> 1.1, != 1.3.0)
118
+ sqlite3 (1.3.11)
119
+ thor (0.14.6)
120
+ thread_safe (0.3.5)
121
+ tilt (1.4.1)
122
+ treetop (1.4.15)
123
+ polyglot
124
+ polyglot (>= 0.3.1)
125
+ tzinfo (0.3.45)
126
+ uber (0.0.15)
127
+ virtus (1.0.5)
128
+ axiom-types (~> 0.1)
129
+ coercible (~> 1.0)
130
+ descendants_tracker (~> 0.0, >= 0.0.3)
131
+ equalizer (~> 0.0, >= 0.0.9)
132
+
133
+ PLATFORMS
134
+ ruby
135
+
136
+ DEPENDENCIES
137
+ actionpack
138
+ activerecord (~> 3.1.0)
139
+ bundler
140
+ declarative!
141
+ lotus-validations
142
+ minitest
143
+ mongoid
144
+ multi_json
145
+ rails
146
+ railties (~> 3.1.0)
147
+ rake
148
+ reform!
149
+ sqlite3
150
+ virtus
151
+
152
+ BUNDLED WITH
153
+ 1.10.6
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in reform.gemspec
4
+ gemspec :path => '../'
5
+
6
+ gem 'railties', '~> 3.2.0'
7
+ gem 'activerecord', '~> 3.2.0'
@@ -0,0 +1,8 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in reform.gemspec
4
+ gemspec :path => '../'
5
+
6
+ gem 'railties', '~> 4.0.0'
7
+ gem 'activerecord', '~> 4.0.0'
8
+ gem 'minitest', '~> 4.2'
@@ -0,0 +1,8 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in reform.gemspec
4
+ gemspec :path => '../'
5
+
6
+ gem 'railties', '~> 4.1.0'
7
+ gem 'activerecord', '~> 4.1.0'
8
+ gem 'minitest'
@@ -0,0 +1,4 @@
1
+ require "reform/form/active_model"
2
+ require "reform/form/orm"
3
+ require "reform/form/active_record"
4
+ require "reform/form/active_model/model_reflections" # only load this in AR context as simple_form currently is bound to AR.
@@ -0,0 +1,87 @@
1
+ require "reform/form/active_model/model_validations"
2
+ require "reform/form/active_model/form_builder_methods"
3
+ require "uber/delegates"
4
+
5
+ module Reform::Form::ActiveModel
6
+ def self.included(base)
7
+ base.class_eval do
8
+ extend ClassMethods
9
+ register_feature ActiveModel
10
+
11
+ extend Uber::Delegates
12
+ delegates :model, *[:persisted?, :to_key, :to_param, :id] # Uber::Delegates
13
+
14
+ def to_model # this is called somewhere in FormBuilder and ActionController.
15
+ self
16
+ end
17
+ end
18
+ end
19
+
20
+
21
+ module ClassMethods
22
+ # this module is only meant to extend (not include). # DISCUSS: is this a sustainable concept?
23
+ def self.extended(base)
24
+ base.class_eval do
25
+ extend Uber::InheritableAttribute
26
+ inheritable_attr :model_options
27
+ end
28
+ end
29
+
30
+ # DISCUSS: can we achieve that somehow via features in build_inline?
31
+ def property(*)
32
+ super.tap do |dfn|
33
+ return dfn unless dfn[:nested]
34
+ _name = dfn[:name]
35
+ dfn[:nested].instance_eval do
36
+ @_name = _name.singularize.camelize
37
+ # this adds Form::name for AM::Validations and I18N.
38
+ def name
39
+ @_name
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+
46
+ # Set a model name for this form if the infered is wrong.
47
+ #
48
+ # class CoverSongForm < Reform::Form
49
+ # model :song
50
+ #
51
+ # or we can setup a isolated namespace model ( which defined in isolated rails egine )
52
+ #
53
+ # class CoverSongForm < Reform::Form
54
+ # model "api/v1/song", namespace: "api"
55
+ def model(main_model, options={})
56
+ self.model_options = [main_model, options]
57
+ end
58
+
59
+ def model_name
60
+ if model_options
61
+ form_name = model_options.first.to_s.camelize
62
+ namespace = model_options.last[:namespace].present? ? model_options.last[:namespace].to_s.camelize.constantize : nil
63
+ else
64
+ if name
65
+ form_name = name.sub(/(::)?Form$/, "") # Song::Form => "Song"
66
+ namespace = nil
67
+ else # anonymous forms. let's drop AM and forget about all this.
68
+ form_name = "reform"
69
+ namespace = nil
70
+ end
71
+ end
72
+
73
+ active_model_name_for(form_name, namespace)
74
+ end
75
+
76
+ private
77
+ def active_model_name_for(string, namespace=nil)
78
+ return ::ActiveModel::Name.new(OpenStruct.new(:name => string)) if Reform.rails3_0?
79
+ ::ActiveModel::Name.new(self, namespace, string)
80
+ end
81
+ end # ClassMethods
82
+
83
+
84
+ def model_name(*args)
85
+ self.class.model_name(*args)
86
+ end
87
+ end
@@ -0,0 +1,48 @@
1
+ module Reform::Form::ActiveModel
2
+ # Including FormBuilderMethods will allow using form instances with form_for, simple_form, etc.
3
+ # in Rails. It will further try to translate Rails' suboptimal songs_attributes weirdness
4
+ # back to normal `songs: ` naming in +#valiate+.
5
+ module FormBuilderMethods
6
+ def self.included(base)
7
+ base.extend ClassMethods # ::model_name
8
+ end
9
+
10
+ module ClassMethods
11
+ private
12
+
13
+ # TODO: add that shit in Form#present, not by overriding ::property.
14
+ def property(name, options={}, &block)
15
+ super.tap do |definition|
16
+ add_nested_attribute_compat(name) if definition[:nested] # TODO: fix that in Rails FB#1832 work.
17
+ end
18
+ end
19
+
20
+ # The Rails FormBuilder "detects" nested attributes (which is what we want) by checking existance of a setter method.
21
+ def add_nested_attribute_compat(name)
22
+ define_method("#{name}_attributes=") {} # this is why i hate respond_to? in Rails.
23
+ end
24
+ end
25
+
26
+ # Modify the incoming Rails params hash to be representable compliant.
27
+ def deserialize!(params)
28
+ # this only happens in a Hash environment. other engines have to overwrite this method.
29
+ schema.each do |dfn|
30
+ rename_nested_param_for!(params, dfn)
31
+ end
32
+
33
+ super(params)
34
+ end
35
+
36
+ private
37
+ def rename_nested_param_for!(params, dfn)
38
+ name = dfn[:name]
39
+ nested_name = "#{name}_attributes"
40
+ return unless params.has_key?(nested_name)
41
+
42
+ value = params["#{name}_attributes"]
43
+ value = value.values if dfn[:collection]
44
+
45
+ params[name] = value
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,46 @@
1
+ # ModelReflections will be the interface between the form object and form builders like simple_form.
2
+ #
3
+ # This module is meant to collect all dependencies simple_form needs in addition to the ActiveModel ones.
4
+ # Goal is to collect all methods and define a reflection API so simple_form works with all ORMs and Reform
5
+ # doesn't have to "guess" what simple_form and other form helpers need.
6
+ class Reform::Form < Reform::Contract
7
+ module ActiveModel::ModelReflections
8
+ def self.included(base)
9
+ base.extend ClassMethods
10
+ base.send :register_feature, self # makes it work in nested forms.
11
+ end
12
+
13
+ module ClassMethods
14
+ # Delegate reflect_on_association to the model class to support simple_form's
15
+ # association input.
16
+ def reflect_on_association(*args)
17
+ model_name.to_s.constantize.reflect_on_association(*args)
18
+ end
19
+
20
+ # this is needed in simpleform to infer required fields.
21
+ def validators_on(*args)
22
+ validation_groups.collect { |k, group| group.instance_variable_get(:@validations).validators_on(*args) }.flatten
23
+ end
24
+ end
25
+
26
+ # Delegate column for attribute to the model to support simple_form's
27
+ # attribute type interrogation.
28
+ def column_for_attribute(name)
29
+ model_for_property(name).column_for_attribute(name)
30
+ end
31
+
32
+ def has_attribute?(name)
33
+ model_for_property(name).has_attribute?(name)
34
+ end
35
+
36
+ def defined_enums
37
+ return model.defined_enums unless is_a?(Reform::Form::Composition)
38
+
39
+ mapper.each.with_object({}) { |m,h| h.merge! m.defined_enums }
40
+ end
41
+
42
+ # this should also contain to_param and friends as this is used by the form helpers.
43
+ end
44
+
45
+ ModelReflections = ActiveModel::ModelReflections
46
+ end
@@ -0,0 +1,110 @@
1
+ module Reform::Form::ActiveModel
2
+ module ModelValidations
3
+ # TODO: extract Composition behaviour.
4
+ # reduce code in Mapping.
5
+
6
+ class ValidationCopier
7
+
8
+ def self.copy(form_class, mapping, models)
9
+ if models.is_a?(Hash)
10
+ models.each do |model_name, model|
11
+ new(form_class, mapping, model, model_name).copy
12
+ end
13
+ else
14
+ new(form_class, mapping, models).copy
15
+ end
16
+ end
17
+
18
+ def initialize(form_class, mapping, model, model_name=nil)
19
+ @form_class = form_class
20
+ @mapping = mapping
21
+ @model = model
22
+ @model_name = model_name
23
+ end
24
+
25
+ def copy
26
+ @model.validators.each(&method(:add_validator))
27
+ end
28
+
29
+ private
30
+
31
+ def add_validator(validator)
32
+ if validator.respond_to?(:attributes)
33
+ add_native_validator validator
34
+ else
35
+ add_custom_validator validator
36
+ end
37
+ end
38
+
39
+ def add_native_validator validator
40
+ attributes = inverse_map_attributes(validator.attributes)
41
+ if attributes.any?
42
+ @form_class.validates(*attributes, {validator.kind => validator.options})
43
+ end
44
+ end
45
+
46
+ def add_custom_validator validator
47
+ @form_class.validates(nil, {validator.kind => validator.options})
48
+ end
49
+
50
+ def inverse_map_attributes(attributes)
51
+ @mapping.inverse_image(create_attributes(attributes))
52
+ end
53
+
54
+ def create_attributes(attributes)
55
+ attributes.map do |attribute|
56
+ [@model_name, attribute].compact
57
+ end
58
+ end
59
+
60
+ end
61
+
62
+ class Mapping
63
+ def self.from_representable_attrs(attrs)
64
+ new.tap do |mapping|
65
+ attrs.each do |dfn|
66
+ from = dfn[:name].to_sym
67
+ to = [dfn[:on], (dfn[:private_name] || dfn[:name])].compact.map(&:to_sym)
68
+ mapping.add(from, to)
69
+ end
70
+ end
71
+ end
72
+
73
+ def initialize
74
+ @forward_map = {}
75
+ @inverse_map = {}
76
+ end
77
+
78
+ # from is a symbol attribute
79
+ # to is an 1 or 2 element array, depending on whether the attribute is 'namespaced', as it is with composite forms.
80
+ # eg, add(:phone_number, [:person, :phone])
81
+ def add(from, to)
82
+ raise 'Mapping is not one-to-one' if @forward_map.has_key?(from) || @inverse_map.has_key?(to)
83
+ @forward_map[from] = to
84
+ @inverse_map[to] = from
85
+ end
86
+
87
+ def forward_image(attrs)
88
+ @forward_map.values_at(*attrs).compact
89
+ end
90
+
91
+ def forward(attr)
92
+ @forward_map[attr]
93
+ end
94
+
95
+ def inverse_image(attrs)
96
+ @inverse_map.values_at(*attrs).compact
97
+ end
98
+
99
+ def inverse(attr)
100
+ @inverse_map[attr]
101
+ end
102
+
103
+ end
104
+
105
+ def copy_validations_from(models)
106
+ ValidationCopier.copy(self, Mapping.from_representable_attrs(definitions), models)
107
+ end
108
+
109
+ end
110
+ end
@@ -0,0 +1,107 @@
1
+ require "active_model"
2
+ require "uber/delegates"
3
+
4
+ module Reform::Form::ActiveModel
5
+ # AM::Validations for your form.
6
+ # Provides ::validates, ::validate, #validate, and #valid?.
7
+ #
8
+ # Most of this file contains unnecessary wiring to make ActiveModel's error message magic work.
9
+ # Since Rails still thinks it's a good idea to do things like object.class.human_attribute_name,
10
+ # we have some hacks in here to provide that. If it doesn't work for you, don't blame us.
11
+ module Validations
12
+ def self.included(includer)
13
+ includer.instance_eval do
14
+ include Reform::Form::ActiveModel
15
+
16
+ class << self
17
+ extend Uber::Delegates
18
+ # # Hooray! Delegate translation back to Reform's Validator class which contains AM::Validations.
19
+ delegates :active_model_really_sucks, :human_attribute_name, :lookup_ancestors, :i18n_scope # Rails 3.1.
20
+
21
+ def validation_group_class
22
+ Group
23
+ end
24
+
25
+ # this is to allow calls like Form::human_attribute_name (note that this is on the CLASS level) to be resolved.
26
+ # those calls happen when adding errors in a custom validation method, which is defined on the form (as an instance method).
27
+ def active_model_really_sucks
28
+ Class.new(Validator).tap do |v|
29
+ v.model_name = model_name
30
+ end
31
+ end
32
+ end
33
+ end # ::included
34
+ end
35
+
36
+ def build_errors
37
+ Errors.new(self)
38
+ end
39
+
40
+ # The concept of "composition" has still not arrived in Rails core and they rely on 400 methods being
41
+ # available in one object. This is why we need to provide parts of the I18N API in the form.
42
+ def read_attribute_for_validation(name)
43
+ send(name)
44
+ end
45
+
46
+ class Group
47
+ def initialize
48
+ @validations = Class.new(Reform::Form::ActiveModel::Validations::Validator)
49
+ end
50
+
51
+ extend Uber::Delegates
52
+ delegates :@validations, :validates, :validate, :validates_with, :validate_with
53
+
54
+ def call(fields, errors, form) # FIXME.
55
+ validator = @validations.new(form)
56
+ validator.valid?
57
+
58
+ validator.errors.each do |name, error| # TODO: handle with proper merge, or something. validator.errors is ALWAYS AM::Errors.
59
+ errors.add(name, error)
60
+ end
61
+ end
62
+ end
63
+
64
+
65
+ # Validator is the validatable object. On the class level, we define validations,
66
+ # on instance, it exposes #valid?.
67
+ require "delegate"
68
+ class Validator < SimpleDelegator
69
+ # current i18n scope: :activemodel.
70
+ include ActiveModel::Validations
71
+
72
+ class << self
73
+ def model_name
74
+ @_active_model_sucks ||= ActiveModel::Name.new(Reform::Form, nil, "Reform::Form")
75
+ end
76
+
77
+ def model_name=(name)
78
+ @_active_model_sucks = name
79
+ end
80
+
81
+ def validates(*args, &block)
82
+ super(*Declarative::DeepDup.(args), &block)
83
+ end
84
+
85
+ # Prevent AM:V from mutating the validator class
86
+ def attr_reader(*)
87
+ end
88
+
89
+ def attr_writer(*)
90
+ end
91
+ end
92
+
93
+ def initialize(form)
94
+ super(form)
95
+ self.class.model_name = form.model_name # one of the many reasons why i will drop support for AM::V in 2.1. or maybe a bit later.
96
+ end
97
+
98
+ def method_missing(m, *args, &block)
99
+ __getobj__.send(m, *args, &block) # send all methods to the form, even privates.
100
+ end
101
+ end
102
+ end
103
+
104
+ class Errors < ActiveModel::Errors
105
+ include Reform::Form::Errors::Merge
106
+ end
107
+ end
@@ -0,0 +1,30 @@
1
+ require "reform/form/orm"
2
+
3
+ module Reform::Form::ActiveRecord
4
+ def self.included(base)
5
+ base.class_eval do
6
+ register_feature Reform::Form::ActiveRecord
7
+ include Reform::Form::ActiveModel
8
+ include Reform::Form::ORM
9
+ extend ClassMethods
10
+ end
11
+ end
12
+
13
+ module ClassMethods
14
+ def validates_uniqueness_of(attribute, options={})
15
+ options = options.merge(:attributes => [attribute])
16
+ validates_with(UniquenessValidator, options)
17
+ end
18
+ def i18n_scope
19
+ :activerecord
20
+ end
21
+ end
22
+
23
+ def to_nested_hash(*)
24
+ super.with_indifferent_access
25
+ end
26
+
27
+ class UniquenessValidator < ::ActiveRecord::Validations::UniquenessValidator
28
+ include Reform::Form::ORM::UniquenessValidator
29
+ end
30
+ end
@@ -0,0 +1,48 @@
1
+ module Reform::Form::MultiParameterAttributes
2
+ # TODO: implement this with parse_filter, so we don't have to manually walk through the hash, etc.
3
+ class DateTimeParamsFilter
4
+ def call(params)
5
+ params = params.dup # DISCUSS: not sure if that slows down form processing?
6
+ date_attributes = {}
7
+
8
+ params.each do |attribute, value|
9
+ if value.is_a?(Hash)
10
+ params[attribute] = call(value) # TODO: #validate should only handle local form params.
11
+ elsif matches = attribute.match(/^(\w+)\(.i\)$/)
12
+ date_attribute = matches[1]
13
+ date_attributes[date_attribute] = params_to_date(
14
+ params.delete("#{date_attribute}(1i)"),
15
+ params.delete("#{date_attribute}(2i)"),
16
+ params.delete("#{date_attribute}(3i)"),
17
+ params.delete("#{date_attribute}(4i)"),
18
+ params.delete("#{date_attribute}(5i)")
19
+ )
20
+ end
21
+ end
22
+ params.merge!(date_attributes)
23
+ end
24
+
25
+ private
26
+ def params_to_date(year, month, day, hour, minute)
27
+ date_fields = [year, month, day].map!(&:to_i)
28
+ time_fields = [hour, minute].map!(&:to_i)
29
+
30
+ if date_fields.any?(&:zero?) || !Date.valid_date?(*date_fields)
31
+ return nil
32
+ end
33
+
34
+ if hour.blank? && minute.blank?
35
+ Date.new(*date_fields)
36
+ else
37
+ args = date_fields + time_fields
38
+ Time.zone ? Time.zone.local(*args) :
39
+ Time.new(*args)
40
+ end
41
+ end
42
+ end
43
+
44
+ # this hooks into the format-specific #deserialize! method.
45
+ def deserialize!(params)
46
+ super DateTimeParamsFilter.new.call(params) # if params.is_a?(Hash) # this currently works for hash, only.
47
+ end
48
+ end
@@ -0,0 +1,54 @@
1
+ # === Unique Validation
2
+ # Reform's own implementation for uniqueness which does not write to model
3
+ #
4
+ # == Usage
5
+ # Pass a true boolean value to validate a field against all values available in
6
+ # the database:
7
+ # validates :title, unique: true
8
+ #
9
+ # == Options
10
+ # = Scope
11
+ # A scope can be use to filter the records that need to be compare with the
12
+ # current value to validate. A scope array can have one to many fields define.
13
+ #
14
+ # A scope can be define the following ways:
15
+ # validates :title, unique: { scope: :album_id }
16
+ # validates :title, unique: { scope: [:album_id] }
17
+ # validates :title, unique: { scope: [:album_id, ...] }
18
+ #
19
+ # All fields included in a scope must be declared as a property like this:
20
+ # property :album_id
21
+ # validates :title, unique: { scope: :album_id }
22
+ #
23
+ # Just remove write access to the property if the field must not be change:
24
+ # property :album_id, writeable: false
25
+ # validates :title, unique: { scope: :album_id }
26
+ #
27
+ # This use case is useful if album_id is set to a Song this way:
28
+ # song = album.songs.new
29
+ # album_id is automatically set and can't be change by the operation
30
+
31
+ class Reform::Form::UniqueValidator < ActiveModel::EachValidator
32
+ def validate_each(form, attribute, value)
33
+ model = form.model_for_property(attribute)
34
+
35
+ # search for models with attribute equals to form field value
36
+ query = model.class.where(attribute => value)
37
+
38
+ # apply scope if options has been declared
39
+ Array(options[:scope]).each do |field|
40
+ # add condition to only check unique value with the same scope
41
+ query = query.where(field => form.send(field))
42
+ end
43
+
44
+ # if model persisted, query may return 0 or 1 rows, else 0
45
+ allow_count = model.persisted? ? 1 : 0
46
+ form.errors.add(attribute, :taken) if query.count > allow_count
47
+ end
48
+ end
49
+
50
+ # FIXME: ActiveModel loads validators via const_get(#{name}Validator). This magic forces us to
51
+ # make the new :unique validator available here.
52
+ Reform::Form::ActiveModel::Validations::Validator.class_eval do
53
+ UniqueValidator = Reform::Form::UniqueValidator
54
+ end
@@ -1,7 +1,10 @@
1
1
  require "reform/rails/version"
2
2
 
3
+ require "reform"
3
4
  require "reform/form/active_model"
4
5
  require "reform/form/active_model/validations"
6
+ require "reform/form/multi_parameter_attributes"
7
+
5
8
 
6
9
  require "reform/active_record" if defined?(ActiveRecord)
7
10
  require "reform/mongoid" if defined?(Mongoid)
@@ -12,4 +15,10 @@ Reform::Form.class_eval do
12
15
  include Reform::Form::ActiveRecord if defined?(ActiveRecord)
13
16
  include Reform::Form::Mongoid if defined?(Mongoid)
14
17
  include Reform::Form::ActiveModel::Validations
15
- end
18
+ end
19
+
20
+ module Reform
21
+ def self.rails3_0?
22
+ ::ActiveModel::VERSION::MAJOR == 3 and ::ActiveModel::VERSION::MINOR == 0
23
+ end
24
+ end
@@ -1,5 +1,5 @@
1
1
  module Reform
2
2
  module Rails
3
- VERSION = "0.1.0"
3
+ VERSION = "0.1.1"
4
4
  end
5
5
  end
@@ -11,14 +11,22 @@ Gem::Specification.new do |spec|
11
11
 
12
12
  spec.summary = %q{Automatically load and include all common Rails form features.}
13
13
  spec.description = %q{Automatically load and include all common Reform features for a standard Rails environment.}
14
- spec.homepage = "http://trailblazerb.org/gems/reform/rails.html"
14
+ spec.homepage = "https://github.com/trailblazer/reform-rails"
15
15
  spec.license = "MIT"
16
16
 
17
17
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
18
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ["lib"]
20
20
 
21
+ spec.add_dependency "reform", ">= 2.2.0"
22
+ spec.add_dependency "activemodel", ">= 3.2"
23
+
24
+ spec.add_development_dependency "rails"
21
25
  spec.add_development_dependency "bundler", "~> 1.10"
22
26
  spec.add_development_dependency "rake", "~> 10.0"
23
27
  spec.add_development_dependency "minitest"
28
+ spec.add_development_dependency "actionpack"
29
+ spec.add_development_dependency "activerecord"
30
+ spec.add_development_dependency "mongoid"
31
+ spec.add_development_dependency "sqlite3"
24
32
  end
metadata CHANGED
@@ -1,15 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reform-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sutterer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-15 00:00:00.000000000 Z
11
+ date: 2016-06-17 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: reform
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 2.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 2.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: activemodel
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '3.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '3.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
13
55
  - !ruby/object:Gem::Dependency
14
56
  name: bundler
15
57
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +94,62 @@ dependencies:
52
94
  - - ">="
53
95
  - !ruby/object:Gem::Version
54
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: actionpack
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: activerecord
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: mongoid
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: sqlite3
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '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'
55
153
  description: Automatically load and include all common Reform features for a standard
56
154
  Rails environment.
57
155
  email:
@@ -62,14 +160,30 @@ extra_rdoc_files: []
62
160
  files:
63
161
  - ".gitignore"
64
162
  - ".travis.yml"
163
+ - CHANGES.md
65
164
  - Gemfile
66
165
  - LICENSE.txt
67
166
  - README.md
68
167
  - Rakefile
168
+ - database.sqlite3
169
+ - gemfiles/Gemfile.rails-3.1
170
+ - gemfiles/Gemfile.rails-3.1.lock
171
+ - gemfiles/Gemfile.rails-3.2
172
+ - gemfiles/Gemfile.rails-4.0
173
+ - gemfiles/Gemfile.rails-4.1
174
+ - lib/reform/active_record.rb
175
+ - lib/reform/form/active_model.rb
176
+ - lib/reform/form/active_model/form_builder_methods.rb
177
+ - lib/reform/form/active_model/model_reflections.rb
178
+ - lib/reform/form/active_model/model_validations.rb
179
+ - lib/reform/form/active_model/validations.rb
180
+ - lib/reform/form/active_record.rb
181
+ - lib/reform/form/multi_parameter_attributes.rb
182
+ - lib/reform/form/validation/unique_validator.rb
69
183
  - lib/reform/rails.rb
70
184
  - lib/reform/rails/version.rb
71
185
  - reform-rails.gemspec
72
- homepage: http://trailblazerb.org/gems/reform/rails.html
186
+ homepage: https://github.com/trailblazer/reform-rails
73
187
  licenses:
74
188
  - MIT
75
189
  metadata: {}
@@ -94,4 +208,3 @@ signing_key:
94
208
  specification_version: 4
95
209
  summary: Automatically load and include all common Rails form features.
96
210
  test_files: []
97
- has_rdoc: