reform 1.2.0.beta2 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/.travis.yml +2 -1
 - data/CHANGES.md +53 -7
 - data/LICENSE.txt +1 -1
 - data/README.md +48 -8
 - data/database.sqlite3 +0 -0
 - data/lib/reform/contract.rb +58 -4
 - data/lib/reform/contract/setup.rb +16 -22
 - data/lib/reform/contract/validate.rb +18 -21
 - data/lib/reform/form/active_model.rb +6 -8
 - data/lib/reform/form/json.rb +1 -1
 - data/lib/reform/form/save.rb +29 -59
 - data/lib/reform/form/sync.rb +52 -70
 - data/lib/reform/form/validate.rb +37 -44
 - data/lib/reform/representer.rb +6 -10
 - data/lib/reform/version.rb +1 -1
 - data/test/active_record_test.rb +68 -0
 - data/test/benchmarking.rb +26 -0
 - data/test/contract_test.rb +40 -0
 - data/test/custom_validation_test.rb +1 -1
 - data/test/empty_test.rb +31 -3
 - data/test/form_composition_test.rb +1 -1
 - data/test/from_test.rb +150 -0
 - data/test/model_validations_test.rb +2 -2
 - data/test/nested_form_test.rb +4 -4
 - data/test/read_only_test.rb +0 -25
 - data/test/readable_test.rb +32 -0
 - data/test/reform_test.rb +0 -21
 - data/test/virtual_test.rb +26 -0
 - data/test/writeable_test.rb +30 -0
 - metadata +14 -6
 - data/test/as_test.rb +0 -75
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA1:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: d8d0dc76658a132e15bea8612f35e7bfa77fb7b8
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 31226f61d63c42b0a641b5592eb9714cb29bcff2
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: cde409c7fa9dc9054d05e2dafb7cf672abf8585d85b97f05f8f0d37b0d6c0de25e554775fb7a8a5eec57567977ba8ff03cdcb2276190c964e5f62c31259ce027
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 70ac7b9421a13e0936ba9e149f611ad9f540b071f1f557d474a191c9c1d4a7e2a8fc126a9510a7fed65b1ef70078033ea4e8f34a6d9d2bc3dd9b74fbc61bd1c3
         
     | 
    
        data/.travis.yml
    CHANGED
    
    
    
        data/CHANGES.md
    CHANGED
    
    | 
         @@ -1,3 +1,7 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ## 1.2.1
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            * Fixed a nasty bug where `ActiveModel` forms with form builder support wouldn't deserialize properly. A million Thanks to @karolsarnacki for finding this and providing an exemplary failing test. <3
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
       1 
5 
     | 
    
         
             
            ## 1.2.0
         
     | 
| 
       2 
6 
     | 
    
         | 
| 
       3 
7 
     | 
    
         
             
            ### Breakage
         
     | 
| 
         @@ -11,16 +15,29 @@ 
     | 
|
| 
       11 
15 
     | 
    
         | 
| 
       12 
16 
     | 
    
         
             
                Including this module will add `#column_for_attribute` and other methods need by form builders to automatically guess the type of a property.
         
     | 
| 
       13 
17 
     | 
    
         | 
| 
      
 18 
     | 
    
         
            +
            * `Form#save` no longer passed `self` to the block. You've been warned long enough. ;)
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
       14 
20 
     | 
    
         
             
            ### Changes
         
     | 
| 
       15 
21 
     | 
    
         | 
| 
      
 22 
     | 
    
         
            +
            * Renamed `:as` to `:from` to be in line with Representable/Roar, Disposable and Cells. Thanks to @bethesque for pushing this.
         
     | 
| 
      
 23 
     | 
    
         
            +
            * `:empty` is now `:virtual` and `:virtual` is `writeable: false`. It was too confusing and sucked. Thanks to @bethesque, again, for her moral assistance.
         
     | 
| 
       16 
24 
     | 
    
         
             
            * `Form#save` with `Composition` now returns true only if all composite models saved.
         
     | 
| 
       17 
25 
     | 
    
         
             
            * `Form::copy_validations_from` allows copying custom validators now.
         
     | 
| 
       18 
26 
     | 
    
         
             
            * New call style for `::properties`. Instead of an array, it's now `properties :title, :genre`.
         
     | 
| 
       19 
27 
     | 
    
         
             
            * All options are evaluated with `pass_options: true`.
         
     | 
| 
      
 28 
     | 
    
         
            +
            * All transforming representers are now created and stored on class level, resulting in simpler code and a 85% speed-up.
         
     | 
| 
       20 
29 
     | 
    
         | 
| 
       21 
30 
     | 
    
         
             
            ### New Stuff!!!
         
     | 
