reform 0.1.2 → 0.2.0
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.
- data/.gitignore +1 -0
 - data/.travis.yml +0 -1
 - data/CHANGES.md +13 -0
 - data/Gemfile +0 -2
 - data/README.md +276 -94
 - data/Rakefile +6 -0
 - data/TODO.md +10 -1
 - data/database.sqlite3 +0 -0
 - data/lib/reform/active_record.rb +2 -0
 - data/lib/reform/composition.rb +55 -0
 - data/lib/reform/form/active_model.rb +60 -15
 - data/lib/reform/form/active_record.rb +3 -3
 - data/lib/reform/form/composition.rb +69 -0
 - data/lib/reform/form.rb +183 -80
 - data/lib/reform/rails.rb +8 -1
 - data/lib/reform/representer.rb +38 -0
 - data/lib/reform/version.rb +1 -1
 - data/lib/reform.rb +5 -2
 - data/reform.gemspec +3 -2
 - data/test/active_model_test.rb +83 -9
 - data/test/coercion_test.rb +26 -0
 - data/test/composition_test.rb +57 -0
 - data/test/dummy/Rakefile +7 -0
 - data/test/dummy/app/controllers/albums_controller.rb +18 -0
 - data/test/dummy/app/controllers/application_controller.rb +4 -0
 - data/test/dummy/app/controllers/musician_controller.rb +5 -0
 - data/test/dummy/app/forms/album_form.rb +18 -0
 - data/test/dummy/app/helpers/application_helper.rb +2 -0
 - data/test/dummy/app/models/album.rb +4 -0
 - data/test/dummy/app/models/song.rb +3 -0
 - data/test/dummy/app/views/albums/new.html.erb +28 -0
 - data/test/dummy/app/views/layouts/application.html.erb +14 -0
 - data/test/dummy/config/application.rb +20 -0
 - data/test/dummy/config/boot.rb +10 -0
 - data/test/dummy/config/database.yml +22 -0
 - data/test/dummy/config/environment.rb +5 -0
 - data/test/dummy/config/environments/development.rb +16 -0
 - data/test/dummy/config/environments/production.rb +46 -0
 - data/test/dummy/config/environments/test.rb +33 -0
 - data/test/dummy/config/locales/en.yml +5 -0
 - data/test/dummy/config/routes.rb +4 -0
 - data/test/dummy/config.ru +4 -0
 - data/test/dummy/db/test.sqlite3 +0 -0
 - data/test/dummy/log/production.log +0 -0
 - data/test/dummy/log/server.log +0 -0
 - data/test/errors_test.rb +95 -0
 - data/test/form_composition_test.rb +60 -0
 - data/test/nested_form_test.rb +129 -0
 - data/test/rails/integration_test.rb +54 -0
 - data/test/reform_test.rb +80 -114
 - data/test/test_helper.rb +14 -1
 - metadata +86 -11
 - data/lib/reform/form/dsl.rb +0 -38
 - data/test/dsl_test.rb +0 -43
 
    
        data/test/active_model_test.rb
    CHANGED
    
    | 
         @@ -1,12 +1,84 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require 'test_helper'
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            class  
     | 
| 
      
 3 
     | 
    
         
            +
            class NewActiveModelTest < MiniTest::Spec # TODO: move to test/rails/
         
     | 
| 
      
 4 
     | 
    
         
            +
              class SongForm < Reform::Form
         
     | 
| 
      
 5 
     | 
    
         
            +
                include Reform::Form::ActiveModel
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                property :name
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              let (:artist) { Artist.create(:name => "Frank Zappa") }
         
     | 
| 
      
 11 
     | 
    
         
            +
              let (:form) { SongForm.new(artist) }
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              it { form.persisted?.must_equal true }
         
     | 
| 
      
 14 
     | 
    
         
            +
              it { form.to_key.must_equal [artist.id] }
         
     | 
| 
      
 15 
     | 
    
         
            +
              it { form.to_param.must_equal "#{artist.id}" }
         
     | 
| 
      
 16 
     | 
    
         
            +
              it { form.to_model.must_equal form }
         
     | 
| 
      
 17 
     | 
    
         
            +
              it { form.id.must_equal artist.id }
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              describe "::model_name" do
         
     | 
| 
      
 20 
     | 
    
         
            +
                it { form.class.model_name.must_be_kind_of ActiveModel::Name }
         
     | 
| 
      
 21 
     | 
    
         
            +
                it { form.class.model_name.to_s.must_equal "NewActiveModelTest::Song" }
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                let (:class_with_model) {
         
     | 
| 
      
 24 
     | 
    
         
            +
                  Class.new(Reform::Form) do
         
     | 
| 
      
 25 
     | 
    
         
            +
                    include Reform::Form::ActiveModel
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                    model :album
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
                }
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                it { class_with_model.model_name.must_be_kind_of ActiveModel::Name }
         
     | 
