formotion 1.0 → 1.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.
- data/CHANGELOG.md +9 -1
- data/LIST_OF_ROW_TYPES.md +226 -6
- data/README.md +21 -2
- data/app/app_delegate.rb +4 -3
- data/examples/KitchenSink/app/app_delegate.rb +67 -0
- data/examples/Persistence/.gitignore +5 -0
- data/examples/Persistence/README.md +3 -0
- data/examples/Persistence/Rakefile +9 -0
- data/examples/Persistence/app/app_delegate.rb +199 -0
- data/examples/Persistence/spec/main_spec.rb +9 -0
- data/gh-pages/assets/css/formotion.css +4 -0
- data/gh-pages/index.html +16 -0
- data/lib/formotion/controller/form_controller.rb +9 -1
- data/lib/formotion/form/form.rb +101 -3
- data/lib/formotion/form/form_delegate.rb +2 -0
- data/lib/formotion/model/formable.rb +15 -4
- data/lib/formotion/patch/object.rb +11 -1
- data/lib/formotion/patch/ui_text_view_placeholder.rb +8 -2
- data/lib/formotion/row/row.rb +48 -1
- data/lib/formotion/row_type/base.rb +5 -0
- data/lib/formotion/row_type/picker_row.rb +5 -1
- data/lib/formotion/row_type/subform_row.rb +37 -0
- data/lib/formotion/version.rb +1 -1
- data/spec/form/persist_spec.rb +58 -0
- data/spec/functional/formable_controller_spec.rb +9 -0
- data/spec/row_spec.rb +9 -0
- data/spec/support/ui_control_wrap_extension.rb +4 -2
- metadata +11 -4
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,7 +1,15 @@ | |
| 1 | 
            -
            ## 1.1 -  | 
| 1 | 
            +
            ## 1.1 - September 20, 2012
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            ### Features
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            -:display_key for subform row; when given, the value for that key in the subform's `#render` will be displayed alongside the chevron.
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            -`Formotion::Form.persist` and `:persist_as` to automatically synchronize your form to the disk.
         | 
| 2 8 |  | 
| 3 9 | 
             
            ### Bug Fixes
         | 
| 4 10 |  | 
| 11 | 
            +
            - Fix problems with inheriting Formable models.
         | 
| 12 | 
            +
             | 
| 5 13 | 
             
            ## 1.0 - August 11, 2012
         | 
| 6 14 |  | 
| 7 15 | 
             
            ### Features
         | 
    
        data/LIST_OF_ROW_TYPES.md
    CHANGED
    
    | @@ -2,6 +2,7 @@ | |