| 
       22 
31 
     | 
    
         | 
| 
       23 
     | 
    
         
            -
            *  
     | 
| 
      
 32 
     | 
    
         
            +
            * In `#validate`, you can ignore properties now using `:skip_if`.
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                ```ruby
         
     | 
| 
      
 35 
     | 
    
         
            +
                property :hit, skip_if: lambda { |fragment, *| fragment["title"].blank? }
         
     | 
| 
      
 36 
     | 
    
         
            +
                ```
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                This works for both properties and nested forms. The property will simply be ignored when deserializing, as if it had never been in the incoming hash/document.
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                For nested properties you can use `:skip_if: :all_blank` as a macro to ignore a nested form if all values are blank.
         
     | 
| 
       24 
41 
     | 
    
         
             
            * You can now specify validations right in the `::property` call.
         
     | 
| 
       25 
42 
     | 
    
         | 
| 
       26 
43 
     | 
    
         
             
                ```ruby
         
     | 
| 
         @@ -28,19 +45,48 @@ 
     | 
|
| 
       28 
45 
     | 
    
         
             
                ```
         
     | 
| 
       29 
46 
     | 
    
         | 
| 
       30 
47 
     | 
    
         
             
                Thanks to @zubin for this brillant idea!
         
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
            *  
     | 
| 
       33 
     | 
    
         
            -
            *  
     | 
| 
       34 
     | 
    
         
            -
            *  
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
            * Reform now tracks which attributes have changed after `#validate`. You can check that using `form.changed?(:title)`.
         
     | 
| 
      
 50 
     | 
    
         
            +
            * When including `Sync::SkipUnchanged`, the form won't try to assign unchanged values anymore in `#sync`. This is extremely helpful when handling file uploads and the like.
         
     | 
| 
      
 51 
     | 
    
         
            +
            * Both `#sync` and `#save` can be configured dynamically now.
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                When syncing, you can run a lambda per property.
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                ```ruby
         
     | 
| 
      
 56 
     | 
    
         
            +
                property :title, sync: lambda { |value, options| model.set_title(value) }
         
     | 
| 
      
 57 
     | 
    
         
            +
                ```
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                This won't run Reform's built-in sync for this property.
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                You can also provide the sync lambda at run-time.
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                ```ruby
         
     | 
| 
      
 64 
     | 
    
         
            +
                form.sync(title: lambda { |value, options| form.model.title = "HOT: #{value}" })
         
     | 
| 
      
 65 
     | 
    
         
            +
                ```
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                This block is run in the caller's context allowing you to access environment variables.
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                Note that the dynamic sync happens _before_ save, so the model id may unavailable.
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                You can do the same for saving.
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                ```ruby
         
     | 
| 
      
 74 
     | 
    
         
            +
                form.save(title: lambda { |value, options| form.model.title = "#{form.model.id} --> #{value}" })
         
     | 
| 
      
 75 
     | 
    
         
            +
                ```
         
     | 
| 
      
 76 
     | 
    
         
            +
                Again, this block is run in the caller's context.
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                The two features are an excellent way to handle file uploads without ActiveRecord's horrible callbacks.
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
            * Adding generic `:base` errors now works. Thanks to @bethesque.
         
     | 
| 
       37 
81 
     | 
    
         | 
| 
       38 
82 
     | 
    
         
             
                ```ruby
         
     | 
| 
       39 
83 
     | 
    
         
             
                errors.add(:base, "You are too awesome!")
         
     | 
| 
       40 
84 
     | 
    
         
             
                ```
         
     | 
| 
       41 
85 
     | 
    
         | 
| 
       42 
86 
     | 
    
         
             
              This will prefix the error with `:base`.
         
     | 
| 
      
 87 
     | 
    
         
            +
            * Need your form to parse JSON? Include `Reform::Form::JSON`, the `#validate` method now expects a JSON string and will deserialize and populate the form from the JSON document.
         
     | 
| 
       43 
88 
     | 
    
         
             
            * Added `Form::schema` to generate a pure representer from the form's representer.
         
     | 
| 
      
 89 
     | 
    
         
            +
            * Added `:readable` and `:writeable` option which allow to skip reading or writing to the model when `false`.
         
     | 
| 
       44 
90 
     | 
    
         | 
| 
       45 
91 
     | 
    
         
             
            ## 1.1.1
         
     | 
| 
       46 
92 
     | 
    
         | 
    
        data/LICENSE.txt
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | 
         @@ -98,11 +98,11 @@ class SongForm < Reform::Form 
     | 
|
| 
       98 
98 
     | 
    
         | 
| 
       99 