| 
      
 32 
     | 
    
         
            +
                it { class_with_model.model_name.to_s.must_equal "Album" }
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
            end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            class FormBuilderCompatTest < MiniTest::Spec
         
     | 
| 
      
 37 
     | 
    
         
            +
              let (:form_class) {
         
     | 
| 
      
 38 
     | 
    
         
            +
                Class.new(Reform::Form) do
         
     | 
| 
      
 39 
     | 
    
         
            +
                  include Reform::Form::ActiveModel::FormBuilderMethods
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  property :artist do
         
     | 
| 
      
 42 
     | 
    
         
            +
                    property :name
         
     | 
| 
      
 43 
     | 
    
         
            +
                    validates :name, :presence => true
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                  collection :songs do
         
     | 
| 
      
 47 
     | 
    
         
            +
                    property :title
         
     | 
| 
      
 48 
     | 
    
         
            +
                    validates :title, :presence => true
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
              }
         
     | 
| 
      
 52 
     | 
    
         
            +
              let (:form) { form_class.new(OpenStruct.new(:artist => Artist.new, :songs => [OpenStruct.new])) }
         
     | 
| 
      
 53 
     | 
    
         
            +
            # TODO: test when keys are missing!
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
              it "respects _attributes params hash" do
         
     | 
| 
      
 56 
     | 
    
         
            +
                form.validate("artist_attributes" => {"name" => "Blink 182"},
         
     | 
| 
      
 57 
     | 
    
         
            +
                  "songs_attributes" => {"0" => {"title" => "Damnit"}})
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                form.artist.name.must_equal "Blink 182"
         
     | 
| 
      
 60 
     | 
    
         
            +
                form.songs.first.title.must_equal "Damnit"
         
     | 
| 
      
 61 
     | 
    
         
            +
              end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
              it "defines _attributes= setter so Rails' FB works properly" do
         
     | 
| 
      
 64 
     | 
    
         
            +
                form.must_respond_to("artist_attributes=")
         
     | 
| 
      
 65 
     | 
    
         
            +
                form.must_respond_to("songs_attributes=")
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
              it "returns flat errors hash" do
         
     | 
| 
      
 69 
     | 
    
         
            +
                form.validate("artist_attributes" => {"name" => ""},
         
     | 
| 
      
 70 
     | 
    
         
            +
                  "songs_attributes" => {"0" => {"title" => ""}})
         
     | 
| 
      
 71 
     | 
    
         
            +
                form.errors.messages.must_equal(:"artist.name" => ["can't be blank"], :"songs.title" => ["can't be blank"])
         
     | 
| 
      
 72 
     | 
    
         
            +
              end
         
     | 
| 
      
 73 
     | 
    
         
            +
            end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
            class ActiveModelWithCompositionTest < MiniTest::Spec
         
     | 
| 
       4 
76 
     | 
    
         
             
               class HitForm < Reform::Form
         
     | 
| 
       5 
     | 
    
         
            -
                include  
     | 
| 
      
 77 
     | 
    
         
            +
                include Composition
         
     | 
| 
       6 
78 
     | 
    
         
             
                include Reform::Form::ActiveModel
         
     | 
| 
       7 
79 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
                property  :title, 
     | 
| 
       9 
     | 
    
         
            -
                properties [:name, :genre], 
     | 
| 
      
 80 
     | 
    
         
            +
                property  :title,           :on => :song
         
     | 
| 
      
 81 
     | 
    
         
            +
                properties [:name, :genre], :on => :artist # we need to check both ::property and ::properties here!
         
     | 
| 
       10 
82 
     | 
    
         | 
| 
       11 
83 
     | 
    
         
             
                model :hit, :on => :song
         
     | 
| 
       12 
84 
     | 
    
         
             
              end
         
     | 
| 
         @@ -21,11 +93,11 @@ class ActiveModelTest < MiniTest::Spec 
     | 
|
| 
       21 
93 
     | 
    
         
             
                end
         
     | 
| 
       22 
94 
     | 
    
         | 
| 
       23 
95 
     | 
    
         
             
                it "doesn't delegate when :on missing" do
         
     | 
| 
       24 
     | 
    
         
            -
                  class  
     | 
| 
       25 
     | 
    
         
            -
                    include  
     | 
| 
      
 96 
     | 
    
         
            +
                  class SongOnlyForm < Reform::Form
         
     | 
| 
      
 97 
     | 
    
         
            +
                    include Composition
         
     | 
| 
       26 
98 
     | 
    
         
             
                    include Reform::Form::ActiveModel
         
     | 
| 
       27 
99 
     | 
    
         | 
| 
       28 
     | 
    
         
            -
                    property 
     | 