| 2 2 | 
             
            [String](#string)<br/>
         | 
| 3 3 | 
             
            [Text](#text)<br/>
         | 
| 4 4 | 
             
            [Email](#email)<br/>
         | 
| 5 | 
            +
            [Phone](#phone)<br/>
         | 
| 5 6 | 
             
            [Number](#number)<br/>
         | 
| 6 7 | 
             
            [Date](#date)
         | 
| 7 8 |  | 
| @@ -11,14 +12,49 @@ | |
| 11 12 | 
             
            [Check](#check)<br/>
         | 
| 12 13 | 
             
            [Slider](#slider)<br/>
         | 
| 13 14 | 
             
            [Image](#image)<br/>
         | 
| 14 | 
            -
            [ | 
| 15 | 
            +
            [Option](#option)<br/>
         | 
| 16 | 
            +
            [Picker](#picker)<br/>
         | 
| 17 | 
            +
            [Subform](#subform)<br/>
         | 
| 18 | 
            +
            [Template](#template)
         | 
| 15 19 |  | 
| 16 20 | 
             
            **Buttons**<br/>
         | 
| 17 | 
            -
            [Submit](#submit)
         | 
| 21 | 
            +
            [Submit](#submit)<br/>
         | 
| 22 | 
            +
            [Back](#back)<br/>
         | 
| 23 | 
            +
            [Edit](#edit)<br/>
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            ## General
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            All row types accept following properties:
         | 
| 28 | 
            +
            ```ruby
         | 
| 29 | 
            +
            {
         | 
| 30 | 
            +
              key:        :user_name,     # Unique identifier for the row
         | 
| 31 | 
            +
              value:      'Some Value',   # The initial value passed to the row
         | 
| 32 | 
            +
              title:      'Title',        # Title of the row
         | 
| 33 | 
            +
              subtitle:   'Subtitle',     # Subtitle of the row
         | 
| 34 | 
            +
              type:       :string,        # The type of row (string, phone, switch, etc)
         | 
| 35 | 
            +
              row_height: 100             # Height of the row
         | 
| 36 | 
            +
            }
         | 
| 37 | 
            +
            ```
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            The `type` property will define what kind of row type will get built. It looks for any Class within the `Formotion::RowType` namespace that has the constantized name (e.g. Formotion::RowType::StringRow).
         | 
| 40 | 
            +
             | 
| 18 41 |  | 
| 19 42 | 
             
            ## Character based
         | 
| 20 43 |  | 
| 44 | 
            +
            All character based row types accept following properties:
         | 
| 45 | 
            +
            ```ruby
         | 
| 46 | 
            +
            {
         | 
| 47 | 
            +
              placeholder:         'James Bond',     # Placeholder before a value exists
         | 
| 48 | 
            +
              auto_correction:     :no,              # Disable auto correction
         | 
| 49 | 
            +
              auto_capitalization: :none,            # Disable auto capitalization
         | 
| 50 | 
            +
              secure:              true,             # Enable secure input (Password)
         | 
| 51 | 
            +
              clear_button:        :while_editing,   # Enable clear button
         | 
| 52 | 
            +
              return_key:          :google,          # Define return key
         | 
| 53 | 
            +
            }
         | 
| 54 | 
            +
            ```
         | 
| 55 | 
            +
             | 
| 21 56 | 
             
            ### <a name="string"></a> String row
         | 
| 57 | 
            +
             | 
| 22 58 | 
             
            
         | 
| 23 59 | 
             
            ```ruby
         | 
| 24 60 | 
             
            {
         | 
| @@ -31,6 +67,9 @@ | |
| 31 67 | 
             
            }
         | 
| 32 68 | 
             
            ```
         | 
| 33 69 |  | 
| 70 | 
            +
            The `StringRow` is a simple text input row and opens a `UIKeyboardTypeDefault` keyboard when editing.
         | 
| 71 | 
            +
             | 
| 72 | 
            +
             | 
| 34 73 | 
             
            ### <a name="text"></a> Text row
         | 
| 35 74 | 
             
            
         | 
| 36 75 |  | 
| @@ -44,6 +83,10 @@ | |
| 44 83 | 
             
            }
         | 
| 45 84 | 
             
            ```
         | 
| 46 85 |  | 
| 86 | 
            +
            The `TextRow` is a multiline text input row and opens the default keyboard when editing.
         | 
| 87 | 
            +
            To define the height of the row set the property `row_height`.
         | 
| 88 | 
            +
             | 
| 89 | 
            +
             | 
| 47 90 | 
             
            ### <a name="email"></a> Email row
         | 
| 48 91 | 
             
            
         | 
| 49 92 | 
             
            ```ruby
         | 
| @@ -55,6 +98,23 @@ | |
| 55 98 | 
             
            }
         | 
| 56 99 | 
             
            ```
         | 
| 57 100 |  | 
| 101 | 
            +
            The `EmailRow` is a string input row for an email address and opens a `UIKeyboardTypeEmailAddress` keyboard when editing.
         | 
| 102 | 
            +
             | 
| 103 | 
            +
             | 
| 104 | 
            +
            ### <a name="phone"></a> Phone row
         | 
| 105 | 
            +
            
         | 
| 106 | 
            +
            ```ruby
         | 
| 107 | 
            +
            {
         | 
| 108 | 
            +
              title: "Phone",
         | 
| 109 | 
            +
              key: :phone,
         | 
| 110 | 
            +
              type: :phone,
         | 
| 111 | 
            +
              placeholder: '+01 877 412 7753'
         | 
| 112 | 
            +
            }
         | 
| 113 | 
            +
            ```
         | 
| 114 | 
            +
             | 
| 115 | 
            +
            The `PhoneRow` is a string input row for a phone number and opens a `UIKeyboardTypePhonePad` keyboard when editing.
         | 
| 116 | 
            +
             | 
| 117 | 
            +
             | 
| 58 118 | 
             
            ### <a name="number"></a> Number row
         | 
| 59 119 | 
             
            
         | 
| 60 120 |  | 
| @@ -67,6 +127,9 @@ | |
| 67 127 | 
             
            }
         | 
| 68 128 | 
             
            ```
         | 
| 69 129 |  | 
| 130 | 
            +
            The `NumberRow` is a string input row for a number and opens a `UIKeyboardTypeDecimalPad` keyboard when editing.
         | 
| 131 | 
            +
             | 
| 132 | 
            +
             | 
| 70 133 | 
             
            ### <a name="date"></a> Date row
         | 
| 71 134 | 
             
            
         | 
| 72 135 |  | 
| @@ -80,6 +143,19 @@ | |
| 80 143 | 
             
            }
         | 
| 81 144 | 
             
            ```
         | 
| 82 145 |  | 
| 146 | 
            +
            The `DateRow` is a string input row for a date and opens a `UIDatePicker` when editing.
         | 
| 147 | 
            +
            You can pass one of following formats as property:
         | 
| 148 | 
            +
            ```ruby
         | 
| 149 | 
            +
              :no       # NSDateFormatterNoStyle - Specifies no style.
         | 
| 150 | 
            +
              :short    # NSDateFormatterShortStyle - Specifies a short style, typically numeric only, such as “11/23/37” or “3:30pm”.
         | 
| 151 | 
            +
              :medium   # NSDateFormatterMediumStyle - Specifies a medium style, typically with abbreviated text, such as “Nov 23, 1937”.
         | 
| 152 | 
            +
              :long     # NSDateFormatterLongStyle - Specifies a long style, typically with full text, such as “November 23, 1937” or “3:30:32pm”.
         | 
| 153 | 
            +
              :full     # NSDateFormatterFullStyle -Specifies a full style with complete details, such as “Tuesday, April 12, 1952 AD” or “3:30:42pm PST”.
         | 
| 154 | 
            +
            ```
         | 
| 155 | 
            +
             | 
| 156 | 
            +
            The `value` is a `NSDate.timeIntervalSince1970` as an integer.
         | 
| 157 | 
            +
             | 
| 158 | 
            +
             | 
| 83 159 | 
             
            ## Other
         | 
| 84 160 |  | 
| 85 161 | 
             
            ### <a name="static"></a> Static row
         | 
| @@ -91,6 +167,9 @@ | |
| 91 167 | 
             
            }
         | 
| 92 168 | 
             
            ```
         | 
| 93 169 |  | 
| 170 | 
            +
            The `StaticRow` has no input funcitonality and therefore needs no `key` property. It is used for static text display like in Settings > General > About on your iOS device.
         | 
| 171 | 
            +
             | 
| 172 | 
            +
             | 
| 94 173 | 
             
            ### <a name="switch"></a> Switch row
         | 
| 95 174 | 
             
            
         | 
| 96 175 |  | 
| @@ -102,6 +181,9 @@ | |
| 102 181 | 
             
            }
         | 
| 103 182 | 
             
            ```
         | 
| 104 183 |  | 
| 184 | 
            +
            The `SwitchRow` is a on/off switch that has the `value` true or false.
         | 
| 185 | 
            +
             | 
| 186 | 
            +
             | 
| 105 187 | 
             
            ### <a name="check"></a> Check row
         | 
| 106 188 | 
             
            
         | 
| 107 189 |  | 
| @@ -114,6 +196,32 @@ | |
| 114 196 | 
             
            }
         | 
| 115 197 | 
             
            ```
         | 
| 116 198 |  | 
| 199 | 
            +
            The `CheckRow` is a row you can check and uncheck by tapping on it. It has either the `value` true or false.
         | 
| 200 | 
            +
            You can create a radio button style group by defining a section with `select_one: true` like this:
         | 
| 201 | 
            +
             | 
| 202 | 
            +
            ```ruby
         | 
| 203 | 
            +
            {
         | 
| 204 | 
            +
              title: "Account Type",
         | 
| 205 | 
            +
              key: :account_type,
         | 
| 206 | 
            +
              select_one: true,
         | 
| 207 | 
            +
              rows: [{
         | 
| 208 | 
            +
                title: "Free",
         | 
| 209 | 
            +
                key: :free,
         | 
| 210 | 
            +
                type: :check,
         | 
| 211 | 
            +
              }, {
         | 
| 212 | 
            +
                title: "Basic",
         | 
| 213 | 
            +
                value: true,
         | 
| 214 | 
            +
                key: :basic,
         | 
| 215 | 
            +
                type: :check,
         | 
| 216 | 
            +
              }, {
         | 
| 217 | 
            +
                title: "Pro",
         | 
| 218 | 
            +
                key: :pro,
         | 
| 219 | 
            +
                type: :check,
         | 
| 220 | 
            +
              }]
         | 
| 221 | 
            +
            }
         | 
| 222 | 
            +
            ```
         | 
| 223 | 
            +
             | 
| 224 | 
            +
             | 
| 117 225 | 
             
            ### <a name="slider"></a> Slider row
         | 
| 118 226 | 
             
            
         | 
| 119 227 |  | 
| @@ -127,6 +235,8 @@ | |
| 127 235 | 
             
            }
         | 
| 128 236 | 
             
            ```
         | 
| 129 237 |  | 
| 238 | 
            +
            The `SliderRow` takes a ruby range as `range` property that defines the min and max value of the slider.
         | 
| 239 | 
            +
             | 
| 130 240 | 
             
            ### <a name="image"></a> Image row
         | 
| 131 241 | 
             
            
         | 
| 132 242 |  | 
| @@ -143,10 +253,91 @@ | |
| 143 253 |  | 
| 144 254 | 
             
            ```ruby
         | 
| 145 255 | 
             
            {
         | 
| 146 | 
            -
              title: " | 
| 147 | 
            -
              key: : | 
| 256 | 
            +
              title: "Account Type",
         | 
| 257 | 
            +
              key: :account_type,
         | 
| 148 258 | 
             
              type: :options,
         | 
| 149 | 
            -
              items: [ | 
| 259 | 
            +
              items: ["Free", "Basic", "Pro"]
         | 
| 260 | 
            +
            }
         | 
| 261 | 
            +
            ```
         | 
| 262 | 
            +
             | 
| 263 | 
            +
             | 
| 264 | 
            +
            ### <a name="picker"></a> Picker row
         | 
| 265 | 
            +
            
         | 
| 266 | 
            +
             | 
| 267 | 
            +
            ```ruby
         | 
| 268 | 
            +
            {
         | 
| 269 | 
            +
              title: "Picker",
         | 
| 270 | 
            +
              key: :pick,
         | 
| 271 | 
            +
              type: :picker,
         | 
| 272 | 
            +
              items: ["Ruby", "Motion", "Rocks"],
         | 
| 273 | 
            +
              value: "Motion"
         | 
| 274 | 
            +
            }
         | 
| 275 | 
            +
            ```
         | 
| 276 | 
            +
             | 
| 277 | 
            +
             | 
| 278 | 
            +
            ### <a name="subform"></a> Subform row
         | 
| 279 | 
            +
            
         | 
| 280 | 
            +
             | 
| 281 | 
            +
            ```ruby
         | 
| 282 | 
            +
            {
         | 
| 283 | 
            +
              title: "Advanced",
         | 
| 284 | 
            +
              type: :subform,
         | 
| 285 | 
            +
              key: :advanced,
         | 
| 286 | 
            +
              subform: {
         | 
| 287 | 
            +
                title: "Advanced",
         | 
| 288 | 
            +
                sections: [{
         | 
| 289 | 
            +
                  title: "Advanced Settings",
         | 
| 290 | 
            +
                  rows: [{
         | 
| 291 | 
            +
                    title: "Server URL",
         | 
| 292 | 
            +
                    key: :server,
         | 
| 293 | 
            +
                    placeholder: "example.com",
         | 
| 294 | 
            +
                    type: :string,
         | 
| 295 | 
            +
                  }]
         | 
| 296 | 
            +
                }, {
         | 
| 297 | 
            +
                  rows: [{
         | 
| 298 | 
            +
                    title: 'Back',
         | 
| 299 | 
            +
                    type: :back
         | 
| 300 | 
            +
                  }]
         | 
| 301 | 
            +
                }]
         | 
| 302 | 
            +
              }
         | 
| 303 | 
            +
            }
         | 
| 304 | 
            +
            ```
         | 
| 305 | 
            +
             | 
| 306 | 
            +
            Use a `:display_key` to show the value of the subform in the row:
         | 
| 307 | 
            +
             | 
| 308 | 
            +
            
         | 
| 309 | 
            +
             | 
| 310 | 
            +
            ```ruby
         | 
| 311 | 
            +
            {
         | 
| 312 | 
            +
              title: "Subform",
         | 
| 313 | 
            +
              subtitle: "With display_key",
         | 
| 314 | 
            +
              type: :subform,
         | 
| 315 | 
            +
              display_key: :type,
         | 
| 316 | 
            +
              subform: {
         | 
| 317 | 
            +
                ...
         | 
| 318 | 
            +
              }
         | 
| 319 | 
            +
            }
         | 
| 320 | 
            +
            ```
         | 
| 321 | 
            +
             | 
| 322 | 
            +
            ### <a name="template"></a> Template row
         | 
| 323 | 
            +
            
         | 
| 324 | 
            +
             | 
| 325 | 
            +
            ```ruby
         | 
| 326 | 
            +
            {
         | 
| 327 | 
            +
              title: 'Your nicknames',
         | 
| 328 | 
            +
              rows: [{
         | 
| 329 | 
            +
                title: "Add nickname",
         | 
| 330 | 
            +
                key: :nicknames,
         | 
| 331 | 
            +
                type: :template,
         | 
| 332 | 
            +
                value: ['Nici', 'Sam'],
         | 
| 333 | 
            +
                template: {
         | 
| 334 | 
            +
                  title: 'Nickname',
         | 
| 335 | 
            +
                  type: :string,
         | 
| 336 | 
            +
                  placeholder: 'Enter here',
         | 
| 337 | 
            +
                  indented: true,
         | 
| 338 | 
            +
                  deletable: true
         | 
| 339 | 
            +
                }
         | 
| 340 | 
            +
              }]
         | 
| 150 341 | 
             
            }
         | 
| 151 342 | 
             
            ```
         | 
| 152 343 |  | 
| @@ -160,4 +351,33 @@ | |
| 160 351 | 
             
              title: "Submit",
         | 
| 161 352 | 
             
              type: :submit,
         | 
| 162 353 | 
             
            }
         | 
| 163 | 
            -
            ```
         | 
| 354 | 
            +
            ```
         | 
| 355 | 
            +
             | 
| 356 | 
            +
            The `SubmitRow` triggers the `form.submit` which triggers the defined `on_submit` callback.
         | 
| 357 | 
            +
             | 
| 358 | 
            +
             | 
| 359 | 
            +
            ### <a name="back"></a> Back row
         | 
| 360 | 
            +
            
         | 
| 361 | 
            +
             | 
| 362 | 
            +
            ```ruby
         | 
| 363 | 
            +
            {
         | 
| 364 | 
            +
              title: "Back",
         | 
| 365 | 
            +
              type: :back,
         | 
| 366 | 
            +
            }
         | 
| 367 | 
            +
            ```
         | 
| 368 | 
            +
             | 
| 369 | 
            +
            The `BackRow` is used in subforms to go back to the parent form.
         | 
| 370 | 
            +
             | 
| 371 | 
            +
             | 
| 372 | 
            +
            ### <a name="edit"></a> Edit row
         | 
| 373 | 
            +
            
         | 
| 374 | 
            +
             | 
| 375 | 
            +
            ```ruby
         | 
| 376 | 
            +
            {
         | 
| 377 | 
            +
              title: "Edit",
         | 
| 378 | 
            +
              alt_title: "View",
         | 
| 379 | 
            +
              type: :edit,
         | 
| 380 | 
            +
            }
         | 
| 381 | 
            +
            ```
         | 
| 382 | 
            +
             | 
| 383 | 
            +
            The `EditRow` is used to switch between edit and view mode. It's mainly used for the `TemplateRow`.
         | 
    
        data/README.md
    CHANGED
    
    | @@ -109,7 +109,7 @@ self.navigationController.pushViewController(@controller, animated: true) | |
| 109 109 |  | 
| 110 110 | 
             
            ### Data Types
         | 
| 111 111 |  | 
| 112 | 
            -
            See [the visual list of support row types](https://github.com/clayallsopp/formotion/ | 
| 112 | 
            +
            See [the visual list of support row types](https://github.com/clayallsopp/formotion/wiki/List-of-all-the-row-types).
         | 
| 113 113 |  | 
| 114 114 | 
             
            To add your own, check [the guide to adding new row types](https://github.com/clayallsopp/formotion/blob/master/NEW_ROW_TYPES.md).
         | 
| 115 115 |  | 
| @@ -155,6 +155,25 @@ end | |
| 155 155 |  | 
| 156 156 | 
             
            `form#submit` just triggers `form#on_submit`.
         | 
| 157 157 |  | 
| 158 | 
            +
            ### Persistence
         | 
| 159 | 
            +
             | 
| 160 | 
            +
            You can easily synchronize a `Form`'s state to disk using the `persist_as` key in conjunction with the `persist` method. When your user edits the form, any changes will be immediately saved. For example:
         | 
| 161 | 
            +
             | 
| 162 | 
            +
            ```ruby
         | 
| 163 | 
            +
            @form = Formotion::Form.persist({
         | 
| 164 | 
            +
              persist_as: :settings,
         | 
| 165 | 
            +
              sections: ...
         | 
| 166 | 
            +
            })
         | 
| 167 | 
            +
            ```
         | 
| 168 | 
            +
             | 
| 169 | 
            +
            This will load the form if it exists, or create it if necessary. If you use `Formotion::Form.new`, then the form will not be loaded and any changes you make will override existing saved forms.
         | 
| 170 | 
            +
             | 
| 171 | 
            +
            Your form values and state are automatically persisted across application loads, no save buttons needed.
         | 
| 172 | 
            +
             | 
| 173 | 
            +
            To reset the form, `persist` it and call `reset`, which restores it to the original state.
         | 
| 174 | 
            +
             | 
| 175 | 
            +
            View the [Persistence Example](./formotion/tree/master/examples/Persistence) to see it in action.
         | 
| 176 | 
            +
                 
         | 
| 158 177 | 
             
            ## Forking
         | 
| 159 178 |  | 
| 160 179 | 
             
            Feel free to fork and submit pull requests! And if you end up using Formotion in your app, I'd love to hear about your experience.
         | 
| @@ -162,4 +181,4 @@ Feel free to fork and submit pull requests! And if you end up using Formotion in | |
| 162 181 | 
             
            ## Todo
         | 
| 163 182 |  | 
| 164 183 | 
             
            - Not very efficient right now (creates a unique reuse idenitifer for each cell)
         | 
| 165 | 
            -
            - Styling/overriding the form for custom UITableViewDelegate/Data Source behaviors.
         | 
| 184 | 
            +
            - Styling/overriding the form for custom UITableViewDelegate/Data Source behaviors.
         | 
    
        data/app/app_delegate.rb
    CHANGED
    
    | @@ -68,6 +68,7 @@ class AppDelegate | |
| 68 68 | 
             
                      title: "Account type",
         | 
| 69 69 | 
             
                      type: :subform,
         | 
| 70 70 | 
             
                      key: :account,
         | 
| 71 | 
            +
                      display_key: :type,
         | 
| 71 72 | 
             
                      subform: {
         | 
| 72 73 | 
             
                        title: "Account Type",
         | 
| 73 74 | 
             
                        sections: [{
         | 
| @@ -75,16 +76,16 @@ class AppDelegate | |
| 75 76 | 
             
                          select_one: true,
         | 
| 76 77 | 
             
                          rows: [{
         | 
| 77 78 | 
             
                            title: "Free",
         | 
| 78 | 
            -
                            key: : | 
| 79 | 
            +
                            key: :Free,
         | 
| 79 80 | 
             
                            type: :check,
         | 
| 80 81 | 
             
                          }, {
         | 
| 81 82 | 
             
                            title: "Basic",
         | 
| 82 83 | 
             
                            value: true,
         | 
| 83 | 
            -
                            key: : | 
| 84 | 
            +
                            key: :Basic,
         | 
| 84 85 | 
             
                            type: :check,
         | 
| 85 86 | 
             
                          }, {
         | 
| 86 87 | 
             
                            title: "Pro",
         | 
| 87 | 
            -
                            key: : | 
| 88 | 
            +
                            key: :Pro,
         | 
| 88 89 | 
             
                            type: :check,
         | 
| 89 90 | 
             
                          }]
         | 
| 90 91 | 
             
                        }, {
         | 
| @@ -116,6 +116,73 @@ class AppDelegate | |
| 116 116 | 
             
                      key: :c,
         | 
| 117 117 | 
             
                      type: :check,
         | 
| 118 118 | 
             
                    }]
         | 
| 119 | 
            +
                  }, {
         | 
| 120 | 
            +
                    title: "Subforms",
         | 
| 121 | 
            +
                    rows: [{
         | 
| 122 | 
            +
                      title: "Subform",
         | 
| 123 | 
            +
                      subtitle: "With display_key",
         | 
| 124 | 
            +
                      type: :subform,
         | 
| 125 | 
            +
                      key: :subform,
         | 
| 126 | 
            +
                      display_key: :type,
         | 
| 127 | 
            +
                      subform: {
         | 
| 128 | 
            +
                        title: "Account Type",
         | 
| 129 | 
            +
                        sections: [{
         | 
| 130 | 
            +
                          key: :type,
         | 
| 131 | 
            +
                          select_one: true,
         | 
| 132 | 
            +
                          rows: [{
         | 
| 133 | 
            +
                            title: "Free",
         | 
| 134 | 
            +
                            key: :Free,
         | 
| 135 | 
            +
                            type: :check,
         | 
| 136 | 
            +
                          }, {
         | 
| 137 | 
            +
                            title: "Basic",
         | 
| 138 | 
            +
                            value: true,
         | 
| 139 | 
            +
                            key: :Basic,
         | 
| 140 | 
            +
                            type: :check,
         | 
| 141 | 
            +
                          }, {
         | 
| 142 | 
            +
                            title: "Pro",
         | 
| 143 | 
            +
                            key: :Pro,
         | 
| 144 | 
            +
                            type: :check,
         | 
| 145 | 
            +
                          }]
         | 
| 146 | 
            +
                        }, {
         | 
| 147 | 
            +
                          rows: [{
         | 
| 148 | 
            +
                            title: "Advanced",
         | 
| 149 | 
            +
                            type: :subform,
         | 
| 150 | 
            +
                            key: :advanced,
         | 
| 151 | 
            +
                            subform: {
         | 
| 152 | 
            +
                              title: "Advanced",
         | 
| 153 | 
            +
                              sections: [{
         | 
| 154 | 
            +
                                key: :currency,
         | 
| 155 | 
            +
                                select_one: true,
         | 
| 156 | 
            +
                                rows: [{
         | 
| 157 | 
            +
                                  title: "USD",
         | 
| 158 | 
            +
                                  value: true,
         | 
| 159 | 
            +
                                  key: :usd,
         | 
| 160 | 
            +
                                  type: :check,
         | 
| 161 | 
            +
                                }, {
         | 
| 162 | 
            +
                                  title: "EUR",
         | 
| 163 | 
            +
                                  key: :eur,
         | 
| 164 | 
            +
                                  type: :check,
         | 
| 165 | 
            +
                                }, {
         | 
| 166 | 
            +
                                  title: "CHF",
         | 
| 167 | 
            +
                                  key: :chf,
         | 
| 168 | 
            +
                                  type: :check,
         | 
| 169 | 
            +
                                }]
         | 
| 170 | 
            +
                              }, {
         | 
| 171 | 
            +
                                rows: [{
         | 
| 172 | 
            +
                                  title: 'Back',
         | 
| 173 | 
            +
                                  type: :back
         | 
| 174 | 
            +
                                }]
         | 
| 175 | 
            +
                              }]
         | 
| 176 | 
            +
                            }
         | 
| 177 | 
            +
                          }]
         | 
| 178 | 
            +
                        }, {
         | 
| 179 | 
            +
                          rows: [{
         | 
| 180 | 
            +
                            title: 'Back',
         | 
| 181 | 
            +
                            type: :back
         | 
| 182 | 
            +
                          }]
         | 
| 183 | 
            +
                        }]
         | 
| 184 | 
            +
                      }
         | 
| 185 | 
            +
                    }]
         | 
| 119 186 | 
             
                  }, {
         | 
| 120 187 | 
             
                    rows: [{
         | 
| 121 188 | 
             
                      title: "Submit",
         | 
| @@ -0,0 +1,199 @@ | |
| 1 | 
            +
            class AppDelegate
         | 
| 2 | 
            +
              def application(application, didFinishLaunchingWithOptions:launchOptions)
         | 
| 3 | 
            +
                @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                @form = Formotion::Form.persist({
         | 
| 6 | 
            +
                  title: "Persist Example",
         | 
| 7 | 
            +
                  persist_as: "example",
         | 
| 8 | 
            +
                  sections: [{
         | 
| 9 | 
            +
                    title: "Section Title",
         | 
| 10 | 
            +
                    footer: "This is the footer for the section. It's good for displaying detailed data about the section.",
         | 
| 11 | 
            +
                    rows: [{
         | 
| 12 | 
            +
                      title: "Static",
         | 
| 13 | 
            +
                      type: :static,
         | 
| 14 | 
            +
                    }, {
         | 
| 15 | 
            +
                      title: "Email",
         | 
| 16 | 
            +
                      key: :email,
         | 
| 17 | 
            +
                      placeholder: "me@mail.com",
         | 
| 18 | 
            +
                      type: :email,
         | 
| 19 | 
            +
                      auto_correction: :no,
         | 
| 20 | 
            +
                      auto_capitalization: :none
         | 
| 21 | 
            +
                    }, {
         | 
| 22 | 
            +
                      title: "Gender",
         | 
| 23 | 
            +
                      key: :gender,
         | 
| 24 | 
            +
                      type: :options,
         | 
| 25 | 
            +
                      items: ['Female', 'Male']
         | 
| 26 | 
            +
                    }, {
         | 
| 27 | 
            +
                      title: "Password",
         | 
| 28 | 
            +
                      key: :password,
         | 
| 29 | 
            +
                      placeholder: "required",
         | 
| 30 | 
            +
                      type: :string,
         | 
| 31 | 
            +
                      secure: true
         | 
| 32 | 
            +
                    }, {
         | 
| 33 | 
            +
                      title: "Phone",
         | 
| 34 | 
            +
                      key: :phone,
         | 
| 35 | 
            +
                      placeholder: "555-555-5555",
         | 
| 36 | 
            +
                      type: :phone,
         | 
| 37 | 
            +
                      auto_correction: :no,
         | 
| 38 | 
            +
                      auto_capitalization: :none
         | 
| 39 | 
            +
                    }, {
         | 
| 40 | 
            +
                      title: "Number",
         | 
| 41 | 
            +
                      key: :number,
         | 
| 42 | 
            +
                      placeholder: "12345",
         | 
| 43 | 
            +
                      type: :number,
         | 
| 44 | 
            +
                      auto_correction: :no,
         | 
| 45 | 
            +
                      auto_capitalization: :none
         | 
| 46 | 
            +
                    }, {
         | 
| 47 | 
            +
                      title: "Subtitle",
         | 
| 48 | 
            +
                      subtitle: "Confirmation",
         | 
| 49 | 
            +
                      key: :confirm,
         | 
| 50 | 
            +
                      placeholder: "required",
         | 
| 51 | 
            +
                      type: :string,
         | 
| 52 | 
            +
                      secure: true
         | 
| 53 | 
            +
                    }, {
         | 
| 54 | 
            +
                      title: "Row Height",
         | 
| 55 | 
            +
                      key: :row_height,
         | 
| 56 | 
            +
                      placeholder: "60px",
         | 
| 57 | 
            +
                      type: :string,
         | 
| 58 | 
            +
                      row_height: 60
         | 
| 59 | 
            +
                    }, {
         | 
| 60 | 
            +
                      title: "Text",
         | 
| 61 | 
            +
                      key: :text,
         | 
| 62 | 
            +
                      type: :text,
         | 
| 63 | 
            +
                      placeholder: "Enter your text here",
         | 
| 64 | 
            +
                      row_height: 100
         | 
| 65 | 
            +
                    }, {
         | 
| 66 | 
            +
                      title: "Check",
         | 
| 67 | 
            +
                      key: :check,
         | 
| 68 | 
            +
                      type: :check,
         | 
| 69 | 
            +
                      value: true
         | 
| 70 | 
            +
                    }, {
         | 
| 71 | 
            +
                      title: "Remember?",
         | 
| 72 | 
            +
                      key: :remember,
         | 
| 73 | 
            +
                      type: :switch,
         | 
| 74 | 
            +
                    }, {
         | 
| 75 | 
            +
                      title: "Date Full",
         | 
| 76 | 
            +
                      subtitle: "w/ :value",
         | 
| 77 | 
            +
                      value: 326937600,
         | 
| 78 | 
            +
                      key: :date_long,
         | 
| 79 | 
            +
                      type: :date,
         | 
| 80 | 
            +
                      format: :full
         | 
| 81 | 
            +
                    }, {
         | 
| 82 | 
            +
                      title: "Date Medium",
         | 
| 83 | 
            +
                      subtitle: "w/ :value",
         | 
| 84 | 
            +
                      value: 1341273600,
         | 
| 85 | 
            +
                      key: :date_medium,
         | 
| 86 | 
            +
                      type: :date,
         | 
| 87 | 
            +
                      format: :medium
         | 
| 88 | 
            +
                    }, {
         | 
| 89 | 
            +
                      title: "Date Short",
         | 
| 90 | 
            +
                      subtitle: "w/ :placeholder",
         | 
| 91 | 
            +
                      placeholder: "DOB",
         | 
| 92 | 
            +
                      key: :date_short,
         | 
| 93 | 
            +
                      type: :date,
         | 
| 94 | 
            +
                      format: :short
         | 
| 95 | 
            +
                    }, {
         | 
| 96 | 
            +
                      title: "Slider",
         | 
| 97 | 
            +
                      key: :slider,
         | 
| 98 | 
            +
                      type: :slider,
         | 
| 99 | 
            +
                      range: (1..100),
         | 
| 100 | 
            +
                      value: 25
         | 
| 101 | 
            +
                    }]
         | 
| 102 | 
            +
                  }, {
         | 
| 103 | 
            +
                    title: "Subforms",
         | 
| 104 | 
            +
                    rows: [{
         | 
| 105 | 
            +
                      title: "Subform",
         | 
| 106 | 
            +
                      subtitle: "With display_key",
         | 
| 107 | 
            +
                      type: :subform,
         | 
| 108 | 
            +
                      key: :subform,
         | 
| 109 | 
            +
                      display_key: :type,
         | 
| 110 | 
            +
                      subform: {
         | 
| 111 | 
            +
                        title: "Account Type",
         | 
| 112 | 
            +
                        sections: [{
         | 
| 113 | 
            +
                          key: :type,
         | 
| 114 | 
            +
                          select_one: true,
         | 
| 115 | 
            +
                          rows: [{
         | 
| 116 | 
            +
                            title: "Free",
         | 
| 117 | 
            +
                            key: :Free,
         | 
| 118 | 
            +
                            type: :check,
         | 
| 119 | 
            +
                          }, {
         | 
| 120 | 
            +
                            title: "Basic",
         | 
| 121 | 
            +
                            value: true,
         | 
| 122 | 
            +
                            key: :Basic,
         | 
| 123 | 
            +
                            type: :check,
         | 
| 124 | 
            +
                          }, {
         | 
| 125 | 
            +
                            title: "Pro",
         | 
| 126 | 
            +
                            key: :Pro,
         | 
| 127 | 
            +
                            type: :check,
         | 
| 128 | 
            +
                          }]
         | 
| 129 | 
            +
                        }, {
         | 
| 130 | 
            +
                          rows: [{
         | 
| 131 | 
            +
                            title: "Advanced",
         | 
| 132 | 
            +
                            type: :subform,
         | 
| 133 | 
            +
                            key: :advanced,
         | 
| 134 | 
            +
                            subform: {
         | 
| 135 | 
            +
                              title: "Advanced",
         | 
| 136 | 
            +
                              sections: [{
         | 
| 137 | 
            +
                                key: :currency,
         | 
| 138 | 
            +
                                select_one: true,
         | 
| 139 | 
            +
                                rows: [{
         | 
| 140 | 
            +
                                  title: "USD",
         | 
| 141 | 
            +
                                  value: true,
         | 
| 142 | 
            +
                                  key: :usd,
         | 
| 143 | 
            +
                                  type: :check,
         | 
| 144 | 
            +
                                }, {
         | 
| 145 | 
            +
                                  title: "EUR",
         | 
| 146 | 
            +
                                  key: :eur,
         | 
| 147 | 
            +
                                  type: :check,
         | 
| 148 | 
            +
                                }, {
         | 
| 149 | 
            +
                                  title: "CHF",
         | 
| 150 | 
            +
                                  key: :chf,
         | 
| 151 | 
            +
                                  type: :check,
         | 
| 152 | 
            +
                                }]
         | 
| 153 | 
            +
                              }, {
         | 
| 154 | 
            +
                                rows: [{
         | 
| 155 | 
            +
                                  title: 'Back',
         | 
| 156 | 
            +
                                  type: :back
         | 
| 157 | 
            +
                                }]
         | 
| 158 | 
            +
                              }]
         | 
| 159 | 
            +
                            }
         | 
| 160 | 
            +
                          }]
         | 
| 161 | 
            +
                        }, {
         | 
| 162 | 
            +
                          rows: [{
         | 
| 163 | 
            +
                            title: 'Back',
         | 
| 164 | 
            +
                            type: :back
         | 
| 165 | 
            +
                          }]
         | 
| 166 | 
            +
                        }]
         | 
| 167 | 
            +
                      }
         | 
| 168 | 
            +
                    }]
         | 
| 169 | 
            +
                  }]
         | 
| 170 | 
            +
                })
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                @view_controller = Formotion::FormController.alloc.initWithForm(@form)
         | 
| 173 | 
            +
                @view_controller.form.on_submit do |form|
         | 
| 174 | 
            +
                  form.active_row && form.active_row.text_field.resignFirstResponder
         | 
| 175 | 
            +
                  alert = UIAlertView.alloc.init
         | 
| 176 | 
            +
                  alert.title = "@form.render"
         | 
| 177 | 
            +
                  alert.message = @form.render.to_s
         | 
| 178 | 
            +
                  alert.addButtonWithTitle("OK")
         | 
| 179 | 
            +
                  alert.show
         | 
| 180 | 
            +
                end
         | 
| 181 | 
            +
             | 
| 182 | 
            +
                @view_controller.navigationItem.leftBarButtonItem = UIBarButtonItem.alloc.initWithTitle("Reset", style: UIBarButtonItemStyleBordered, target:self, action:'reset_form')
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                @navigation_controller = UINavigationController.alloc.initWithRootViewController(@view_controller)
         | 
| 185 | 
            +
             | 
| 186 | 
            +
                @window.rootViewController = @navigation_controller
         | 
| 187 | 
            +
                @window.makeKeyAndVisible
         | 
| 188 | 
            +
             | 
| 189 | 
            +
                true
         | 
| 190 | 
            +
              end
         | 
| 191 | 
            +
             | 
| 192 | 
            +
              def submit
         | 
| 193 | 
            +
                @form.submit
         | 
| 194 | 
            +
              end
         | 
| 195 | 
            +
             | 
| 196 | 
            +
              def reset_form
         | 
| 197 | 
            +
                @form.reset
         | 
| 198 | 
            +
              end
         | 
| 199 | 
            +
            end
         | 
    
        data/gh-pages/index.html
    CHANGED
    
    | @@ -199,6 +199,22 @@ | |
| 199 199 |  | 
| 200 200 | 
             
                  <hr />
         | 
| 201 201 |  | 
| 202 | 
            +
                  <div class="row">
         | 
| 203 | 
            +
                    <div class="span12" id="maintained">
         | 
| 204 | 
            +
                      <h1>Maintained By</h1>
         | 
| 205 | 
            +
                    </div>
         | 
| 206 | 
            +
                    <div class="span3">
         | 
| 207 | 
            +
                      <a href="https://twitter.com/clayallsopp" class="twitter-follow-button" data-show-count="false" data-size="large">Follow @clayallsopp</a>
         | 
| 208 | 
            +
            <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
         | 
| 209 | 
            +
                    </div>
         | 
| 210 | 
            +
                    <div class="span3">
         | 
| 211 | 
            +
                      <a href="https://twitter.com/mordaroso" class="twitter-follow-button" data-show-count="false" data-size="large">Follow @mordaroso</a>
         | 
| 212 | 
            +
            <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
         | 
| 213 | 
            +
                    </div>
         | 
| 214 | 
            +
                  </div>
         | 
| 215 | 
            +
             | 
| 216 | 
            +
                  <hr />
         | 
| 217 | 
            +
             | 
| 202 218 |  | 
| 203 219 | 
             
                </div> <!-- /container -->
         | 
| 204 220 | 
             
              </body>
         | 
| @@ -17,6 +17,7 @@ module Formotion | |
| 17 17 | 
             
                  self.form = form
         | 
| 18 18 | 
             
                  #self.view.setEditing true, animated: true
         | 
| 19 19 | 
             
                  self.tableView.allowsSelectionDuringEditing = true
         | 
| 20 | 
            +
             | 
| 20 21 | 
             
                  self
         | 
| 21 22 | 
             
                end
         | 
| 22 23 |  | 
| @@ -49,6 +50,13 @@ module Formotion | |
| 49 50 | 
             
                  @form.controller = self
         | 
| 50 51 | 
             
                end
         | 
| 51 52 |  | 
| 53 | 
            +
             | 
| 54 | 
            +
                def viewWillAppear(animated)
         | 
| 55 | 
            +
                  super
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  self.tableView.reloadData
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 52 60 | 
             
                # Subview Methods
         | 
| 53 61 | 
             
                def push_subform(form)
         | 
| 54 62 | 
             
                  @subform_controller = self.class.alloc.initWithForm(form)
         | 
| @@ -68,4 +76,4 @@ module Formotion | |
| 68 76 | 
             
                  end
         | 
| 69 77 | 
             
                end
         | 
| 70 78 | 
             
              end
         | 
| 71 | 
            -
            end
         | 
| 79 | 
            +
            end
         | 
    
        data/lib/formotion/form/form.rb
    CHANGED
    
    | @@ -1,11 +1,16 @@ | |
| 1 1 | 
             
            module Formotion
         | 
| 2 2 | 
             
              class Form < Formotion::Base
         | 
| 3 | 
            +
                extend BW::KVO
         | 
| 4 | 
            +
                include BW::KVO
         | 
| 5 | 
            +
             | 
| 3 6 | 
             
                PROPERTIES = [
         | 
| 4 7 | 
             
                  # By default, Formotion::Controller will set it's title to this
         | 
| 5 8 | 
             
                  # (so navigation bars will reflect it).
         | 
| 6 9 | 
             
                  :title,
         | 
| 7 10 | 
             
                  # If you want to have some internal id to track the form.
         | 
| 8 | 
            -
                  :id
         | 
| 11 | 
            +
                  :id,
         | 
| 12 | 
            +
                  # [STRING/SYMBOL] used to store your form's state on disk
         | 
| 13 | 
            +
                  :persist_as
         | 
| 9 14 | 
             
                ]
         | 
| 10 15 | 
             
                PROPERTIES.each {|prop|
         | 
| 11 16 | 
             
                  attr_accessor prop
         | 
| @@ -15,6 +20,12 @@ module Formotion | |
| 15 20 | 
             
                # them to be pased in the hash
         | 
| 16 21 | 
             
                SERIALIZE_PROPERTIES = PROPERTIES + [:sections]
         | 
| 17 22 |  | 
| 23 | 
            +
                def self.persist(params = {})
         | 
| 24 | 
            +
                  form = new(params)
         | 
| 25 | 
            +
                  form.open && form.save
         | 
| 26 | 
            +
                  form
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 18 29 | 
             
                def initialize(params = {})
         | 
| 19 30 | 
             
                  # super takes care of initializing the ::PROPERTIES in params
         | 
| 20 31 | 
             
                  super
         | 
| @@ -104,7 +115,6 @@ module Formotion | |
| 104 115 | 
             
                # as shown above.
         | 
| 105 116 | 
             
                def submit
         | 
| 106 117 | 
             
                  if @on_submit_callback.nil?
         | 
| 107 | 
            -
                    p "`Form#submit` invoked without a callback, doing nothing."
         | 
| 108 118 | 
             
                    return
         | 
| 109 119 | 
             
                  end
         | 
| 110 120 |  | 
| @@ -184,7 +194,95 @@ module Formotion | |
| 184 194 | 
             
                  kv
         | 
| 185 195 | 
             
                end
         | 
| 186 196 |  | 
| 197 | 
            +
                #########################
         | 
| 198 | 
            +
                # Persisting Forms
         | 
| 199 | 
            +
             | 
| 200 | 
            +
                # loads the given settings into the the form, and
         | 
| 201 | 
            +
                # places observers to save on changes
         | 
| 202 | 
            +
                def open
         | 
| 203 | 
            +
                  @form_observer ||= lambda { |form, saved_form|
         | 
| 204 | 
            +
                    form.send(:each_row) do |row, index|
         | 
| 205 | 
            +
                      s_index = row.section.index
         | 
| 206 | 
            +
                      temp_row = saved_form.sections[s_index].rows[index]
         | 
| 207 | 
            +
             | 
| 208 | 
            +
                      if row.subform?
         | 
| 209 | 
            +
                        saved_subform = temp_row.subform.to_form
         | 
| 210 | 
            +
                        @form_observer.call(row.subform.to_form, saved_subform)
         | 
| 211 | 
            +
                      else
         | 
| 212 | 
            +
                        row.value = temp_row.value
         | 
| 213 | 
            +
             | 
| 214 | 
            +
                        observe(row, "value") do |old_value, new_value|
         | 
| 215 | 
            +
                          self.save
         | 
| 216 | 
            +
                        end
         | 
| 217 | 
            +
                      end
         | 
| 218 | 
            +
                    end
         | 
| 219 | 
            +
                  }
         | 
| 220 | 
            +
             | 
| 221 | 
            +
                  saved_hash = load_state
         | 
| 222 | 
            +
                  @form_observer.call(self, Formotion::Form.new(saved_hash))
         | 
| 223 | 
            +
                end
         | 
| 224 | 
            +
             | 
| 225 | 
            +
                # places hash of values into application persistance
         | 
| 226 | 
            +
                def save
         | 
| 227 | 
            +
                  App::Persistence[persist_key] = to_hash.to_archived_data
         | 
| 228 | 
            +
                  App::Persistence[original_persist_key] ||= self.to_hash.to_archived_data
         | 
| 229 | 
            +
                end
         | 
| 230 | 
            +
             | 
| 231 | 
            +
                def reset
         | 
| 232 | 
            +
                  App::Persistence[persist_key] = nil
         | 
| 233 | 
            +
             | 
| 234 | 
            +
                  @form_resetter ||= lambda { |form, original_form|
         | 
| 235 | 
            +
                    form.send(:each_row) do |row, index|
         | 
| 236 | 
            +
                      s_index = row.section.index
         | 
| 237 | 
            +
                      temp_row = original_form.sections[s_index].rows[index]
         | 
| 238 | 
            +
             | 
| 239 | 
            +
                      if row.subform?
         | 
| 240 | 
            +
                        original_subform = temp_row.subform.to_form
         | 
| 241 | 
            +
                        @form_resetter.call(row.subform.to_form, original_subform)
         | 
| 242 | 
            +
                      else
         | 
| 243 | 
            +
                        row.value = temp_row.value
         | 
| 244 | 
            +
                      end
         | 
| 245 | 
            +
                    end
         | 
| 246 | 
            +
                  }
         | 
| 247 | 
            +
             | 
| 248 | 
            +
                  temp_form = Formotion::Form.new(App::Persistence[original_persist_key].unarchive)
         | 
| 249 | 
            +
                  @form_resetter.call(self, temp_form)
         | 
| 250 | 
            +
             | 
| 251 | 
            +
                  self.save
         | 
| 252 | 
            +
                end
         | 
| 253 | 
            +
             | 
| 187 254 | 
             
                private
         | 
| 255 | 
            +
                def persist_key
         | 
| 256 | 
            +
                  "FORMOTION_#{self.persist_as}"
         | 
| 257 | 
            +
                end
         | 
| 258 | 
            +
             | 
| 259 | 
            +
                def original_persist_key
         | 
| 260 | 
            +
                  "#{persist_key}_ORIGINAL"
         | 
| 261 | 
            +
                end
         | 
| 262 | 
            +
             | 
| 263 | 
            +
                def load_state
         | 
| 264 | 
            +
                  begin
         | 
| 265 | 
            +
                    state = App::Persistence[persist_key] && App::Persistence[persist_key].unarchive
         | 
| 266 | 
            +
                  rescue
         | 
| 267 | 
            +
                    self.save
         | 
| 268 | 
            +
                  end
         | 
| 269 | 
            +
                  
         | 
| 270 | 
            +
                  state ||= self.to_hash
         | 
| 271 | 
            +
                end
         | 
| 272 | 
            +
             | 
| 273 | 
            +
                def each_row(&block)
         | 
| 274 | 
            +
                  self.sections.each_with_index do |section, s_index|
         | 
| 275 | 
            +
                    section.rows.each_with_index do |row, index|
         | 
| 276 | 
            +
                      case block.arity
         | 
| 277 | 
            +
                      when 1
         | 
| 278 | 
            +
                        block.call(row)
         | 
| 279 | 
            +
                      when 2
         | 
| 280 | 
            +
                        block.call(row, index)
         | 
| 281 | 
            +
                      end
         | 
| 282 | 
            +
                    end
         | 
| 283 | 
            +
                  end
         | 
| 284 | 
            +
                end
         | 
| 285 | 
            +
             | 
| 188 286 | 
             
                def recursive_delete_nil(h)
         | 
| 189 287 | 
             
                  delete_empty = Proc.new { |k, v|
         | 
| 190 288 | 
             
                    v.delete_if(&delete_empty) if v.kind_of?(Hash)
         | 
| @@ -193,4 +291,4 @@ module Formotion | |
| 193 291 | 
             
                  h.delete_if &delete_empty
         | 
| 194 292 | 
             
                end
         | 
| 195 293 | 
             
              end
         | 
| 196 | 
            -
            end
         | 
| 294 | 
            +
            end
         | 
| @@ -5,6 +5,21 @@ module Formotion | |
| 5 5 | 
             
                end
         | 
| 6 6 |  | 
| 7 7 | 
             
                module ClassMethods
         | 
| 8 | 
            +
                  INHERITABLE_ATTRIBUTES = [:form_properties, :form_title].each do |prop|
         | 
| 9 | 
            +
                    attr_accessor prop
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def inherited(subclass)
         | 
| 13 | 
            +
                    INHERITABLE_ATTRIBUTES.each do |inheritable_attribute|
         | 
| 14 | 
            +
                      instance_var = "@#{inheritable_attribute}"
         | 
| 15 | 
            +
                      subclass.instance_variable_set(instance_var, instance_variable_get(instance_var))
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  def form_properties
         | 
| 20 | 
            +
                    @form_properties ||= []
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 8 23 | 
             
                  # Relates a property to a RowType.
         | 
| 9 24 | 
             
                  # @param property is the name of the attribute to KVO
         | 
| 10 25 | 
             
                  # @param row_type is the Formotion::RowType to use for that attribute
         | 
| @@ -19,10 +34,6 @@ module Formotion | |
| 19 34 | 
             
                    self.form_properties << { property: property, row_type: row_type}.merge(options)
         | 
| 20 35 | 
             
                  end
         | 
| 21 36 |  | 
| 22 | 
            -
                  def form_properties
         | 
| 23 | 
            -
                    @form_properties ||= []
         | 
| 24 | 
            -
                  end
         | 
| 25 | 
            -
             | 
| 26 37 | 
             
                  # Sets the top bar title for this model
         | 
| 27 38 | 
             
                  # EX
         | 
| 28 39 | 
             
                  # form_title "Some Settings"
         | 
| @@ -6,4 +6,14 @@ class Object | |
| 6 6 | 
             
                self.class.send(:alias_method, "old_#{method.to_s}".to_sym, method)
         | 
| 7 7 | 
             
                self.instance_eval &block
         | 
| 8 8 | 
             
              end
         | 
| 9 | 
            -
             | 
| 9 | 
            +
             | 
| 10 | 
            +
              def to_archived_data
         | 
| 11 | 
            +
                NSKeyedArchiver.archivedDataWithRootObject(self)
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
            end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            class NSData
         | 
| 16 | 
            +
              def unarchive
         | 
| 17 | 
            +
                NSKeyedUnarchiver.unarchiveObjectWithData(self)
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
            end
         | 
| @@ -19,7 +19,13 @@ class UITextView | |
| 19 19 | 
             
              def setup
         | 
| 20 20 | 
             
                @foreground_observer = NSNotificationCenter.defaultCenter.observe UITextViewTextDidChangeNotification do |notification|
         | 
| 21 21 | 
             
                  updateShouldDrawPlaceholder
         | 
| 22 | 
            -
                end | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              alias_method :old_dealloc, :dealloc
         | 
| 26 | 
            +
              def dealloc
         | 
| 27 | 
            +
                NSNotificationCenter.defaultCenter.unobserve @foreground_observer
         | 
| 28 | 
            +
                old_dealloc
         | 
| 23 29 | 
             
              end
         | 
| 24 30 |  | 
| 25 31 | 
             
              alias_method :old_drawRect, :drawRect
         | 
| @@ -62,4 +68,4 @@ class UITextView | |
| 62 68 |  | 
| 63 69 | 
             
                self.setNeedsDisplay if (prev != @shouldDrawPlaceholder)
         | 
| 64 70 | 
             
              end
         | 
| 65 | 
            -
            end
         | 
| 71 | 
            +
            end
         | 
    
        data/lib/formotion/row/row.rb
    CHANGED
    
    | @@ -53,6 +53,11 @@ module Formotion | |
| 53 53 | 
             
                  # A hash for a Form used for subforms
         | 
| 54 54 | 
             
                  # DEFAULT is nil
         | 
| 55 55 | 
             
                  :subform,
         | 
| 56 | 
            +
                  # Used in a subform row; when given,
         | 
| 57 | 
            +
                  # will display the value of the matching key
         | 
| 58 | 
            +
                  # of the subform's rendering.
         | 
| 59 | 
            +
                  # DEFAULT is nil
         | 
| 60 | 
            +
                  :display_key,
         | 
| 56 61 | 
             
                  # A hash for a Row used for templates
         | 
| 57 62 | 
             
                  # DEFAULT is nil
         | 
| 58 63 | 
             
                  :template,
         | 
| @@ -160,6 +165,19 @@ module Formotion | |
| 160 165 | 
             
                  object.button?
         | 
| 161 166 | 
             
                end
         | 
| 162 167 |  | 
| 168 | 
            +
                def subform?
         | 
| 169 | 
            +
                  self.type == :subform
         | 
| 170 | 
            +
                end
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                #########################
         | 
| 173 | 
            +
                #  getter overrides
         | 
| 174 | 
            +
                def items
         | 
| 175 | 
            +
                  if @items.respond_to?(:call)
         | 
| 176 | 
            +
                    @items = @items.call
         | 
| 177 | 
            +
                  end
         | 
| 178 | 
            +
                  @items
         | 
| 179 | 
            +
                end
         | 
| 180 | 
            +
             | 
| 163 181 | 
             
                #########################
         | 
| 164 182 | 
             
                #  setter overrides
         | 
| 165 183 | 
             
                def type=(type)
         | 
| @@ -167,6 +185,20 @@ module Formotion | |
| 167 185 | 
             
                  @type = type
         | 
| 168 186 | 
             
                end
         | 
| 169 187 |  | 
| 188 | 
            +
                def range=(range)
         | 
| 189 | 
            +
                  if range
         | 
| 190 | 
            +
                    case range
         | 
| 191 | 
            +
                    when Range
         | 
| 192 | 
            +
                      # all good
         | 
| 193 | 
            +
                    when Array
         | 
| 194 | 
            +
                      range = Range.new(range[0], range[1])
         | 
| 195 | 
            +
                    else
         | 
| 196 | 
            +
                      raise Formotion::InvalidClassError, "Attempted Row.range = #{range.inspect} should be of type Range or Array"
         | 
| 197 | 
            +
                    end
         | 
| 198 | 
            +
                  end
         | 
| 199 | 
            +
                  @range = range
         | 
| 200 | 
            +
                end
         | 
| 201 | 
            +
             | 
| 170 202 | 
             
                def return_key=(value)
         | 
| 171 203 | 
             
                  @return_key = const_int_get("UIReturnKey", value)
         | 
| 172 204 | 
             
                end
         | 
| @@ -205,10 +237,24 @@ module Formotion | |
| 205 237 | 
             
                  cell
         | 
| 206 238 | 
             
                end
         | 
| 207 239 |  | 
| 240 | 
            +
                # Called on every tableView:cellForRowAtIndexPath:
         | 
| 241 | 
            +
                # so keep implementation details minimal
         | 
| 242 | 
            +
                def update_cell(cell)
         | 
| 243 | 
            +
                  self.object.update_cell(cell)
         | 
| 244 | 
            +
                  cell
         | 
| 245 | 
            +
                end
         | 
| 246 | 
            +
             | 
| 208 247 | 
             
                #########################
         | 
| 209 248 | 
             
                # Retreiving data
         | 
| 210 249 | 
             
                def to_hash
         | 
| 211 | 
            -
                  super
         | 
| 250 | 
            +
                  h = super
         | 
| 251 | 
            +
                  if h[:range] && h[:range].is_a?(Range)
         | 
| 252 | 
            +
                    h[:range] = [self.range.begin, self.range.end]
         | 
| 253 | 
            +
                  end
         | 
| 254 | 
            +
                  if subform?
         | 
| 255 | 
            +
                    h[:subform] = self.subform.to_form.to_hash
         | 
| 256 | 
            +
                  end
         | 
| 257 | 
            +
                  h
         | 
| 212 258 | 
             
                end
         | 
| 213 259 |  | 
| 214 260 | 
             
                def subform=(subform)
         | 
| @@ -225,6 +271,7 @@ module Formotion | |
| 225 271 | 
             
                      @hash_subform ||= self
         | 
| 226 272 | 
             
                    end
         | 
| 227 273 | 
             
                  end
         | 
| 274 | 
            +
                  @subform
         | 
| 228 275 | 
             
                end
         | 
| 229 276 |  | 
| 230 277 | 
             
                private
         | 
| @@ -43,6 +43,11 @@ module Formotion | |
| 43 43 | 
             
                  def after_build(cell)
         | 
| 44 44 | 
             
                  end
         | 
| 45 45 |  | 
| 46 | 
            +
                  # Called on every tableView:cellForRowAtIndexPath:
         | 
| 47 | 
            +
                  # so keep implementation details minimal
         | 
| 48 | 
            +
                  def update_cell(cell)
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 46 51 | 
             
                  # method gets triggered when tableView(tableView, didSelectRowAtIndexPath:indexPath)
         | 
| 47 52 | 
             
                  # in UITableViewDelegate is executed
         | 
| 48 53 | 
             
                  def on_select(tableView, tableViewDelegate)
         | 
| @@ -18,7 +18,11 @@ module Formotion | |
| 18 18 |  | 
| 19 19 | 
             
                      if self.row.value
         | 
| 20 20 | 
             
                        picker_row = self.row.items.index(row.value)
         | 
| 21 | 
            -
                         | 
| 21 | 
            +
                        if picker_row
         | 
| 22 | 
            +
                          picker.selectRow(picker_row, inComponent:0, animated:false)
         | 
| 23 | 
            +
                        else
         | 
| 24 | 
            +
                          warn "Picker item '#{row.value}' not found in #{row.items.inspect} for '#{row.key}'"
         | 
| 25 | 
            +
                        end
         | 
| 22 26 | 
             
                      end
         | 
| 23 27 |  | 
| 24 28 | 
             
                      picker
         | 
| @@ -2,8 +2,36 @@ module Formotion | |
| 2 2 | 
             
              module RowType
         | 
| 3 3 | 
             
                class SubformRow < Base
         | 
| 4 4 |  | 
| 5 | 
            +
                  LABEL_TAG=1001
         | 
| 6 | 
            +
             | 
| 5 7 | 
             
                  def build_cell(cell)
         | 
| 6 8 | 
             
                    cell.accessoryType = cell.editingAccessoryType = UITableViewCellAccessoryDisclosureIndicator
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    cell.contentView.addSubview(self.display_key_label)
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                    cell.swizzle(:layoutSubviews) do
         | 
| 13 | 
            +
                      def layoutSubviews
         | 
| 14 | 
            +
                        old_layoutSubviews
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                        formotion_label = self.viewWithTag(LABEL_TAG)
         | 
| 17 | 
            +
                        formotion_label.sizeToFit
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                        field_frame = formotion_label.frame
         | 
| 20 | 
            +
                        # HARDCODED CONSTANT
         | 
| 21 | 
            +
                        field_frame.origin.x = self.contentView.frame.size.width - field_frame.size.width - 10
         | 
| 22 | 
            +
                        field_frame.origin.y = ((self.contentView.frame.size.height - field_frame.size.height) / 2.0).round
         | 
| 23 | 
            +
                        formotion_label.frame = field_frame
         | 
| 24 | 
            +
                      end
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    display_key_label.highlightedTextColor = cell.textLabel.highlightedTextColor
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  def update_cell(cell)
         | 
| 31 | 
            +
                    subform = row.subform.to_form
         | 
| 32 | 
            +
                    if row.display_key and subform.render[row.display_key]
         | 
| 33 | 
            +
                      self.display_key_label.text = subform.render[row.display_key]
         | 
| 34 | 
            +
                    end
         | 
| 7 35 | 
             
                  end
         | 
| 8 36 |  | 
| 9 37 | 
             
                  def on_select(tableView, tableViewDelegate)
         | 
| @@ -11,6 +39,15 @@ module Formotion | |
| 11 39 | 
             
                    row.form.controller.push_subform(subform)
         | 
| 12 40 | 
             
                  end
         | 
| 13 41 |  | 
| 42 | 
            +
                  def display_key_label
         | 
| 43 | 
            +
                    @display_key_label ||= begin
         | 
| 44 | 
            +
                      label = UILabel.alloc.initWithFrame(CGRectZero)
         | 
| 45 | 
            +
                      label.textColor = "#385387".to_color
         | 
| 46 | 
            +
                      label.tag = LABEL_TAG
         | 
| 47 | 
            +
                      label.backgroundColor = UIColor.clearColor
         | 
| 48 | 
            +
                      label
         | 
| 49 | 
            +
                    end
         | 
| 50 | 
            +
                  end
         | 
| 14 51 | 
             
                end
         | 
| 15 52 | 
             
              end
         | 
| 16 53 | 
             
            end
         | 
    
        data/lib/formotion/version.rb
    CHANGED
    
    
| @@ -0,0 +1,58 @@ | |
| 1 | 
            +
            describe "Form Persisting" do
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              it "works" do
         | 
| 4 | 
            +
                f = Formotion::Form.persist({
         | 
| 5 | 
            +
                  persist_as: "test_#{rand(255)}",
         | 
| 6 | 
            +
                  sections: [
         | 
| 7 | 
            +
                    rows: [ {
         | 
| 8 | 
            +
                        key: "first",
         | 
| 9 | 
            +
                        type: "string",
         | 
| 10 | 
            +
                        value: "initial value"
         | 
| 11 | 
            +
                      }
         | 
| 12 | 
            +
                    ]
         | 
| 13 | 
            +
                  ]
         | 
| 14 | 
            +
                })
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                r = f.sections[0].rows[0]
         | 
| 17 | 
            +
                r.value = "new value"
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                saved = Formotion::Form.new(f.send(:load_state))
         | 
| 20 | 
            +
                saved.sections[0].rows[0].value.should == r.value
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                f.reset
         | 
| 23 | 
            +
                r.value.should == "initial value"
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              it "works with subforms" do
         | 
| 27 | 
            +
                f = Formotion::Form.persist({
         | 
| 28 | 
            +
                  persist_as: "test_#{rand(255)}",
         | 
| 29 | 
            +
                  sections: [
         | 
| 30 | 
            +
                    rows: [ {
         | 
| 31 | 
            +
                        key: :subform,
         | 
| 32 | 
            +
                        type: :subform,
         | 
| 33 | 
            +
                        title: "Subform",
         | 
| 34 | 
            +
                        subform: {
         | 
| 35 | 
            +
                          title: "New Page",
         | 
| 36 | 
            +
                          sections: [
         | 
| 37 | 
            +
                            rows: [{
         | 
| 38 | 
            +
                              key: "second",
         | 
| 39 | 
            +
                              type: "string",
         | 
| 40 | 
            +
                              value: "initial value"
         | 
| 41 | 
            +
                            }]
         | 
| 42 | 
            +
                          ]
         | 
| 43 | 
            +
                        }
         | 
| 44 | 
            +
                      }
         | 
| 45 | 
            +
                    ]
         | 
| 46 | 
            +
                  ]
         | 
| 47 | 
            +
                })
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                r = f.sections[0].rows[0].subform.to_form.sections[0].rows[0]
         | 
| 50 | 
            +
                r.value = "new value"
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                saved = Formotion::Form.new(f.send(:load_state))
         | 
| 53 | 
            +
                saved.sections[0].rows[0].subform.to_form.sections[0].rows[0].value.should == r.value
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                f.reset
         | 
| 56 | 
            +
                r.value.should == "initial value"
         | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
            end
         | 
| @@ -7,6 +7,15 @@ class TestModel | |
| 7 7 | 
             
              form_property :my_number, :number, title: "My Number", :transform => lambda { |value| value.to_i + 10 }
         | 
| 8 8 | 
             
            end
         | 
| 9 9 |  | 
| 10 | 
            +
            class AdditionalTestModel
         | 
| 11 | 
            +
              include Formotion::Formable
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              attr_accessor :my_other_name, :my_other_number
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              form_property :my_other_name, :string
         | 
| 16 | 
            +
              form_property :my_other_number, :number
         | 
| 17 | 
            +
            end
         | 
| 18 | 
            +
             | 
| 10 19 | 
             
            describe "FormableController" do
         | 
| 11 20 | 
             
              tests Formotion::FormableController
         | 
| 12 21 |  | 
    
        data/spec/row_spec.rb
    CHANGED
    
    | @@ -22,4 +22,13 @@ describe "Rows" do | |
| 22 22 | 
             
                  r.title?.should == true
         | 
| 23 23 | 
             
                }
         | 
| 24 24 | 
             
              end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              it "should allow arrays and blocks for items" do
         | 
| 27 | 
            +
                r = Formotion::Row.new()
         | 
| 28 | 
            +
                r.items = [1, 2, 3]
         | 
| 29 | 
            +
                r.items.should == [1, 2, 3]
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                r.items = lambda { [1, 2, 3].reverse }
         | 
| 32 | 
            +
                r.items.should == [3, 2, 1]
         | 
| 33 | 
            +
              end
         | 
| 25 34 | 
             
            end
         | 
| @@ -1,7 +1,9 @@ | |
| 1 1 | 
             
            module UIControlWrap
         | 
| 2 | 
            -
              # adds ability to trigger a defined callback | 
| 2 | 
            +
              # adds ability to trigger a defined callback
         | 
| 3 | 
            +
              # that was previously defined by #when
         | 
| 3 4 | 
             
              def trigger(events)
         | 
| 4 5 | 
             
                @callback ||= {}
         | 
| 5 | 
            -
                 | 
| 6 | 
            +
                p "Callback #{@callback}"
         | 
| 7 | 
            +
                @callback[events].map(&:call) if @callback.has_key? events
         | 
| 6 8 | 
             
              end
         | 
| 7 9 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: formotion
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: '1. | 
| 4 | 
            +
              version: '1.1'
         | 
| 5 5 | 
             
              prerelease: 
         | 
| 6 6 | 
             
            platform: ruby
         | 
| 7 7 | 
             
            authors:
         | 
| @@ -9,7 +9,7 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2012- | 
| 12 | 
            +
            date: 2012-09-20 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: bubble-wrap
         | 
| @@ -77,6 +77,11 @@ files: | |
| 77 77 | 
             
            - examples/Login/app/login_controller.rb
         | 
| 78 78 | 
             
            - examples/Login/app/user_controller.rb
         | 
| 79 79 | 
             
            - examples/Login/spec/main_spec.rb
         | 
| 80 | 
            +
            - examples/Persistence/.gitignore
         | 
| 81 | 
            +
            - examples/Persistence/README.md
         | 
| 82 | 
            +
            - examples/Persistence/Rakefile
         | 
| 83 | 
            +
            - examples/Persistence/app/app_delegate.rb
         | 
| 84 | 
            +
            - examples/Persistence/spec/main_spec.rb
         | 
| 80 85 | 
             
            - gh-pages/assets/css/bootstrap-responsive.css
         | 
| 81 86 | 
             
            - gh-pages/assets/css/bootstrap-responsive.min.css
         | 
| 82 87 | 
             
            - gh-pages/assets/css/bootstrap.css
         | 
| @@ -138,6 +143,7 @@ files: | |
| 138 143 | 
             
            - lib/formotion/row_type/text_row.rb
         | 
| 139 144 | 
             
            - lib/formotion/section/section.rb
         | 
| 140 145 | 
             
            - lib/formotion/version.rb
         | 
| 146 | 
            +
            - spec/form/persist_spec.rb
         | 
| 141 147 | 
             
            - spec/form_spec.rb
         | 
| 142 148 | 
             
            - spec/formable_spec.rb
         | 
| 143 149 | 
             
            - spec/functional/character_spec.rb
         | 
| @@ -188,7 +194,7 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 188 194 | 
             
                  version: '0'
         | 
| 189 195 | 
             
                  segments:
         | 
| 190 196 | 
             
                  - 0
         | 
| 191 | 
            -
                  hash:  | 
| 197 | 
            +
                  hash: 4038020492425824062
         | 
| 192 198 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 193 199 | 
             
              none: false
         | 
| 194 200 | 
             
              requirements:
         | 
| @@ -197,7 +203,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 197 203 | 
             
                  version: '0'
         | 
| 198 204 | 
             
                  segments:
         | 
| 199 205 | 
             
                  - 0
         | 
| 200 | 
            -
                  hash:  | 
| 206 | 
            +
                  hash: 4038020492425824062
         | 
| 201 207 | 
             
            requirements: []
         | 
| 202 208 | 
             
            rubyforge_project: 
         | 
| 203 209 | 
             
            rubygems_version: 1.8.23
         | 
| @@ -205,6 +211,7 @@ signing_key: | |
| 205 211 | 
             
            specification_version: 3
         | 
| 206 212 | 
             
            summary: Making iOS Forms insanely great with RubyMotion
         | 
| 207 213 | 
             
            test_files:
         | 
| 214 | 
            +
            - spec/form/persist_spec.rb
         | 
| 208 215 | 
             
            - spec/form_spec.rb
         | 
| 209 216 | 
             
            - spec/formable_spec.rb
         | 
| 210 217 | 
             
            - spec/functional/character_spec.rb
         |