99 
     | 
    
         
             
            Internally, this form will call `song.title` to populate the title field.
         
     | 
| 
       100 
100 
     | 
    
         | 
| 
       101 
     | 
    
         
            -
            If you, for whatever reasons, want to use a different public name, use `: 
     | 
| 
      
 101 
     | 
    
         
            +
            If you, for whatever reasons, want to use a different public name, use `:from`.
         
     | 
| 
       102 
102 
     | 
    
         | 
| 
       103 
103 
     | 
    
         
             
            ```ruby
         
     | 
| 
       104 
104 
     | 
    
         
             
            class SongForm < Reform::Form
         
     | 
| 
       105 
     | 
    
         
            -
              property :name,  
     | 
| 
      
 105 
     | 
    
         
            +
              property :name, from: :title
         
     | 
| 
       106 
106 
     | 
    
         
             
            ```
         
     | 
| 
       107 
107 
     | 
    
         | 
| 
       108 
108 
     | 
    
         
             
            This will still call `song.title` but expose the attribute as `name`.
         
     | 
| 
         @@ -633,17 +633,17 @@ As with the built-in coercion, this setter is only called in `#validate`. 
     | 
|
| 
       633 
633 
     | 
    
         
             
            Virtual fields come in handy when there's no direct mapping to a model attribute or when you plan on displaying but not processing a value.
         
     | 
| 
       634 
634 
     | 
    
         | 
| 
       635 
635 
     | 
    
         | 
| 
       636 
     | 
    
         
            -
            ###  
     | 
| 
      
 636 
     | 
    
         
            +
            ### Virtual Fields
         
     | 
| 
       637 
637 
     | 
    
         | 
| 
       638 
     | 
    
         
            -
            Often, fields like `password_confirmation`  
     | 
| 
      
 638 
     | 
    
         
            +
            Often, fields like `password_confirmation` should neither be read from nor written back to the model. Reform comes with the `:virtual` option to handle that case.
         
     | 
| 
       639 
639 
     | 
    
         | 
| 
       640 
640 
     | 
    
         
             
            ```ruby
         
     | 
| 
       641 
641 
     | 
    
         
             
            class PasswordForm < Reform::Form
         
     | 
| 
       642 
642 
     | 
    
         
             
              property :password
         
     | 
| 
       643 
     | 
    
         
            -
              property :password_confirmation, : 
     | 
| 
      
 643 
     | 
    
         
            +
              property :password_confirmation, virtual: true
         
     | 
| 
       644 
644 
     | 
    
         
             
            ```
         
     | 
| 
       645 
645 
     | 
    
         | 
| 
       646 
     | 
    
         
            -
            Here, the model won't be queried for a `password_confirmation` field when creating and rendering the form.  
     | 
| 
      
 646 
     | 
    
         
            +
            Here, the model won't be queried for a `password_confirmation` field when creating and rendering the form. When saving the form, the input value is not written to the decorated model. It is only readable in validations and when saving the form manually.
         
     | 
| 
       647 
647 
     | 
    
         | 
| 
       648 
648 
     | 
    
         
             
            ```ruby
         
     | 
| 
       649 
649 
     | 
    
         
             
            form.validate("password" => "123", "password_confirmation" => "321")
         
     | 
| 
         @@ -660,11 +660,11 @@ form.save do |nested| 
     | 
|
| 
       660 
660 
     | 
    
         | 
| 
       661 
661 
     | 
    
         
             
            ### Read-Only Fields
         
     | 
| 
       662 
662 
     | 
    
         | 
| 
       663 
     | 
    
         
            -
             
     | 
| 
      
 663 
     | 
    
         
            +
            When you want to show a value but skip processing it after submission the `:writeable` option is your friend.
         
     | 
| 
       664 
664 
     | 
    
         | 
| 
       665 
665 
     | 
    
         
             
            ```ruby
         
     | 
| 
       666 
666 
     | 
    
         
             
            class ProfileForm < Reform::Form
         
     | 
| 
       667 
     | 
    
         
            -
              property :country, : 
     | 
| 
      
 667 
     | 
    
         
            +
              property :country, :writeable => false
         
     | 
| 
       668 
668 
     | 
    
         
             
            ```
         
     | 
| 
       669 
669 
     | 
    
         | 
| 
       670 
670 
     | 
    
         
             
            This time reform will query the model for the value by calling `model.country`.
         
     | 
| 
         @@ -678,6 +678,14 @@ form.save do |nested| 
     | 
|
| 
       678 
678 
     | 
    
         
             
              nested[:country] #=> "Australia"
         
     | 
| 
       679 
679 
     | 
    
         
             
            ```
         
     | 
| 
       680 
680 
     | 
    
         | 