| 
      
 100 
     | 
    
         
            +
                    property :title,  :on => :song
         
     | 
| 
       29 
101 
     | 
    
         | 
| 
       30 
102 
     | 
    
         
             
                    model :song
         
     | 
| 
       31 
103 
     | 
    
         
             
                  end.new(:song => rio, :artist => duran).song.must_equal rio
         
     | 
| 
         @@ -34,6 +106,7 @@ class ActiveModelTest < MiniTest::Spec 
     | 
|
| 
       34 
106 
     | 
    
         | 
| 
       35 
107 
     | 
    
         | 
| 
       36 
108 
     | 
    
         
             
              it "creates composition readers" do
         
     | 
| 
      
 109 
     | 
    
         
            +
                skip "we don't want those anymore since they don't represent the form internal state!"
         
     | 
| 
       37 
110 
     | 
    
         
             
                form.song.must_equal rio
         
     | 
| 
       38 
111 
     | 
    
         
             
                form.artist.must_equal duran
         
     | 
| 
       39 
112 
     | 
    
         
             
              end
         
     | 
| 
         @@ -55,12 +128,13 @@ class ActiveModelTest < MiniTest::Spec 
     | 
|
| 
       55 
128 
     | 
    
         
             
              end
         
     | 
| 
       56 
129 
     | 
    
         | 
| 
       57 
130 
     | 
    
         
             
              it "provides #to_model" do
         
     | 