| 
      
 681 
     | 
    
         
            +
            ### Write-Only Fields
         
     | 
| 
      
 682 
     | 
    
         
            +
             
     | 
| 
      
 683 
     | 
    
         
            +
            A third alternative is to hide a field's value but write it to the database when syncing. This can be achieved using the `:readable` option.
         
     | 
| 
      
 684 
     | 
    
         
            +
             
     | 
| 
      
 685 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 686 
     | 
    
         
            +
            property :credit_card_number, :readable => false
         
     | 
| 
      
 687 
     | 
    
         
            +
            ```
         
     | 
| 
      
 688 
     | 
    
         
            +
             
     | 
| 
       681 
689 
     | 
    
         
             
            ## Validations From Models
         
     | 
| 
       682 
690 
     | 
    
         | 
| 
       683 
691 
     | 
    
         
             
            Sometimes when you still keep validations in your models (which you shouldn't) copying them to a form might not feel right. In that case, you can let Reform automatically copy them.
         
     | 
| 
         @@ -869,6 +877,38 @@ form.validate("title" => "Just Kiddin'") 
     | 
|
| 
       869 
877 
     | 
    
         
             
            form.changed?(:title) #=> true
         
     | 
| 
       870 
878 
     | 
    
         
             
            ```
         
     | 
| 
       871 
879 
     | 
    
         | 
| 
      
 880 
     | 
    
         
            +
            When including `Sync::SkipUnchanged`, the form won't assign unchanged values anymore in `#sync`.
         
     | 
| 
      
 881 
     | 
    
         
            +
             
     | 
| 
      
 882 
     | 
    
         
            +
             
     | 
| 
      
 883 
     | 
    
         
            +
            ## Dynamically Syncing And Saving Properties
         
     | 
| 
      
 884 
     | 
    
         
            +
             
     | 
| 
      
 885 
     | 
    
         
            +
            Both `#sync` and `#save` can be configured to run a dynamical lambda per property.
         
     | 
| 
      
 886 
     | 
    
         
            +
             
     | 
| 
      
 887 
     | 
    
         
            +
            The `sync:` option allows to statically add a lambda to a property.
         
     | 
| 
      
 888 
     | 
    
         
            +
             
     | 
| 
      
 889 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 890 
     | 
    
         
            +
            property :title, sync: lambda { |value, options| model.set_title(value) }
         
     | 
| 
      
 891 
     | 
    
         
            +
            ```
         
     | 
| 
      
 892 
     | 
    
         
            +
             
     | 
| 
      
 893 
     | 
    
         
            +
            Instead of running Reform's built-in sync for this property the block is run.
         
     | 
| 
      
 894 
     | 
    
         
            +
             
     | 
| 
      
 895 
     | 
    
         
            +
            You can also provide the sync lambda at run-time.
         
     | 
| 
      
 896 
     | 
    
         
            +
             
     | 
| 
      
 897 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 898 
     | 
    
         
            +
            form.sync(title: lambda { |value, options| form.model.title = "HOT: #{value}" })
         
     | 
| 
      
 899 
     | 
    
         
            +
            ```
         
     | 
| 
      
 900 
     | 
    
         
            +
             
     | 
| 
      
 901 
     | 
    
         
            +
            This block is run in the caller's context allowing you to access environment variables. Note that the dynamic sync happens _before_ save, so the model id may unavailable.
         
     | 
| 
      
 902 
     | 
    
         
            +
             
     | 
| 
      
 903 
     | 
    
         
            +
            You can do the same for saving.
         
     | 
| 
      
 904 
     | 
    
         
            +
             
     | 
| 
      
 905 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 906 
     | 
    
         
            +
            form.save(title: lambda { |value, options| form.model.title = "#{form.model.id} --> #{value}" })
         
     | 
| 
      
 907 
     | 
    
         
            +
            ```
         
     | 
| 
      
 908 
     | 
    
         
            +
            Again, this block is run in the caller's context.
         
     | 
| 
      
 909 
     | 
    
         
            +
             
     | 
| 
      
 910 
     | 
    
         
            +
            The two features are an excellent way to handle file uploads without ActiveRecord's horrible callbacks.
         
     | 
| 
      
 911 
     | 
    
         
            +
             
     | 
| 
       872 
912 
     | 
    
         | 
| 
       873 
913 
     | 
    
         
             
            ## Undocumented Features
         
     | 
| 
       874 
914 
     | 
    
         | 
    
        data/database.sqlite3
    CHANGED
    
    | 
         Binary file 
     | 
    
        data/lib/reform/contract.rb
    CHANGED
    
    | 
         @@ -29,12 +29,27 @@ module Reform 
     | 
|
| 
       29 
29 
     | 
    
         | 
| 
       30 
30 
     | 
    
         
             
                module PropertyMethods
         
     | 
| 
       31 
31 
     | 
    
         
             
                  def property(name, options={}, &block)
         
     | 
| 
       32 
     | 
    
         
            -
                    options 
     | 
| 
      
 32 
     | 
    
         
            +
                    deprecate_as!(options)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    options[:private_name] = options.delete(:from)
         
     | 
| 
       33 
34 
     | 
    
         
             
                    options[:coercion_type] = options.delete(:type)
         
     | 
| 
       34 
35 
     | 
    
         
             
                    options[:features] ||= []
         
     | 
| 
       35 
36 
     | 
    
         
             
                    options[:features] += features.keys if block_given?
         
     | 
| 
       36 
37 
     | 
    
         
             
                    options[:pass_options] = true
         
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    # readable and writeable is true as it's not == false
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                    if reform_2_0
         
     | 
| 
      
 42 
     | 
    
         
            +
                      if options.delete(:virtual)
         
     | 
| 
      
 43 
     | 
    
         
            +
                        options[:_readable]  = false
         
     | 
| 
      
 44 
     | 
    
         
            +
                        options[:_writeable] = false
         
     | 
| 
      
 45 
     | 
    
         
            +
                      else
         
     | 
| 
      
 46 
     | 
    
         
            +
                        options[:_readable]  = options.delete(:readable)
         
     | 
| 
      
 47 
     | 
    
         
            +
                        options[:_writeable] = options.delete(:writeable)
         
     | 
| 
      
 48 
     | 
    
         
            +
                      end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                    else # TODO: remove me in 2.0.
         
     | 
| 
      
 51 
     | 
    
         
            +
                      deprecate_virtual_and_empty!(options)
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end
         
     | 
| 
       38 
53 
     | 
    
         | 
| 
       39 
54 
     | 
    
         
             
                    validates(name, options.delete(:validates).dup) if options[:validates]
         
     | 
| 
       40 
55 
     | 
    
         | 
| 
         @@ -98,10 +113,24 @@ module Reform 
     | 
|
| 
       98 
113 
     | 
    
         | 
| 
       99 
114 
     | 
    
         
             
                require 'reform/contract/setup'
         
     | 
| 
       100 
115 
     | 
    
         
             
                include Setup
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                def self.representers # keeps all transformation representers for one class.
         
     | 
| 
      
 118 
     | 
    
         
            +
                  @representers ||= {}
         
     | 
| 
      
 119 
     | 
    
         
            +
                end
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                def self.representer(name=nil, options={}, &block)
         
     | 
| 
      
 122 
     | 
    
         
            +
                  return representer_class.each(&block) if name == nil
         
     | 
| 
      
 123 
     | 
    
         
            +
                  return representers[name] if representers[name] # don't run block as this representer is already setup for this form class.
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                  only_forms = options[:all] ? false : true
         
     | 
| 
      
 126 
     | 
    
         
            +
                  base       = options[:superclass] || representer_class
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                  representers[name] = Class.new(base).each(only_forms, &block) # let user modify representer.
         
     | 
| 
      
 129 
     | 
    
         
            +
                end
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
       101 
131 
     | 
    
         
             
                require 'reform/contract/validate'
         
     | 
| 
       102 
132 
     | 
    
         
             
                include Validate
         
     | 
| 
       103 
133 
     | 
    
         | 
| 
       104 
     | 
    
         
            -
             
     | 
| 
       105 
134 
     | 
    
         
             
                def errors # FIXME: this is needed for Rails 3.0 compatibility.
         
     | 
| 
       106 
135 
     | 
    
         
             
                  @errors ||= Errors.new(self)
         
     | 
| 
       107 
136 
     | 
    
         
             
                end
         
     | 
| 
         @@ -111,10 +140,35 @@ module Reform 
     | 
|
| 
       111 
140 
     | 
    
         
             
                attr_accessor :fields
         
     | 
| 
       112 
141 
     | 
    
         
             
                attr_writer :errors # only used in top form. (is that true?)
         
     | 
| 
       113 
142 
     | 
    
         | 
| 
       114 
     | 
    
         
            -
                def mapper
         
     | 
| 
      
 143 
     | 
    
         
            +
                def mapper # FIXME: do we need this with class-level representers?
         
     | 
| 
       115 
144 
     | 
    
         
             
                  self.class.representer_class
         
     | 
| 
       116 
145 
     | 
    
         
             
                end
         
     | 
| 
       117 
146 
     | 
    
         | 
| 
      
 147 
     | 
    
         
            +
                def self.deprecate_as!(options) # TODO: remove me in 2.0.
         
     | 
| 
      
 148 
     | 
    
         
            +
                  return unless as = options.delete(:as)
         
     | 
| 
      
 149 
     | 
    
         
            +
                  options[:from] = as
         
     | 
| 
      
 150 
     | 
    
         
            +
                  warn "[Reform] The :as options got renamed to :from. See https://github.com/apotonick/reform/wiki/Migration-Guide and have a nice day."
         
     | 
| 
      
 151 
     | 
    
         
            +
                end
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
                def self.deprecate_virtual_and_empty!(options) # TODO: remove me in 2.0.
         
     | 
| 
      
 154 
     | 
    
         
            +
                  if options.delete(:virtual)
         
     | 
| 
      
 155 
     | 
    
         
            +
                    warn "[Reform] The :virtual option has changed! Check https://github.com/apotonick/reform/wiki/Migration-Guide and have a good day."
         
     | 
| 
      
 156 
     | 
    
         
            +
                    options[:_readable] = true
         
     | 
| 
      
 157 
     | 
    
         
            +
                    options[:_writeable] = false
         
     | 
| 
      
 158 
     | 
    
         
            +
                  end
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
                  if options[:empty]
         
     | 
| 
      
 161 
     | 
    
         
            +
                    warn "[Reform] The :empty option has changed! Check https://github.com/apotonick/reform/wiki/Migration-Guide and have a good day."
         
     | 
| 
      
 162 
     | 
    
         
            +
                    options[:_readable]  = false
         
     | 
| 
      
 163 
     | 
    
         
            +
                    options[:_writeable] = false
         
     | 
| 
      
 164 
     | 
    
         
            +
                  end
         
     | 
| 
      
 165 
     | 
    
         
            +
                end
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
                inheritable_attr :reform_2_0 # TODO: remove me in 2.0.
         
     | 
| 
      
 168 
     | 
    
         
            +
                def self.reform_2_0!
         
     | 
| 
      
 169 
     | 
    
         
            +
                  self.reform_2_0= true
         
     | 
| 
      
 170 
     | 
    
         
            +
                end
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
       118 
172 
     | 
    
         
             
                def self.register_feature(mod)
         
     | 
| 
       119 
173 
     | 
    
         
             
                  features[mod] = true
         
     | 
| 
       120 
174 
     | 
    
         
             
                end
         
     | 
| 
         @@ -6,14 +6,25 @@ module Reform 
     | 
|
| 
       6 
6 
     | 
    
         
             
                    @fields = setup_fields  # delegate all methods to Fields instance.
         
     | 
| 
       7 
7 
     | 
    
         
             
                  end
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
      
 9 
     | 
    
         
            +
                  # Setup#to_hash will create a nested hash of property values from the model.
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # Nested properties will be recursively wrapped in a form instance.
         
     | 
| 
      
 11 
     | 
    
         
            +
                  def setup_representer
         
     | 
| 
      
 12 
     | 
    
         
            +
                    self.class.representer(:setup) do |dfn| # only nested forms.
         
     | 
| 
      
 13 
     | 
    
         
            +
                      dfn.merge!(
         
     | 
| 
      
 14 
     | 
    
         
            +
                        :representable => false, # don't call #to_hash, only prepare.
         
     | 
| 
      
 15 
     | 
    
         
            +
                        :prepare       => lambda { |model, args| args.binding[:form].new(model) } # wrap nested properties in form.
         
     | 
| 
      
 16 
     | 
    
         
            +
                      )
         
     | 
| 
      
 17 
     | 
    
         
            +
                    end
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
       9 
20 
     | 
    
         
             
                  def setup_fields
         
     | 
| 
       10 
     | 
    
         
            -
                    representer =  
     | 
| 
      
 21 
     | 
    
         
            +
                    representer = setup_representer.new(aliased_model)
         
     | 
| 
       11 
22 
     | 
    
         
             
                    options     = setup_options(Reform::Representer::Options[]) # handles :empty.
         
     | 
| 
       12 
23 
     | 
    
         | 
| 
      
 24 
     | 
    
         
            +
                    # populate the internal @fields set with data from the model.
         
     | 
| 
       13 
25 
     | 
    
         
             
                    create_fields(mapper.fields, representer.to_hash(options))
         
     | 
| 
       14 
26 
     | 
    
         
             
                  end
         
     | 
| 
       15 
27 
     | 
    
         | 
| 
       16 
     | 
    
         
            -
                  # DISCUSS: setting up the Validation (populating with values) will soon be handled with Disposable::Twin logic.
         
     | 
| 
       17 
28 
     | 
    
         
             
                  def create_fields(field_names, fields)
         
     | 
| 
       18 
29 
     | 
    
         
             
                    Fields.new(field_names, fields)
         
     | 
| 
       19 
30 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -26,31 +37,14 @@ module Reform 
     | 
|
| 
       26 
37 
     | 
    
         
             
                  include SetupOptions
         
     | 
| 
       27 
38 
     | 
    
         | 
| 
       28 
39 
     | 
    
         | 
| 
       29 
     | 
    
         
            -
                   
     | 
| 
       30 
     | 
    
         
            -
                  module Representer
         
     | 
| 
       31 
     | 
    
         
            -
                    def to_hash(*)
         
     | 
| 
       32 
     | 
    
         
            -
                      nested_forms do |attr|
         
     | 
| 
       33 
     | 
    
         
            -
                        attr.merge!(
         
     | 
| 
       34 
     | 
    
         
            -
                          :representable => false, # don't call #to_hash.
         
     | 
| 
       35 
     | 
    
         
            -
                          :prepare       => lambda do |model, args|
         
     | 
| 
       36 
     | 
    
         
            -
                            args.binding[:form].new(model)
         
     | 
| 
       37 
     | 
    
         
            -
                          end
         
     | 
| 
       38 
     | 
    
         
            -
                        )
         
     | 
| 
       39 
     | 
    
         
            -
                      end
         
     | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
       41 
     | 
    
         
            -
                      super
         
     | 
| 
       42 
     | 
    
         
            -
                    end
         
     | 
| 
       43 
     | 
    
         
            -
                  end # Representer
         
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
                  module Empty
         
     | 
| 
      
 40 
     | 
    
         
            +
                  module Readable
         
     | 
| 
       47 
41 
     | 
    
         
             
                    def setup_options(options)
         
     | 
| 
       48 
     | 
    
         
            -
                      empty_fields = mapper.representable_attrs.find_all { |d| d[: 
     | 
| 
      
 42 
     | 
    
         
            +
                      empty_fields = mapper.representable_attrs.find_all { |d| d[:_readable] == false }.collect  { |d| d.name.to_sym }
         
     | 
| 
       49 
43 
     | 
    
         | 
| 
       50 
44 
     | 
    
         
             
                      options.exclude!(empty_fields)
         
     | 
| 
       51 
45 
     | 
    
         
             
                    end
         
     | 
| 
       52 
46 
     | 
    
         
             
                  end
         
     | 
| 
       53 
     | 
    
         
            -
                  include  
     | 
| 
      
 47 
     | 
    
         
            +
                  include Readable
         
     | 
| 
       54 
48 
     | 
    
         
             
                end
         
     | 
| 
       55 
49 
     | 
    
         
             
              end # Setup
         
     | 
| 
       56 
50 
     | 
    
         
             
            end
         
     | 
| 
         @@ -1,24 +1,4 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Reform::Contract::Validate
         
     | 
| 
       2 
     | 
    
         
            -
              module NestedValid
         
     | 
| 
       3 
     | 
    
         
            -
                def to_hash(*)
         
     | 
| 
       4 
     | 
    
         
            -
                  nested_forms do |attr|
         
     | 
| 
       5 
     | 
    
         
            -
                    attr.merge!(
         
     | 
| 
       6 
     | 
    
         
            -
                      :serialize => lambda { |object, args|
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
                        # FIXME: merge with Validate::Writer
         
     | 
| 
       9 
     | 
    
         
            -
                        options = args.user_options.dup
         
     | 
| 
       10 
     | 
    
         
            -
                        options[:prefix] = options[:prefix].dup # TODO: implement Options#dup.
         
     | 
| 
       11 
     | 
    
         
            -
                        options[:prefix] << args.binding.name # FIXME: should be #as.
         
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
                        object.validate!(options) # recursively call valid?
         
     | 
| 
       14 
     | 
    
         
            -
                      },
         
     | 
| 
       15 
     | 
    
         
            -
                    )
         
     | 
| 
       16 
     | 
    
         
            -
                  end
         
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
                  super
         
     | 
| 
       19 
     | 
    
         
            -
                end
         
     | 
| 
       20 
     | 
    
         
            -
              end
         
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
2 
     | 
    
         
             
              def validate
         
     | 
| 
       23 
3 
     | 
    
         
             
                options = {:errors => errs = Reform::Contract::Errors.new(self), :prefix => []}
         
     | 
| 
       24 
4 
     | 
    
         | 
| 
         @@ -28,15 +8,32 @@ module Reform::Contract::Validate 
     | 
|
| 
       28 
8 
     | 
    
         | 
| 
       29 
9 
     | 
    
         
             
                errors.valid?
         
     | 
| 
       30 
10 
     | 
    
         
             
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
       31 
12 
     | 
    
         
             
              def validate!(options)
         
     | 
| 
       32 
13 
     | 
    
         
             
                prefix = options[:prefix]
         
     | 
| 
       33 
14 
     | 
    
         | 
| 
       34 
15 
     | 
    
         
             
                # call valid? recursively and collect nested errors.
         
     | 
| 
       35 
     | 
    
         
            -
                 
     | 
| 
      
 16 
     | 
    
         
            +
                valid_representer.new(fields).to_hash(options) # TODO: only include nested forms here.
         
     | 
| 
       36 
17 
     | 
    
         | 
| 
       37 
18 
     | 
    
         
             
                valid?  # this validates on <Fields> using AM::Validations, currently.
         
     | 
| 
       38 
19 
     | 
    
         | 
| 
       39 
20 
     | 
    
         
             
                options[:errors].merge!(self.errors, prefix)
         
     | 
| 
       40 
21 
     | 
    
         
             
              end
         
     | 
| 
       41 
22 
     | 
    
         | 
| 
      
 23 
     | 
    
         
            +
            private
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              # runs form.validate! on all nested forms
         
     | 
| 
      
 26 
     | 
    
         
            +
              def valid_representer
         
     | 
| 
      
 27 
     | 
    
         
            +
                self.class.representer(:valid) do |dfn|
         
     | 
| 
      
 28 
     | 
    
         
            +
                  dfn.merge!(
         
     | 
| 
      
 29 
     | 
    
         
            +
                    :serialize => lambda { |form, args|
         
     | 
| 
      
 30 
     | 
    
         
            +
                      options = args.user_options.dup
         
     | 
| 
      
 31 
     | 
    
         
            +
                      options[:prefix] = options[:prefix].dup # TODO: implement Options#dup.
         
     | 
| 
      
 32 
     | 
    
         
            +
                      options[:prefix] << args.binding.name # FIXME: should be #as.
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                      form.validate!(options) # recursively call valid? on nested form.
         
     | 
| 
      
 35 
     | 
    
         
            +
                    }
         
     | 
| 
      
 36 
     | 
    
         
            +
                  )
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
       42 
39 
     | 
    
         
             
            end
         
     | 
| 
         @@ -29,22 +29,20 @@ module Reform::Form::ActiveModel 
     | 
|
| 
       29 
29 
     | 
    
         
             
                  return super unless params.is_a?(Hash)
         
     | 
| 
       30 
30 
     | 
    
         
             
                  # TODO: run this only for hash deserialization, but generically (#deserialize_hash ?).
         
     | 
| 
       31 
31 
     | 
    
         | 
| 
       32 
     | 
    
         
            -
                   
     | 
| 
       33 
     | 
    
         
            -
                    rename_nested_param_for!(params, attr)
         
     | 
| 
       34 
     | 
    
         
            -
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
                  self.class.representer { |dfn| rename_nested_param_for!(params, dfn) }
         
     | 
| 
       35 
33 
     | 
    
         | 
| 
       36 
34 
     | 
    
         
             
                  super
         
     | 
| 
       37 
35 
     | 
    
         
             
                end
         
     | 
| 
       38 
36 
     | 
    
         | 
| 
       39 
37 
     | 
    
         
             
              private
         
     | 
| 
       40 
     | 
    
         
            -
                def rename_nested_param_for!(params,  
     | 
| 
       41 
     | 
    
         
            -
                  nested_name = "#{ 
     | 
| 
      
 38 
     | 
    
         
            +
                def rename_nested_param_for!(params, dfn)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  nested_name = "#{dfn.name}_attributes"
         
     | 
| 
       42 
40 
     | 
    
         
             
                  return unless params.has_key?(nested_name)
         
     | 
| 
       43 
41 
     | 
    
         | 
| 
       44 
     | 
    
         
            -
                  value = params["#{ 
     | 
| 
       45 
     | 
    
         
            -
                  value = value.values if  
     | 
| 
      
 42 
     | 
    
         
            +
                  value = params["#{dfn.name}_attributes"]
         
     | 
| 
      
 43 
     | 
    
         
            +
                  value = value.values if dfn[:collection]
         
     | 
| 
       46 
44 
     | 
    
         | 
| 
       47 
     | 
    
         
            -
                  params[ 
     | 
| 
      
 45 
     | 
    
         
            +
                  params[dfn.name] = value
         
     | 
| 
       48 
46 
     | 
    
         
             
                end
         
     | 
| 
       49 
47 
     | 
    
         
             
              end # FormBuilderMethods
         
     | 
| 
       50 
48 
     | 
    
         |