| 
       58 
     | 
    
         
            -
                HitForm.new(:song => OpenStruct.new 
     | 
| 
      
 131 
     | 
    
         
            +
                form = HitForm.new(:song => OpenStruct.new, :artist => OpenStruct.new)
         
     | 
| 
      
 132 
     | 
    
         
            +
                form.to_model.must_equal form
         
     | 
| 
       59 
133 
     | 
    
         
             
              end
         
     | 
| 
       60 
134 
     | 
    
         | 
| 
       61 
135 
     | 
    
         
             
              it "works with any order of ::model and ::property" do
         
     | 
| 
       62 
136 
     | 
    
         
             
                class AnotherForm < Reform::Form
         
     | 
| 
       63 
     | 
    
         
            -
                  include  
     | 
| 
      
 137 
     | 
    
         
            +
                  include Composition
         
     | 
| 
       64 
138 
     | 
    
         
             
                  include Reform::Form::ActiveModel
         
     | 
| 
       65 
139 
     | 
    
         | 
| 
       66 
140 
     | 
    
         
             
                  model :song, :on => :song
         
     | 
| 
         @@ -0,0 +1,26 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "test_helper"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "reform/form/coercion"
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class CoercionTest < MiniTest::Spec
         
     | 
| 
      
 5 
     | 
    
         
            +
              it "allows coercion" do
         
     | 
| 
      
 6 
     | 
    
         
            +
                form = Class.new(Reform::Form) do
         
     | 
| 
      
 7 
     | 
    
         
            +
                  include Reform::Form::Coercion
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  property :written_at, :type => DateTime
         
     | 
| 
      
 10 
     | 
    
         
            +
                end.new(OpenStruct.new(:written_at => "31/03/1981"))
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                form.written_at.must_be_kind_of DateTime
         
     | 
| 
      
 13 
     | 
    
         
            +
                form.written_at.must_equal DateTime.parse("Tue, 31 Mar 1981 00:00:00 +0000")
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              it "allows coercion in validate" do
         
     | 
| 
      
 17 
     | 
    
         
            +
                form = Class.new(Reform::Form) do
         
     | 
| 
      
 18 
     | 
    
         
            +
                  include Reform::Form::Coercion
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  property :id, :type => Integer
         
     | 
| 
      
 21 
     | 
    
         
            +
                end.new(OpenStruct.new())
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                form.validate("id" => "1")
         
     | 
| 
      
 24 
     | 
    
         
            +
                form.to_hash.must_equal("id" => 1)
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,57 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class CompositionTest < ReformSpec
         
     | 
| 
      
 2 
     | 
    
         
            +
              class SongAndArtist < Reform::Composition
         
     | 
| 
      
 3 
     | 
    
         
            +
                map({:artist => [:name], :song => [:title]}) #SongAndArtistMap.representable_attrs
         
     | 
| 
      
 4 
     | 
    
         
            +
              end
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              let (:comp) { SongAndArtist.new(:artist => @artist=OpenStruct.new, :song => rio) }
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              it "delegates to models as defined" do
         
     | 
| 
      
 9 
     | 
    
         
            +
                comp.name.must_equal nil
         
     | 
| 
      
 10 
     | 
    
         
            +
                comp.title.must_equal "Rio"
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              it "raises when non-mapped property" do
         
     | 
| 
      
 14 
     | 
    
         
            +
                assert_raises NoMethodError do
         
     | 
| 
      
 15 
     | 
    
         
            +
                  comp.raise_an_exception
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              it "creates readers to models" do
         
     | 
| 
      
 20 
     | 
    
         
            +
                comp.song.object_id.must_equal rio.object_id
         
     | 
| 
      
 21 
     | 
    
         
            +
                comp.artist.object_id.must_equal @artist.object_id
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              describe "::map_from" do
         
     | 
| 
      
 25 
     | 
    
         
            +
                it "creates the same mapping" do
         
     | 
| 
      
 26 
     | 
    
         
            +
                  comp =
         
     | 
| 
      
 27 
     | 
    
         
            +
                  Class.new(Reform::Composition) do
         
     | 
| 
      
 28 
     | 
    
         
            +
                    map_from(
         
     | 
| 
      
 29 
     | 
    
         
            +
                      Class.new(Reform::Representer) do
         
     | 
| 
      
 30 
     | 
    
         
            +
                        property :name,  :on => :artist
         
     | 
| 
      
 31 
     | 
    
         
            +
                        property :title, :on => :song
         
     | 
| 
      
 32 
     | 
    
         
            +
                      end
         
     | 
| 
      
 33 
     | 
    
         
            +
                    )
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end.
         
     | 
| 
      
 35 
     | 
    
         
            +
                  new(:artist => duran, :song => rio)
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  comp.name.must_equal "Duran Duran"
         
     | 
| 
      
 38 
     | 
    
         
            +
                  comp.title.must_equal "Rio"
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
              describe "#nested_hash_for" do
         
     | 
| 
      
 43 
     | 
    
         
            +
                it "returns nested hash" do
         
     | 
| 
      
 44 
     | 
    
         
            +
                  comp.nested_hash_for(:name => "Jimi Hendrix", :title => "Fire").must_equal({:artist=>{:name=>"Jimi Hendrix"}, :song=>{:title=>"Fire"}})
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                it "works with strings" do
         
     | 
| 
      
 48 
     | 
    
         
            +
                  comp.nested_hash_for("name" => "Jimi Hendrix", "title" => "Fire").must_equal({:artist=>{:name=>"Jimi Hendrix"}, :song=>{:title=>"Fire"}})
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                it "works with strings in map" do
         
     | 
| 
      
 52 
     | 
    
         
            +
                  Class.new(Reform::Composition) do
         
     | 
| 
      
 53 
     | 
    
         
            +
                    map(:artist => ["name"])
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end.new([nil]).nested_hash_for(:name => "Jimi Hendrix").must_equal({:artist=>{:name=>"Jimi Hendrix"}})
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
              end
         
     | 
| 
      
 57 
     | 
    
         
            +
            end
         
     | 
    
        data/test/dummy/Rakefile
    ADDED
    
    | 
         @@ -0,0 +1,7 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Add your own tasks in files placed in lib/tasks ending in .rake,
         
     | 
| 
      
 2 
     | 
    
         
            +
            # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            require File.expand_path('../config/application', __FILE__)
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'rake'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            Rails::Application.load_tasks
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class AlbumsController < ActionController::Base
         
     | 
| 
      
 2 
     | 
    
         
            +
              def new
         
     | 
| 
      
 3 
     | 
    
         
            +
                album = Album.new(:songs => [Song.new, Song.new])
         
     | 
| 
      
 4 
     | 
    
         
            +
                @form = AlbumForm.new(album)
         
     | 
| 
      
 5 
     | 
    
         
            +
              end
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              def create
         
     | 
| 
      
 8 
     | 
    
         
            +
                album = Album.new(songs: [Song.new, Song.new])
         
     | 
| 
      
 9 
     | 
    
         
            +
                @form = AlbumForm.new(album)
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                if @form.validate(params["album"])
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @form.save
         
     | 
| 
      
 13 
     | 
    
         
            +
                  redirect_to album_path(album)
         
     | 
| 
      
 14 
     | 
    
         
            +
                else
         
     | 
| 
      
 15 
     | 
    
         
            +
                  render :new
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class AlbumForm < Reform::Form # FIXME: sub forms don't inherit FBM.
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
              model :album
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              property :title
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              collection :songs do
         
     | 
| 
      
 8 
     | 
    
         
            +
                property :title
         
     | 
| 
      
 9 
     | 
    
         
            +
                validates :title, presence: true
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              validates :title, presence: true
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              def save
         
     | 
| 
      
 15 
     | 
    
         
            +
                super
         
     | 
| 
      
 16 
     | 
    
         
            +
                model.save
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,28 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            New Album
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            <%= form_for @form do |f| %>
         
     | 
| 
      
 4 
     | 
    
         
            +
              <% if @form.errors.any? %>
         
     | 
| 
      
 5 
     | 
    
         
            +
                <div>
         
     | 
| 
      
 6 
     | 
    
         
            +
                  <h3><%= pluralize(@form.errors.count, "error") %> prohibited this form from being saved:</h3>
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  <ul>
         
     | 
| 
      
 9 
     | 
    
         
            +
                    <% @form.errors.full_messages.each do |msg| %>
         
     | 
| 
      
 10 
     | 
    
         
            +
                      <li><%= msg %></li>
         
     | 
| 
      
 11 
     | 
    
         
            +
                    <% end %>
         
     | 
| 
      
 12 
     | 
    
         
            +
                  </ul>
         
     | 
| 
      
 13 
     | 
    
         
            +
                </div>
         
     | 
| 
      
 14 
     | 
    
         
            +
              <% end %>
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              <%= f.label :title, "Album Title" %>
         
     | 
| 
      
 17 
     | 
    
         
            +
              <%= f.text_field :title %>
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              <%= f.fields_for :songs do |song| %>
         
     | 
| 
      
 20 
     | 
    
         
            +
                <div>
         
     | 
| 
      
 21 
     | 
    
         
            +
                  <%= song.label :title %>
         
     | 
| 
      
 22 
     | 
    
         
            +
                  <%= song.text_field :title %>
         
     | 
| 
      
 23 
     | 
    
         
            +
                </div>
         
     | 
| 
      
 24 
     | 
    
         
            +
              <% end %>
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              <%= f.submit %>
         
     | 
| 
      
 27 
     | 
    
         
            +
            <% end %>
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.expand_path('../boot', __FILE__)
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "active_model/railtie"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "action_controller/railtie"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "action_view/railtie"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            Bundler.require
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            module Dummy
         
     | 
| 
      
 10 
     | 
    
         
            +
              class Application < Rails::Application
         
     | 
| 
      
 11 
     | 
    
         
            +
                config.encoding = "utf-8"
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                # Configure sensitive parameters which will be filtered from the log file.
         
     | 
| 
      
 14 
     | 
    
         
            +
                config.filter_parameters += [:password]
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                config.cache_store = :memory_store
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            require "reform/rails"
         
     | 
| 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # SQLite version 3.x
         
     | 
| 
      
 2 
     | 
    
         
            +
            #   gem install sqlite3-ruby (not necessary on OS X Leopard)
         
     | 
| 
      
 3 
     | 
    
         
            +
            development:
         
     | 
| 
      
 4 
     | 
    
         
            +
              adapter: sqlite3
         
     | 
| 
      
 5 
     | 
    
         
            +
              database: db/development.sqlite3
         
     | 
| 
      
 6 
     | 
    
         
            +
              pool: 5
         
     | 
| 
      
 7 
     | 
    
         
            +
              timeout: 5000
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            # Warning: The database defined as "test" will be erased and
         
     | 
| 
      
 10 
     | 
    
         
            +
            # re-generated from your development database when you run "rake".
         
     | 
| 
      
 11 
     | 
    
         
            +
            # Do not set this db to the same as development or production.
         
     | 
| 
      
 12 
     | 
    
         
            +
            test:
         
     | 
| 
      
 13 
     | 
    
         
            +
              adapter: sqlite3
         
     | 
| 
      
 14 
     | 
    
         
            +
              database: db/test.sqlite3
         
     | 
| 
      
 15 
     | 
    
         
            +
              pool: 5
         
     | 
| 
      
 16 
     | 
    
         
            +
              timeout: 5000
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            production:
         
     | 
| 
      
 19 
     | 
    
         
            +
              adapter: sqlite3
         
     | 
| 
      
 20 
     | 
    
         
            +
              database: db/production.sqlite3
         
     | 
| 
      
 21 
     | 
    
         
            +
              pool: 5
         
     | 
| 
      
 22 
     | 
    
         
            +
              timeout: 5000
         
     | 
| 
         @@ -0,0 +1,16 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            Dummy::Application.configure do
         
     | 
| 
      
 2 
     | 
    
         
            +
              # Settings specified here will take precedence over those in config/environment.rb
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
              # In the development environment your application's code is reloaded on
         
     | 
| 
      
 5 
     | 
    
         
            +
              # every request.  This slows down response time but is perfect for development
         
     | 
| 
      
 6 
     | 
    
         
            +
              # since you don't have to restart the webserver when you make code changes.
         
     | 
| 
      
 7 
     | 
    
         
            +
              config.cache_classes = false
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              # Log error messages when you accidentally call methods on nil.
         
     | 
| 
      
 10 
     | 
    
         
            +
              config.whiny_nils = true
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              # Show full error reports and disable caching
         
     | 
| 
      
 13 
     | 
    
         
            +
              config.consider_all_requests_local       = true
         
     | 
| 
      
 14 
     | 
    
         
            +
              config.action_controller.perform_caching = false
         
     | 
| 
      
 15 
     | 
    
         
            +
              config.secret_key_base = 123
         
     | 
| 
      
 16 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,46 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            Dummy::Application.configure do
         
     | 
| 
      
 2 
     | 
    
         
            +
              # Settings specified here will take precedence over those in config/environment.rb
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
              # The production environment is meant for finished, "live" apps.
         
     | 
| 
      
 5 
     | 
    
         
            +
              # Code is not reloaded between requests
         
     | 
| 
      
 6 
     | 
    
         
            +
              config.cache_classes = true
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              # Full error reports are disabled and caching is turned on
         
     | 
| 
      
 9 
     | 
    
         
            +
              config.consider_all_requests_local       = false
         
     | 
| 
      
 10 
     | 
    
         
            +
              config.action_controller.perform_caching = true
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              # Specifies the header that your server uses for sending files
         
     | 
| 
      
 13 
     | 
    
         
            +
              config.action_dispatch.x_sendfile_header = "X-Sendfile"
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              # For nginx:
         
     | 
| 
      
 16 
     | 
    
         
            +
              # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              # If you have no front-end server that supports something like X-Sendfile,
         
     | 
| 
      
 19 
     | 
    
         
            +
              # just comment this out and Rails will serve the files
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
              # See everything in the log (default is :info)
         
     | 
| 
      
 22 
     | 
    
         
            +
              # config.log_level = :debug
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              # Use a different logger for distributed setups
         
     | 
| 
      
 25 
     | 
    
         
            +
              # config.logger = SyslogLogger.new
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              # Use a different cache store in production
         
     | 
| 
      
 28 
     | 
    
         
            +
              # config.cache_store = :mem_cache_store
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              # Disable Rails's static asset server
         
     | 
| 
      
 31 
     | 
    
         
            +
              # In production, Apache or nginx will already do this
         
     | 
| 
      
 32 
     | 
    
         
            +
              config.serve_static_assets = false
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
              # Enable serving of images, stylesheets, and javascripts from an asset server
         
     | 
| 
      
 35 
     | 
    
         
            +
              # config.action_controller.asset_host = "http://assets.example.com"
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
              # Disable delivery errors, bad email addresses will be ignored
         
     | 
| 
      
 38 
     | 
    
         
            +
              # config.action_mailer.raise_delivery_errors = false
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
              # Enable threaded mode
         
     | 
| 
      
 41 
     | 
    
         
            +
              # config.threadsafe!
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
              # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
         
     | 
| 
      
 44 
     | 
    
         
            +
              # the I18n.default_locale when a translation can not be found)
         
     | 
| 
      
 45 
     | 
    
         
            +
              config.i18n.fallbacks = true
         
     | 
| 
      
 46 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,33 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            Dummy::Application.configure do
         
     | 
| 
      
 2 
     | 
    
         
            +
              # Settings specified here will take precedence over those in config/environment.rb
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
              # The test environment is used exclusively to run your application's
         
     | 
| 
      
 5 
     | 
    
         
            +
              # test suite.  You never need to work with it otherwise.  Remember that
         
     | 
| 
      
 6 
     | 
    
         
            +
              # your test database is "scratch space" for the test suite and is wiped
         
     | 
| 
      
 7 
     | 
    
         
            +
              # and recreated between test runs.  Don't rely on the data there!
         
     | 
| 
      
 8 
     | 
    
         
            +
              config.cache_classes = true
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              # Log error messages when you accidentally call methods on nil.
         
     | 
| 
      
 11 
     | 
    
         
            +
              config.whiny_nils = true
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              # Show full error reports and disable caching
         
     | 
| 
      
 14 
     | 
    
         
            +
              config.consider_all_requests_local       = true
         
     | 
| 
      
 15 
     | 
    
         
            +
              config.action_controller.perform_caching = false
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              # Raise exceptions instead of rendering exception templates
         
     | 
| 
      
 18 
     | 
    
         
            +
              config.action_dispatch.show_exceptions = false
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              # Disable request forgery protection in test environment
         
     | 
| 
      
 21 
     | 
    
         
            +
              config.action_controller.allow_forgery_protection    = false
         
     | 
| 
      
 22 
     | 
    
         
            +
              config.secret_key_base = "yo"
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              # Tell Action Mailer not to deliver emails to the real world.
         
     | 
| 
      
 25 
     | 
    
         
            +
              # The :test delivery method accumulates sent emails in the
         
     | 
| 
      
 26 
     | 
    
         
            +
              # ActionMailer::Base.deliveries array.
         
     | 
| 
      
 27 
     | 
    
         
            +
              #config.action_mailer.delivery_method = :test
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
              # Use SQL instead of Active Record's schema dumper when creating the test database.
         
     | 
| 
      
 30 
     | 
    
         
            +
              # This is necessary if your schema can't be completely dumped by the schema dumper,
         
     | 
| 
      
 31 
     | 
    
         
            +
              # like if you have constraints or database-specific column types
         
     | 
| 
      
 32 
     | 
    
         
            +
              # config.active_record.schema_format = :sql
         
     | 
| 
      
 33 
     | 
    
         
            +
            end
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
    
        data/test/errors_test.rb
    ADDED
    
    | 
         @@ -0,0 +1,95 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'test_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class ErrorsTest < MiniTest::Spec
         
     | 
| 
      
 4 
     | 
    
         
            +
              class AlbumForm < Reform::Form
         
     | 
| 
      
 5 
     | 
    
         
            +
                property :title
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                property :hit do
         
     | 
| 
      
 8 
     | 
    
         
            +
                  property :title
         
     | 
| 
      
 9 
     | 
    
         
            +
                  validates :title, :presence => true
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                collection :songs do
         
     | 
| 
      
 13 
     | 
    
         
            +
                  property :title
         
     | 
| 
      
 14 
     | 
    
         
            +
                  validates :title, :presence => true
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                validates :title, :presence => true
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              let (:album) do
         
     | 
| 
      
 21 
     | 
    
         
            +
                OpenStruct.new(
         
     | 
| 
      
 22 
     | 
    
         
            +
                  :title  => "Blackhawks Over Los Angeles",
         
     | 
| 
      
 23 
     | 
    
         
            +
                  :hit    => song,
         
     | 
| 
      
 24 
     | 
    
         
            +
                  :songs  => songs # TODO: document this requirement
         
     | 
| 
      
 25 
     | 
    
         
            +
                )
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
              let (:song)  { OpenStruct.new(:title => "Downtown") }
         
     | 
| 
      
 28 
     | 
    
         
            +
              let (:songs) { [song=OpenStruct.new(:title => "Calling"), song] }
         
     | 
| 
      
 29 
     | 
    
         
            +
              let (:form)  { AlbumForm.new(album) }
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              describe "incorrect #validate" do
         
     | 
| 
      
 33 
     | 
    
         
            +
                before { form.validate(
         
     | 
| 
      
 34 
     | 
    
         
            +
                  "hit"   =>{"title" => ""},
         
     | 
| 
      
 35 
     | 
    
         
            +
                  "title" => "",
         
     | 
| 
      
 36 
     | 
    
         
            +
                  "songs" => [{"title" => ""}, {"title" => ""}]) } # FIXME: what happens if item is missing?
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                it do
         
     | 
| 
      
 39 
     | 
    
         
            +
                  form.errors.messages.must_equal({
         
     | 
| 
      
 40 
     | 
    
         
            +
                    :title  => ["can't be blank"],
         
     | 
| 
      
 41 
     | 
    
         
            +
                    :"hit.title"=>["can't be blank"],
         
     | 
| 
      
 42 
     | 
    
         
            +
                    :"songs.title"=>["can't be blank"]})
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                it do
         
     | 
| 
      
 46 
     | 
    
         
            +
                  #form.errors.must_equal({:title  => ["can't be blank"]})
         
     | 
| 
      
 47 
     | 
    
         
            +
                  # TODO: this should only contain local errors?
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                # nested forms keep their own Errors:
         
     | 
| 
      
 51 
     | 
    
         
            +
                it { form.hit.errors.messages.must_equal({:title=>["can't be blank"]}) }
         
     | 
| 
      
 52 
     | 
    
         
            +
                it { form.songs[0].errors.messages.must_equal({:title=>["can't be blank"]}) }
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                it do
         
     | 
| 
      
 55 
     | 
    
         
            +
                  form.errors.messages.must_equal({
         
     | 
| 
      
 56 
     | 
    
         
            +
                    :title        => ["can't be blank"],
         
     | 
| 
      
 57 
     | 
    
         
            +
                    :"hit.title"  => ["can't be blank"],
         
     | 
| 
      
 58 
     | 
    
         
            +
                    :"songs.title"=> ["can't be blank"]})
         
     | 
| 
      
 59 
     | 
    
         
            +
                end # TODO: add another invalid item.
         
     | 
| 
      
 60 
     | 
    
         
            +
              end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
              describe "#validate with main form invalid" do
         
     | 
| 
      
 63 
     | 
    
         
            +
                before { @result = form.validate("title"=>"") }
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                it { @result.must_equal false }
         
     | 
| 
      
 66 
     | 
    
         
            +
                it { form.errors.messages.must_equal({:title=>["can't be blank"]}) }
         
     | 
| 
      
 67 
     | 
    
         
            +
              end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
              describe "#validate with middle nested form invalid" do
         
     | 
| 
      
 70 
     | 
    
         
            +
                before { @result = form.validate("hit"=>{"title" => ""}) }
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                it { @result.must_equal false }
         
     | 
| 
      
 73 
     | 
    
         
            +
                it { form.errors.messages.must_equal({:"hit.title"=>["can't be blank"]}) }
         
     | 
| 
      
 74 
     | 
    
         
            +
              end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
              describe "#validate with last nested form invalid" do
         
     | 
| 
      
 77 
     | 
    
         
            +
                before { @result = form.validate("songs"=>[{"title" => ""}]) }
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                it { @result.must_equal false }
         
     | 
| 
      
 80 
     | 
    
         
            +
                it { form.errors.messages.must_equal({:"songs.title"=>["can't be blank"]}) }
         
     | 
| 
      
 81 
     | 
    
         
            +
              end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
              describe "correct #validate" do
         
     | 
| 
      
 84 
     | 
    
         
            +
                before { @result = form.validate(
         
     | 
| 
      
 85 
     | 
    
         
            +
                  "hit"   => {"title" => "Sacrifice"},
         
     | 
| 
      
 86 
     | 
    
         
            +
                  "title" => "Second Heat",
         
     | 
| 
      
 87 
     | 
    
         
            +
                  "songs" => [{"title"=>"Heart Of A Lion"}]
         
     | 
| 
      
 88 
     | 
    
         
            +
                  ) }
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                it { @result.must_equal true }
         
     | 
| 
      
 91 
     | 
    
         
            +
                it { form.hit.title.must_equal "Sacrifice" }
         
     | 
| 
      
 92 
     | 
    
         
            +
                it { form.title.must_equal "Second Heat" }
         
     | 
| 
      
 93 
     | 
    
         
            +
                it { form.songs.first.title.must_equal "Heart Of A Lion" }
         
     | 
| 
      
 94 
     | 
    
         
            +
              end
         
     | 
| 
      
 95 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,60 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'test_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class FormCompositionTest < MiniTest::Spec
         
     | 
| 
      
 4 
     | 
    
         
            +
              class SongForm < Reform::Form
         
     | 
| 
      
 5 
     | 
    
         
            +
                include Composition
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                property  :title,           :on => :song
         
     | 
| 
      
 8 
     | 
    
         
            +
                properties [:name, :genre], :on => :artist
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                validates :name, :title, :genre, :presence => true
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              let (:form)   { SongForm.new(:song => song, :artist => artist) }
         
     | 
| 
      
 14 
     | 
    
         
            +
              let (:song)   { OpenStruct.new(:title => "Rio") }
         
     | 
| 
      
 15 
     | 
    
         
            +
              let (:artist) { OpenStruct.new(:name => "Duran Duran") }
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              # delegation form -> composition works
         
     | 
| 
      
 19 
     | 
    
         
            +
              it { form.title.must_equal  "Rio" }
         
     | 
| 
      
 20 
     | 
    
         
            +
              it { form.name.must_equal   "Duran Duran" }
         
     | 
| 
      
 21 
     | 
    
         
            +
              # delegation form -> composed models (e.g. when saving this can be handy)
         
     | 
| 
      
 22 
     | 
    
         
            +
              it { form.song.must_equal   song }
         
     | 
| 
      
 23 
     | 
    
         
            +
              it { form.artist.must_equal artist }
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              it "creates Composition for you" do
         
     | 
| 
      
 27 
     | 
    
         
            +
                form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb").must_equal false
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              describe "#save" do
         
     | 
| 
      
 31 
     | 
    
         
            +
                it "provides data block argument" do
         
     | 
| 
      
 32 
     | 
    
         
            +
                  hash = {}
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                  form.save do |data, map|
         
     | 
| 
      
 35 
     | 
    
         
            +
                    hash[:name]   = data.name
         
     | 
| 
      
 36 
     | 
    
         
            +
                    hash[:title]  = data.title
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  hash.must_equal({:name=>"Duran Duran", :title=>"Rio"})
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                it "provides nested symbolized hash as second block argument" do
         
     | 
| 
      
 43 
     | 
    
         
            +
                  hash = {}
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                  form.save do |data, map|
         
     | 
| 
      
 46 
     | 
    
         
            +
                    hash = map
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  hash.must_equal({:song=>{:title=>"Rio"}, :artist=>{:name=>"Duran Duran"}})
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                it "pushes data to models when no block passed" do
         
     | 
| 
      
 53 
     | 
    
         
            +
                  form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb")
         
     | 
| 
      
 54 
     | 
    
         
            +
                  form.save
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                  artist.name.must_equal "Frenzal Rhomb"
         
     | 
| 
      
 57 
     | 
    
         
            +
                  song.title.must_equal "Greyhound"
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
              end
         
     | 
| 
      
 60 
     | 
    
         
            +
            end
         
     |