user_preferences 0.0.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 +15 -0
 - data/.gitignore +14 -0
 - data/Gemfile +4 -0
 - data/LICENSE.txt +22 -0
 - data/README.md +137 -0
 - data/Rakefile +7 -0
 - data/lib/generators/user_preferences/install_generator.rb +21 -0
 - data/lib/generators/user_preferences/templates/migration.rb +19 -0
 - data/lib/generators/user_preferences/templates/user_preferences.yml +0 -0
 - data/lib/user_preferences.rb +37 -0
 - data/lib/user_preferences/api.rb +63 -0
 - data/lib/user_preferences/defaults.rb +23 -0
 - data/lib/user_preferences/has_preferences.rb +36 -0
 - data/lib/user_preferences/preference.rb +21 -0
 - data/lib/user_preferences/preference_definition.rb +47 -0
 - data/lib/user_preferences/railtie.rb +9 -0
 - data/lib/user_preferences/version.rb +3 -0
 - data/spec/fixtures/user_preferences.yml +19 -0
 - data/spec/migrations/001_create_users.rb +11 -0
 - data/spec/migrations/002_create_preferences.rb +19 -0
 - data/spec/spec_helper.rb +24 -0
 - data/spec/user_preferences/api_spec.rb +81 -0
 - data/spec/user_preferences/defaults_spec.rb +33 -0
 - data/spec/user_preferences/has_preferences_spec.rb +44 -0
 - data/spec/user_preferences/preference_definition_spec.rb +90 -0
 - data/spec/user_preferences/preference_spec.rb +79 -0
 - data/spec/user_preferences_spec.rb +44 -0
 - data/user_preferences.gemspec +29 -0
 - metadata +167 -0
 
    
        checksums.yaml
    ADDED
    
    | 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ---
         
     | 
| 
      
 2 
     | 
    
         
            +
            !binary "U0hBMQ==":
         
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: !binary |-
         
     | 
| 
      
 4 
     | 
    
         
            +
                ZDJiY2VjZTUyZTNjNmRlYmNmY2E0Yjk1ODljZWE2ZDQ3ZTQ1NGVlNg==
         
     | 
| 
      
 5 
     | 
    
         
            +
              data.tar.gz: !binary |-
         
     | 
| 
      
 6 
     | 
    
         
            +
                MmM4Zjk4MDMzYjA4ZGUzMDZlZGM0YTY0NjE3ODZkMzA4YWY2Yjc1YQ==
         
     | 
| 
      
 7 
     | 
    
         
            +
            SHA512:
         
     | 
| 
      
 8 
     | 
    
         
            +
              metadata.gz: !binary |-
         
     | 
| 
      
 9 
     | 
    
         
            +
                N2YyZTlmMzlkYmFiYzNjODU5ZjI4N2YyY2U0MGZlYmI4OTdlYzlhODkzOWQ2
         
     | 
| 
      
 10 
     | 
    
         
            +
                YjhmNDRhYWU3ZTRkOWM4OTdiYmM0YTA3NzRkMTQyOTJlZGQ4NDBmOWFlY2Ix
         
     | 
| 
      
 11 
     | 
    
         
            +
                YWExMTExMWYxNmRhOTFhNzZmZjRlM2U3ZDU2ODkyM2ZhYTJlMzk=
         
     | 
| 
      
 12 
     | 
    
         
            +
              data.tar.gz: !binary |-
         
     | 
| 
      
 13 
     | 
    
         
            +
                N2JkY2I0NTAyMjJjZGE5ZWFmMWQ4NGY3ODRjODA4OTFjMDEzYzdhMDA1OTk0
         
     | 
| 
      
 14 
     | 
    
         
            +
                YzJlOGJlNmRlODdiODEyZTdmOTY3YWJmODQ5M2U2MDdiOWE4ZmQ2Nzg4YmZj
         
     | 
| 
      
 15 
     | 
    
         
            +
                MGY2ZmNlOWJjZDczMTRjZjhiOTc4NDIzNjA3NGE4NTRlZmJkNWU=
         
     | 
    
        data/.gitignore
    ADDED
    
    
    
        data/Gemfile
    ADDED
    
    
    
        data/LICENSE.txt
    ADDED
    
    | 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            Copyright (c) 2014 Andy Dust
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            MIT License
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            Permission is hereby granted, free of charge, to any person obtaining
         
     | 
| 
      
 6 
     | 
    
         
            +
            a copy of this software and associated documentation files (the
         
     | 
| 
      
 7 
     | 
    
         
            +
            "Software"), to deal in the Software without restriction, including
         
     | 
| 
      
 8 
     | 
    
         
            +
            without limitation the rights to use, copy, modify, merge, publish,
         
     | 
| 
      
 9 
     | 
    
         
            +
            distribute, sublicense, and/or sell copies of the Software, and to
         
     | 
| 
      
 10 
     | 
    
         
            +
            permit persons to whom the Software is furnished to do so, subject to
         
     | 
| 
      
 11 
     | 
    
         
            +
            the following conditions:
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            The above copyright notice and this permission notice shall be
         
     | 
| 
      
 14 
     | 
    
         
            +
            included in all copies or substantial portions of the Software.
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         
     | 
| 
      
 17 
     | 
    
         
            +
            EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         
     | 
| 
      
 18 
     | 
    
         
            +
            MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         
     | 
| 
      
 19 
     | 
    
         
            +
            NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         
     | 
| 
      
 20 
     | 
    
         
            +
            LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         
     | 
| 
      
 21 
     | 
    
         
            +
            OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         
     | 
| 
      
 22 
     | 
    
         
            +
            WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         
     | 
    
        data/README.md
    ADDED
    
    | 
         @@ -0,0 +1,137 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # UserPreferences
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            An ActiveRecord backed user preference library that supports:
         
     | 
| 
      
 4 
     | 
    
         
            +
            * Categories (currently non-optional)
         
     | 
| 
      
 5 
     | 
    
         
            +
            * Binary and non-binary preferences
         
     | 
| 
      
 6 
     | 
    
         
            +
            * Default values
         
     | 
| 
      
 7 
     | 
    
         
            +
            * Value validation
         
     | 
| 
      
 8 
     | 
    
         
            +
            * Retrieving users scoped by a particular preference
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            ## Installation
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            Add this line to your application's Gemfile:
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 15 
     | 
    
         
            +
            gem 'user_preferences'
         
     | 
| 
      
 16 
     | 
    
         
            +
            ```
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            And then execute:
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            ```sh
         
     | 
| 
      
 21 
     | 
    
         
            +
            $ bundle
         
     | 
| 
      
 22 
     | 
    
         
            +
            ```
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            Run the installation script:
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            ```sh
         
     | 
| 
      
 27 
     | 
    
         
            +
            $ rails g user_preferences:install
         
     | 
| 
      
 28 
     | 
    
         
            +
            ```
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            This will copy across the migration and add an empty preference definition file in config/
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            Finally, run the database migrations:
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            ```sh
         
     | 
| 
      
 35 
     | 
    
         
            +
            $ rake db:migrate
         
     | 
| 
      
 36 
     | 
    
         
            +
            ```
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            ## Defining preferences
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            Your preferences are defined in ``config/user_preferences.yml``. You define each of your
         
     | 
| 
      
 41 
     | 
    
         
            +
            preferences within a category. This example definition for a binary preference implies that users receive emails notifications by default but not newsletters:
         
     | 
| 
      
 42 
     | 
    
         
            +
            ```yaml
         
     | 
| 
      
 43 
     | 
    
         
            +
            emails:
         
     | 
| 
      
 44 
     | 
    
         
            +
              notifications: true
         
     | 
| 
      
 45 
     | 
    
         
            +
              newsletters: false
         
     | 
| 
      
 46 
     | 
    
         
            +
            ```
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
            You can configure non-binary preferences. For example, if users could choose periodical notification digests, the configuration might look like this:
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            ```yaml
         
     | 
| 
      
 51 
     | 
    
         
            +
            emails:
         
     | 
| 
      
 52 
     | 
    
         
            +
              notifications:
         
     | 
| 
      
 53 
     | 
    
         
            +
                default: instant
         
     | 
| 
      
 54 
     | 
    
         
            +
                values:
         
     | 
| 
      
 55 
     | 
    
         
            +
                  - off
         
     | 
| 
      
 56 
     | 
    
         
            +
                  - instant
         
     | 
| 
      
 57 
     | 
    
         
            +
                  - daily
         
     | 
| 
      
 58 
     | 
    
         
            +
                  - weekly
         
     | 
| 
      
 59 
     | 
    
         
            +
              newsletters: false
         
     | 
| 
      
 60 
     | 
    
         
            +
            ```
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
            You can add as many categories as you like:
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
            ```yaml
         
     | 
| 
      
 65 
     | 
    
         
            +
            emails:
         
     | 
| 
      
 66 
     | 
    
         
            +
              notifications: true
         
     | 
| 
      
 67 
     | 
    
         
            +
              newsletters: false
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
            beta_features:
         
     | 
| 
      
 70 
     | 
    
         
            +
              two_factor_authentication: false
         
     | 
| 
      
 71 
     | 
    
         
            +
              the_big_red_button: false
         
     | 
| 
      
 72 
     | 
    
         
            +
            ```
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
            ## API
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
            ### set
         
     | 
| 
      
 77 
     | 
    
         
            +
            Similar to ActiveRecord, setting a preference returns true or false depending on whether or not it was successfully persisted:
         
     | 
| 
      
 78 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 79 
     | 
    
         
            +
            user.preferences(:emails).set(notifications: 'instant') # => true
         
     | 
| 
      
 80 
     | 
    
         
            +
            user.preferences(:emails).set(notifications: 'some_typo') # => false
         
     | 
| 
      
 81 
     | 
    
         
            +
            ```
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
            You can set multiple preferences at once:
         
     | 
| 
      
 84 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 85 
     | 
    
         
            +
            user.preferences(:emails).set(notifications: 'instant', newsletter: true) # => true
         
     | 
| 
      
 86 
     | 
    
         
            +
            ```
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
            ### get
         
     | 
| 
      
 89 
     | 
    
         
            +
            A single preference:
         
     | 
| 
      
 90 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 91 
     | 
    
         
            +
            user.preferences(:emails).get(:notifications) # => 'instant'
         
     | 
| 
      
 92 
     | 
    
         
            +
            ```
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
            ### all
         
     | 
| 
      
 95 
     | 
    
         
            +
            All preferences for a category:
         
     | 
| 
      
 96 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 97 
     | 
    
         
            +
            user.preferences(:emails).all # => { notifications: 'instant', newsletter: true }
         
     | 
| 
      
 98 
     | 
    
         
            +
            ```
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
            ### reload
         
     | 
| 
      
 101 
     | 
    
         
            +
            Reload the preferences from the database; since something else might have changed the user's state.
         
     | 
| 
      
 102 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 103 
     | 
    
         
            +
            user.preferences(:emails).reload # => { notifications: 'instant', newsletter: true }
         
     | 
| 
      
 104 
     | 
    
         
            +
            ```
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
            ## Scoping users
         
     | 
| 
      
 107 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 108 
     | 
    
         
            +
              newsletter_users = User.with_preference(:email, :newsletter, true) #=> an ActiveRecord::Relation
         
     | 
| 
      
 109 
     | 
    
         
            +
            ```
         
     | 
| 
      
 110 
     | 
    
         
            +
            Note: this _will_ include users who have not overriden the default value if the value incidentally matches the default value.
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
            ## Other useful stuff
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
            ### Single preference definition
         
     | 
| 
      
 115 
     | 
    
         
            +
            * Get your preference definition (as per your .yml) as a hash: ``UserPreferences.definitions``
         
     | 
| 
      
 116 
     | 
    
         
            +
            * Get the definition for a single preference:
         
     | 
| 
      
 117 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 118 
     | 
    
         
            +
              preference = UserPreferences[:emails, :notifications]
         
     | 
| 
      
 119 
     | 
    
         
            +
              preference.default # => 'instant'
         
     | 
| 
      
 120 
     | 
    
         
            +
              preference.binary? # => false
         
     | 
| 
      
 121 
     | 
    
         
            +
              preference.permitted_values # => ['off', 'instant', 'daily', 'weekly']
         
     | 
| 
      
 122 
     | 
    
         
            +
            ```
         
     | 
| 
      
 123 
     | 
    
         
            +
            * Retrieve the default preference state with ``UserPreferences.defaults``. You can also scope to a category: ``UserPreferences.defaults(:emails)``
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
            ## Testing
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
            ```sh
         
     | 
| 
      
 128 
     | 
    
         
            +
            $ rake test
         
     | 
| 
      
 129 
     | 
    
         
            +
            ```
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
            ## Contributing
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
            1. Fork it ( http://github.com/mubi/user_preferences/fork )
         
     | 
| 
      
 134 
     | 
    
         
            +
            2. Create your feature branch (`git checkout -b my-new-feature`)
         
     | 
| 
      
 135 
     | 
    
         
            +
            3. Commit your changes (`git commit -am 'Add some feature'`)
         
     | 
| 
      
 136 
     | 
    
         
            +
            4. Push to the branch (`git push origin my-new-feature`)
         
     | 
| 
      
 137 
     | 
    
         
            +
            5. Create new Pull Request
         
     | 
    
        data/Rakefile
    ADDED
    
    
| 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module UserPreferences
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Generators
         
     | 
| 
      
 3 
     | 
    
         
            +
                class InstallGenerator < Rails::Generators::Base
         
     | 
| 
      
 4 
     | 
    
         
            +
                  include Rails::Generators::Migration
         
     | 
| 
      
 5 
     | 
    
         
            +
                  source_root File.expand_path('../templates', __FILE__)
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                  def create_initializer_file
         
     | 
| 
      
 8 
     | 
    
         
            +
                    template 'user_preferences.yml', "config/user_preferences.yml"
         
     | 
| 
      
 9 
     | 
    
         
            +
                  end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  def copy_migrations
         
     | 
| 
      
 12 
     | 
    
         
            +
                    migration_template 'migration.rb', "db/migrate/create_preferences.rb"
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  # TODO get rid of this
         
     | 
| 
      
 16 
     | 
    
         
            +
                  def self.next_migration_number(dir)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    Time.now.utc.strftime("%Y%m%d%H%M%S")
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class CreatePreferences < ActiveRecord::Migration
         
     | 
| 
      
 2 
     | 
    
         
            +
              def self.up
         
     | 
| 
      
 3 
     | 
    
         
            +
                create_table :preferences do |t|
         
     | 
| 
      
 4 
     | 
    
         
            +
                  t.integer :user_id, null: false
         
     | 
| 
      
 5 
     | 
    
         
            +
                  t.string  :category, null: false
         
     | 
| 
      
 6 
     | 
    
         
            +
                  t.string  :name, null: false
         
     | 
| 
      
 7 
     | 
    
         
            +
                  t.integer :value, null: false
         
     | 
| 
      
 8 
     | 
    
         
            +
                  t.timestamps
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                add_index :preferences, :user_id
         
     | 
| 
      
 12 
     | 
    
         
            +
                add_index :preferences, [:user_id, :category]
         
     | 
| 
      
 13 
     | 
    
         
            +
                add_index :preferences, [:category, :name, :value]
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              def self.down
         
     | 
| 
      
 17 
     | 
    
         
            +
                drop_table :preferences
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
| 
         
            File without changes
         
     | 
| 
         @@ -0,0 +1,37 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'yaml'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'active_support'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'active_support/core_ext/hash'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module UserPreferences
         
     | 
| 
      
 6 
     | 
    
         
            +
              extend ActiveSupport::Autoload
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              autoload :API, 'user_preferences/api'
         
     | 
| 
      
 9 
     | 
    
         
            +
              autoload :Defaults
         
     | 
| 
      
 10 
     | 
    
         
            +
              autoload :HasPreferences
         
     | 
| 
      
 11 
     | 
    
         
            +
              autoload :Preference
         
     | 
| 
      
 12 
     | 
    
         
            +
              autoload :PreferenceDefinition
         
     | 
| 
      
 13 
     | 
    
         
            +
              autoload :VERSION
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 16 
     | 
    
         
            +
                def [](category, name)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  unless (pref = definitions[category].try(:[], name)).nil?
         
     | 
| 
      
 18 
     | 
    
         
            +
                    PreferenceDefinition.new(pref, category, name)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                def defaults(category = nil)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  @_defaults ||= Defaults.new(definitions)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  @_defaults.get(category)
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                def yml_path
         
     | 
| 
      
 28 
     | 
    
         
            +
                  Rails.root.join('config', 'user_preferences.yml') if defined?(Rails)
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def definitions
         
     | 
| 
      
 32 
     | 
    
         
            +
                  @_definitions ||= YAML.load_file(yml_path).with_indifferent_access
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
            end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            require 'user_preferences/railtie' if defined?(Rails)
         
     | 
| 
         @@ -0,0 +1,63 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module UserPreferences
         
     | 
| 
      
 2 
     | 
    
         
            +
              class API
         
     | 
| 
      
 3 
     | 
    
         
            +
                def initialize(category, scope)
         
     | 
| 
      
 4 
     | 
    
         
            +
                  @category = category
         
     | 
| 
      
 5 
     | 
    
         
            +
                  @scope = scope.where(category: category)
         
     | 
| 
      
 6 
     | 
    
         
            +
                end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def all
         
     | 
| 
      
 9 
     | 
    
         
            +
                  serialized_preferences
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def get(name)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  serialized_preferences[name]
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                def set(hash)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  hash_setter do
         
     | 
| 
      
 18 
     | 
    
         
            +
                    hash.each do |name, value|
         
     | 
| 
      
 19 
     | 
    
         
            +
                      find_or_init_preference(name).update_value!(value)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    end
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def reload
         
     | 
| 
      
 25 
     | 
    
         
            +
                  @_saved_preferences = nil
         
     | 
| 
      
 26 
     | 
    
         
            +
                  all
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                private
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def serialized_preferences
         
     | 
| 
      
 32 
     | 
    
         
            +
                  default_preferences.merge Hash[saved_preferences.map { |p| [p.name.to_sym, p.value] }]
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                def default_preferences
         
     | 
| 
      
 36 
     | 
    
         
            +
                  @_category_defaults ||= UserPreferences.defaults(@category)
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                def saved_preferences
         
     | 
| 
      
 40 
     | 
    
         
            +
                  @_saved_preferences ||= @scope.select([:id, :category, :name, :value, :user_id]).all
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                def find_or_init_preference(name)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  unless preference = saved_preferences.detect { |p| p.name == name }
         
     | 
| 
      
 45 
     | 
    
         
            +
                    preference = @scope.find_by_name(name) || @scope.build(name: name, category: @category)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    saved_preferences << preference
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
                  preference
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                def hash_setter(&block)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  ActiveRecord::Base.transaction do
         
     | 
| 
      
 53 
     | 
    
         
            +
                    result = true
         
     | 
| 
      
 54 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 55 
     | 
    
         
            +
                      yield
         
     | 
| 
      
 56 
     | 
    
         
            +
                    rescue ActiveRecord::RecordInvalid
         
     | 
| 
      
 57 
     | 
    
         
            +
                      result = false
         
     | 
| 
      
 58 
     | 
    
         
            +
                    end
         
     | 
| 
      
 59 
     | 
    
         
            +
                    result
         
     | 
| 
      
 60 
     | 
    
         
            +
                  end
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
              end
         
     | 
| 
      
 63 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module UserPreferences
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Defaults
         
     | 
| 
      
 3 
     | 
    
         
            +
                def initialize(definitions)
         
     | 
| 
      
 4 
     | 
    
         
            +
                  @definitions = definitions
         
     | 
| 
      
 5 
     | 
    
         
            +
                end
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                def get(category = nil)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  if category
         
     | 
| 
      
 9 
     | 
    
         
            +
                    category_defaults(category)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  else
         
     | 
| 
      
 11 
     | 
    
         
            +
                    @definitions.inject({}) { |h, (k,v)| h[k.to_sym] = category_defaults(k); h }
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                private
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def category_defaults(category)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @definitions[category].inject({}) do |h, (k,v)|
         
     | 
| 
      
 19 
     | 
    
         
            +
                    h[k.to_sym] = v.is_a?(Hash) ? v['default'] : v; h
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,36 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module UserPreferences
         
     | 
| 
      
 2 
     | 
    
         
            +
              module HasPreferences
         
     | 
| 
      
 3 
     | 
    
         
            +
                extend ActiveSupport::Concern
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                module ActiveRecordExtension
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def has_preferences
         
     | 
| 
      
 7 
     | 
    
         
            +
                    include HasPreferences
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                included do
         
     | 
| 
      
 12 
     | 
    
         
            +
                  has_many :saved_preferences, class_name: 'UserPreferences::Preference', dependent: :destroy
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  def preferences(category)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    @_preference_apis ||= {}
         
     | 
| 
      
 16 
     | 
    
         
            +
                    @_preference_apis[category] ||= UserPreferences::API.new(category, saved_preferences)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  def self.with_preference(category, name, value)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    definition = UserPreferences[category, name]
         
     | 
| 
      
 21 
     | 
    
         
            +
                    db_value = definition.to_db(value)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    scope = select('users.*, p.id as preference_id')
         
     | 
| 
      
 23 
     | 
    
         
            +
                    join = %Q{
         
     | 
| 
      
 24 
     | 
    
         
            +
                      %s join #{UserPreferences::Preference.table_name} p
         
     | 
| 
      
 25 
     | 
    
         
            +
                      on p.category = '#{category}' and p.name = '#{name}'
         
     | 
| 
      
 26 
     | 
    
         
            +
                      and p.user_id = #{self.table_name}.id
         
     | 
| 
      
 27 
     | 
    
         
            +
                    }
         
     | 
| 
      
 28 
     | 
    
         
            +
                    if value != definition.default
         
     | 
| 
      
 29 
     | 
    
         
            +
                      scope.joins(join % 'inner').where("p.value = #{db_value}")
         
     | 
| 
      
 30 
     | 
    
         
            +
                    else
         
     | 
| 
      
 31 
     | 
    
         
            +
                      scope.joins(join % 'left').where("p.value = #{db_value} or p.id is null")
         
     | 
| 
      
 32 
     | 
    
         
            +
                    end
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class UserPreferences::Preference < ActiveRecord::Base
         
     | 
| 
      
 2 
     | 
    
         
            +
              self.table_name = 'preferences'
         
     | 
| 
      
 3 
     | 
    
         
            +
              belongs_to :user
         
     | 
| 
      
 4 
     | 
    
         
            +
              validates_uniqueness_of :name, scope: [:user_id, :category]
         
     | 
| 
      
 5 
     | 
    
         
            +
              validates_presence_of :user_id, :category, :name
         
     | 
| 
      
 6 
     | 
    
         
            +
              validates :value, inclusion: { in: ->(p) { p.permitted_values }}
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              delegate :binary?, :default, :permitted_values, :lookup, :to_db, to: :definition
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              def update_value!(v)
         
     | 
| 
      
 11 
     | 
    
         
            +
                update_attributes!(value: to_db(v))
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              def value
         
     | 
| 
      
 15 
     | 
    
         
            +
                lookup(attributes['value'])
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              def definition
         
     | 
| 
      
 19 
     | 
    
         
            +
                UserPreferences[category, name]
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,47 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module UserPreferences
         
     | 
| 
      
 2 
     | 
    
         
            +
              class PreferenceDefinition
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_reader :name, :category
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                def initialize(definition, category, name)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @definition = definition
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @category = category
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @name = name
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def permitted_values
         
     | 
| 
      
 12 
     | 
    
         
            +
                  if binary?
         
     | 
| 
      
 13 
     | 
    
         
            +
                    result = [false, true]
         
     | 
| 
      
 14 
     | 
    
         
            +
                  else
         
     | 
| 
      
 15 
     | 
    
         
            +
                    @definition[:values]
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                def binary?
         
     | 
| 
      
 20 
     | 
    
         
            +
                  !@definition.is_a?(Hash)
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                def default
         
     | 
| 
      
 24 
     | 
    
         
            +
                  binary? ? @definition : @definition[:default]
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                def lookup(index)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  permitted_values[index] if index
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def to_db(value)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  value = to_bool(value) if binary?
         
     | 
| 
      
 33 
     | 
    
         
            +
                  permitted_values.index(value)
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                private
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                def to_bool(value)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  return true if value == 1
         
     | 
| 
      
 40 
     | 
    
         
            +
                  return true if value == true || value =~ (/^(true|t|yes|y|1)$/i)
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  return false if value == 0
         
     | 
| 
      
 43 
     | 
    
         
            +
                  return false if value == false || value.blank? || value =~ (/^(false|f|no|n|0)$/i)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  raise ArgumentError.new("invalid value for Boolean: \"#{value}\"")
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class CreatePreferences < ActiveRecord::Migration
         
     | 
| 
      
 2 
     | 
    
         
            +
              def self.up
         
     | 
| 
      
 3 
     | 
    
         
            +
                create_table :preferences do |t|
         
     | 
| 
      
 4 
     | 
    
         
            +
                  t.integer :user_id, null: false
         
     | 
| 
      
 5 
     | 
    
         
            +
                  t.string  :category, null: false
         
     | 
| 
      
 6 
     | 
    
         
            +
                  t.string  :name, null: false
         
     | 
| 
      
 7 
     | 
    
         
            +
                  t.integer :value, null: false
         
     | 
| 
      
 8 
     | 
    
         
            +
                  t.timestamps
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                add_index :preferences, :user_id
         
     | 
| 
      
 12 
     | 
    
         
            +
                add_index :preferences, [:user_id, :category]
         
     | 
| 
      
 13 
     | 
    
         
            +
                add_index :preferences, [:category, :name, :value]
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              def self.down
         
     | 
| 
      
 17 
     | 
    
         
            +
                drop_table :preferences
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
    
        data/spec/spec_helper.rb
    ADDED
    
    | 
         @@ -0,0 +1,24 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'user_preferences'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'active_record'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'active_record/connection_adapters/sqlite3_adapter'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            RSpec.configure do |config|
         
     | 
| 
      
 6 
     | 
    
         
            +
              $stdout = StringIO.new # silence migrations
         
     | 
| 
      
 7 
     | 
    
         
            +
              ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
         
     | 
| 
      
 8 
     | 
    
         
            +
              ActiveRecord::Migrator.migrate(File.expand_path('../migrations', __FILE__))
         
     | 
| 
      
 9 
     | 
    
         
            +
              $stdout = STDOUT
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              # prevent deprecation warnings
         
     | 
| 
      
 12 
     | 
    
         
            +
              I18n.enforce_available_locales = true
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              config.before(:each) { stub_yml }
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            def stub_yml
         
     | 
| 
      
 18 
     | 
    
         
            +
              fixture = File.expand_path("../fixtures/user_preferences.yml", __FILE__)
         
     | 
| 
      
 19 
     | 
    
         
            +
              UserPreferences.stub(:yml_path).and_return(fixture)
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            class User < ActiveRecord::Base
         
     | 
| 
      
 23 
     | 
    
         
            +
              include UserPreferences::HasPreferences
         
     | 
| 
      
 24 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,81 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe UserPreferences::API do
         
     | 
| 
      
 4 
     | 
    
         
            +
              let(:user) { User.create! }
         
     | 
| 
      
 5 
     | 
    
         
            +
              subject(:api) { UserPreferences::API.new(:food, user.saved_preferences) }
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              describe '#all' do
         
     | 
| 
      
 8 
     | 
    
         
            +
                it 'returns preference state' do
         
     | 
| 
      
 9 
     | 
    
         
            +
                  expect(api.all).to eq({
         
     | 
| 
      
 10 
     | 
    
         
            +
                    vegetarian: false,
         
     | 
| 
      
 11 
     | 
    
         
            +
                    a_la_carte: true,
         
     | 
| 
      
 12 
     | 
    
         
            +
                    courses: 2,
         
     | 
| 
      
 13 
     | 
    
         
            +
                    wine: 'red'
         
     | 
| 
      
 14 
     | 
    
         
            +
                  })
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              describe '#get' do
         
     | 
| 
      
 19 
     | 
    
         
            +
                it 'gets the preference value' do
         
     | 
| 
      
 20 
     | 
    
         
            +
                  expect(api.get(:a_la_carte)).to eq(true)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  expect(api.get(:wine)).to eq('red')
         
     | 
| 
      
 22 
     | 
    
         
            +
                  expect(api.get(:courses)).to eq(2)
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              describe '#set' do
         
     | 
| 
      
 27 
     | 
    
         
            +
                it 'persists the preference values' do
         
     | 
| 
      
 28 
     | 
    
         
            +
                  api.set(wine: 'white')
         
     | 
| 
      
 29 
     | 
    
         
            +
                  api.reload
         
     | 
| 
      
 30 
     | 
    
         
            +
                  expect(api.get(:wine)).to eq('white')
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                it 'allows setting of multiple preferences' do
         
     | 
| 
      
 34 
     | 
    
         
            +
                  api.set(wine: 'white', courses: 3)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  api.reload
         
     | 
| 
      
 36 
     | 
    
         
            +
                  expect(api.get(:wine)).to eq('white')
         
     | 
| 
      
 37 
     | 
    
         
            +
                  expect(api.get(:courses)).to eq(3)
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                it 'returns true' do
         
     | 
| 
      
 41 
     | 
    
         
            +
                  expect(api.set(wine: 'white')).to eq(true)
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                context 'one or more of the preference values were invalid' do
         
     | 
| 
      
 45 
     | 
    
         
            +
                  it 'returns false' do
         
     | 
| 
      
 46 
     | 
    
         
            +
                    expect(api.set(wine: 'sparkling')).to eq(false)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    expect(api.set(wine: 'white', courses: 10)).to eq(false)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  it 'does not persist anything if any of the values were invalid' do
         
     | 
| 
      
 51 
     | 
    
         
            +
                    api.set(wine: 'sparkling', courses: 3)
         
     | 
| 
      
 52 
     | 
    
         
            +
                    api.reload
         
     | 
| 
      
 53 
     | 
    
         
            +
                    expect(api.get(:wine)).to eq('red')
         
     | 
| 
      
 54 
     | 
    
         
            +
                    expect(api.get(:courses)).to eq(2)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
              describe '#reload' do
         
     | 
| 
      
 60 
     | 
    
         
            +
                before(:each) do
         
     | 
| 
      
 61 
     | 
    
         
            +
                  api.set(courses: 1)
         
     | 
| 
      
 62 
     | 
    
         
            +
                  other_thread_api = UserPreferences::API.new('food', User.last.saved_preferences)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  other_thread_api.set(courses: 3)
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                it 'reloads the preference state' do
         
     | 
| 
      
 67 
     | 
    
         
            +
                  expect(api.get(:courses)).to eq(1)
         
     | 
| 
      
 68 
     | 
    
         
            +
                  api.reload
         
     | 
| 
      
 69 
     | 
    
         
            +
                  expect(api.get(:courses)).to eq(3)
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                it 'returns the preference state' do
         
     | 
| 
      
 73 
     | 
    
         
            +
                  expect(api.reload).to eq({
         
     | 
| 
      
 74 
     | 
    
         
            +
                    vegetarian: false,
         
     | 
| 
      
 75 
     | 
    
         
            +
                    a_la_carte: true,
         
     | 
| 
      
 76 
     | 
    
         
            +
                    courses: 3,
         
     | 
| 
      
 77 
     | 
    
         
            +
                    wine: 'red'
         
     | 
| 
      
 78 
     | 
    
         
            +
                  })
         
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
              end
         
     | 
| 
      
 81 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,33 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe UserPreferences::Defaults do
         
     | 
| 
      
 4 
     | 
    
         
            +
              subject(:defaults) { UserPreferences::Defaults.new(UserPreferences.definitions) }
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              describe '.get' do
         
     | 
| 
      
 7 
     | 
    
         
            +
                it 'returns the default preference state' do
         
     | 
| 
      
 8 
     | 
    
         
            +
                  expect(defaults.get).to eq(
         
     | 
| 
      
 9 
     | 
    
         
            +
                    {
         
     | 
| 
      
 10 
     | 
    
         
            +
                      hobbies: {
         
     | 
| 
      
 11 
     | 
    
         
            +
                        outdoors: true,
         
     | 
| 
      
 12 
     | 
    
         
            +
                        cultural: false
         
     | 
| 
      
 13 
     | 
    
         
            +
                      },
         
     | 
| 
      
 14 
     | 
    
         
            +
                      food: {
         
     | 
| 
      
 15 
     | 
    
         
            +
                        vegetarian: false,
         
     | 
| 
      
 16 
     | 
    
         
            +
                        a_la_carte: true,
         
     | 
| 
      
 17 
     | 
    
         
            +
                        courses: 2,
         
     | 
| 
      
 18 
     | 
    
         
            +
                        wine: 'red'
         
     | 
| 
      
 19 
     | 
    
         
            +
                      }
         
     | 
| 
      
 20 
     | 
    
         
            +
                    }
         
     | 
| 
      
 21 
     | 
    
         
            +
                  )
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                context 'a category is supplied' do
         
     | 
| 
      
 25 
     | 
    
         
            +
                  it 'returns the default preference state for the category' do
         
     | 
| 
      
 26 
     | 
    
         
            +
                    expect(defaults.get(:hobbies)).to eq({
         
     | 
| 
      
 27 
     | 
    
         
            +
                      outdoors: true,
         
     | 
| 
      
 28 
     | 
    
         
            +
                      cultural: false
         
     | 
| 
      
 29 
     | 
    
         
            +
                    })
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,44 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe UserPreferences::HasPreferences do
         
     | 
| 
      
 4 
     | 
    
         
            +
              let(:user) { User.create }
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              it 'should mix in preference methods' do
         
     | 
| 
      
 7 
     | 
    
         
            +
                expect(user).to respond_to(:saved_preferences)
         
     | 
| 
      
 8 
     | 
    
         
            +
                expect(user).to respond_to(:preferences)
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              describe '#preferences' do
         
     | 
| 
      
 12 
     | 
    
         
            +
                it 'should return an API instance' do
         
     | 
| 
      
 13 
     | 
    
         
            +
                  expect(user.preferences(:food)).to be_kind_of(UserPreferences::API)
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              describe '.with_preference' do
         
     | 
| 
      
 18 
     | 
    
         
            +
                before(:each) { User.destroy_all }
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                it 'only returns users with the matching preference' do
         
     | 
| 
      
 21 
     | 
    
         
            +
                  user_1, user_2 = 2.times.map { User.create }
         
     | 
| 
      
 22 
     | 
    
         
            +
                  user_1.preferences(:food).set(wine: 'white')
         
     | 
| 
      
 23 
     | 
    
         
            +
                  user_2.preferences(:food).set(wine: 'red')
         
     | 
| 
      
 24 
     | 
    
         
            +
                  expect(User.with_preference(:food, :wine, 'white')).to eq([user_1])
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  user_2.preferences(:food).set(wine: 'white')
         
     | 
| 
      
 27 
     | 
    
         
            +
                  expect(User.with_preference(:food, :wine, 'white')).to eq([user_1, user_2])
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                it 'returns a chainable active record relation' do
         
     | 
| 
      
 31 
     | 
    
         
            +
                  user.preferences(:food).set(wine: 'white')
         
     | 
| 
      
 32 
     | 
    
         
            +
                  expect(User.with_preference(:food, :wine, 'white')).to be_kind_of(ActiveRecord::Relation)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  expect(User.with_preference(:food, :wine, 'white').where('1=1')).to eq([user])
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                context 'the desired preference matches the default value' do
         
     | 
| 
      
 37 
     | 
    
         
            +
                  it 'includes users who have not explicitely overriden the preference' do
         
     | 
| 
      
 38 
     | 
    
         
            +
                    user.preferences(:food).set(wine: 'red') # the default value
         
     | 
| 
      
 39 
     | 
    
         
            +
                    user_2 = User.create
         
     | 
| 
      
 40 
     | 
    
         
            +
                    expect(User.with_preference(:food, :wine, 'red')).to eq([user, user_2])
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
      
 44 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,90 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe UserPreferences::PreferenceDefinition do
         
     | 
| 
      
 4 
     | 
    
         
            +
              subject(:preference) do
         
     | 
| 
      
 5 
     | 
    
         
            +
                definition = UserPreferences.definitions[:food][:wine]
         
     | 
| 
      
 6 
     | 
    
         
            +
                UserPreferences::PreferenceDefinition.new(definition, :food, :wine)
         
     | 
| 
      
 7 
     | 
    
         
            +
              end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              describe '#name' do
         
     | 
| 
      
 10 
     | 
    
         
            +
                it 'returns the preference name' do
         
     | 
| 
      
 11 
     | 
    
         
            +
                  expect(preference.name).to eq(:wine)
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              describe '#category' do
         
     | 
| 
      
 16 
     | 
    
         
            +
                it 'returns the preference category' do
         
     | 
| 
      
 17 
     | 
    
         
            +
                  expect(preference.category).to eq(:food)
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
              describe '#permitted_values' do
         
     | 
| 
      
 22 
     | 
    
         
            +
                it 'returns an array of permitted values' do
         
     | 
| 
      
 23 
     | 
    
         
            +
                  expect(preference.permitted_values).to eq(['red', 'white'])
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              describe '#binary?' do
         
     | 
| 
      
 28 
     | 
    
         
            +
                it 'is false' do
         
     | 
| 
      
 29 
     | 
    
         
            +
                  expect(preference.binary?).to eq(false)
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
              end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
              describe '#default' do
         
     | 
| 
      
 34 
     | 
    
         
            +
                it 'returns the default preference value' do
         
     | 
| 
      
 35 
     | 
    
         
            +
                  expect(preference.default).to eq('red')
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
              describe '#lookup' do
         
     | 
| 
      
 40 
     | 
    
         
            +
                it 'returns the value at the supplied index' do
         
     | 
| 
      
 41 
     | 
    
         
            +
                  expect(preference.lookup(0)).to eq('red')
         
     | 
| 
      
 42 
     | 
    
         
            +
                  expect(preference.lookup(1)).to eq('white')
         
     | 
| 
      
 43 
     | 
    
         
            +
                  expect(preference.lookup(2)).to be_nil
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
              end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
              describe '#to_db' do
         
     | 
| 
      
 48 
     | 
    
         
            +
                it 'returns the value index for the value' do
         
     | 
| 
      
 49 
     | 
    
         
            +
                  expect(preference.to_db('red')).to eq(0)
         
     | 
| 
      
 50 
     | 
    
         
            +
                  expect(preference.to_db('white')).to eq(1)
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
              context 'the preference is binary' do
         
     | 
| 
      
 55 
     | 
    
         
            +
                subject(:preference) do
         
     | 
| 
      
 56 
     | 
    
         
            +
                  definition = UserPreferences.definitions[:food][:vegetarian]
         
     | 
| 
      
 57 
     | 
    
         
            +
                  UserPreferences::PreferenceDefinition.new(definition, :food, :vegetarian)
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                describe '#permitted_values' do
         
     | 
| 
      
 61 
     | 
    
         
            +
                  it 'returns an array of booleans' do
         
     | 
| 
      
 62 
     | 
    
         
            +
                    expect(preference.permitted_values).to eq([false, true])
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                describe '#binary?' do
         
     | 
| 
      
 67 
     | 
    
         
            +
                  it 'is true' do
         
     | 
| 
      
 68 
     | 
    
         
            +
                    expect(preference.binary?).to eq(true)
         
     | 
| 
      
 69 
     | 
    
         
            +
                  end
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                describe '#default' do
         
     | 
| 
      
 73 
     | 
    
         
            +
                  it 'returns the default boolean' do
         
     | 
| 
      
 74 
     | 
    
         
            +
                    expect(preference.default).to eq(false)
         
     | 
| 
      
 75 
     | 
    
         
            +
                  end
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                describe '#to_db' do
         
     | 
| 
      
 79 
     | 
    
         
            +
                  it 'casts true/false strings to booleans' do
         
     | 
| 
      
 80 
     | 
    
         
            +
                    expect(preference.to_db('false')).to eq(0)
         
     | 
| 
      
 81 
     | 
    
         
            +
                    expect(preference.to_db('true')).to eq(1)
         
     | 
| 
      
 82 
     | 
    
         
            +
                  end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                  it 'casts integers to booleans' do
         
     | 
| 
      
 85 
     | 
    
         
            +
                    expect(preference.to_db(0)).to eq(0)
         
     | 
| 
      
 86 
     | 
    
         
            +
                    expect(preference.to_db(1)).to eq(1)
         
     | 
| 
      
 87 
     | 
    
         
            +
                  end
         
     | 
| 
      
 88 
     | 
    
         
            +
                end
         
     | 
| 
      
 89 
     | 
    
         
            +
              end
         
     | 
| 
      
 90 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,79 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe UserPreferences::Preference do
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              let(:user) { User.create }
         
     | 
| 
      
 6 
     | 
    
         
            +
              subject(:preference) { UserPreferences::Preference.new(category: :food, name: :wine, user: user) }
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              it 'must have a name' do
         
     | 
| 
      
 9 
     | 
    
         
            +
                stub_definition
         
     | 
| 
      
 10 
     | 
    
         
            +
                preference.name = nil
         
     | 
| 
      
 11 
     | 
    
         
            +
                expect(preference.valid?).to eq(false)
         
     | 
| 
      
 12 
     | 
    
         
            +
                expect(preference.errors.full_messages).to include("Name can't be blank")
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              it 'must have a category' do
         
     | 
| 
      
 16 
     | 
    
         
            +
                stub_definition
         
     | 
| 
      
 17 
     | 
    
         
            +
                preference.category = nil
         
     | 
| 
      
 18 
     | 
    
         
            +
                expect(preference.valid?).to eq(false)
         
     | 
| 
      
 19 
     | 
    
         
            +
                expect(preference.errors.full_messages).to include("Category can't be blank")
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              it 'must have a value' do
         
     | 
| 
      
 23 
     | 
    
         
            +
                expect(preference.valid?).to eq(false)
         
     | 
| 
      
 24 
     | 
    
         
            +
                expect(preference.errors.full_messages).to include("Value is not included in the list")
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              it 'must have a user' do
         
     | 
| 
      
 28 
     | 
    
         
            +
                preference.user = nil
         
     | 
| 
      
 29 
     | 
    
         
            +
                expect(preference.valid?).to eq(false)
         
     | 
| 
      
 30 
     | 
    
         
            +
                expect(preference.errors.full_messages).to include("User can't be blank")
         
     | 
| 
      
 31 
     | 
    
         
            +
              end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
              it 'must have a unique name, scoped within user and category' do
         
     | 
| 
      
 34 
     | 
    
         
            +
                preference.update_value! 'white'
         
     | 
| 
      
 35 
     | 
    
         
            +
                second_preference = UserPreferences::Preference.new(category: :food, name: :wine, user: user, value: 1)
         
     | 
| 
      
 36 
     | 
    
         
            +
                expect(second_preference.valid?).to eq(false)
         
     | 
| 
      
 37 
     | 
    
         
            +
                expect(second_preference.errors.full_messages).to include('Name has already been taken')
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
              it 'must have a valid value' do
         
     | 
| 
      
 41 
     | 
    
         
            +
                expect(preference.valid?).to eq(false)
         
     | 
| 
      
 42 
     | 
    
         
            +
                expect(preference.errors.full_messages).to include("Value is not included in the list")
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
              describe '#update_value' do
         
     | 
| 
      
 46 
     | 
    
         
            +
                it 'persists the updated the value' do
         
     | 
| 
      
 47 
     | 
    
         
            +
                  preference.update_value! 'white'
         
     | 
| 
      
 48 
     | 
    
         
            +
                  expect(preference.reload.value).to eq('white')
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                context 'the validation fails' do
         
     | 
| 
      
 52 
     | 
    
         
            +
                  it 'raises' do
         
     | 
| 
      
 53 
     | 
    
         
            +
                    expect { preference.update_value! 'sparkling' }.to raise_error(ActiveRecord::RecordInvalid)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
              end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
              describe '#value' do
         
     | 
| 
      
 59 
     | 
    
         
            +
                it 'casts the persisted value index back to its human readable form' do
         
     | 
| 
      
 60 
     | 
    
         
            +
                  preference.update_value!('red')
         
     | 
| 
      
 61 
     | 
    
         
            +
                  expect(preference.attributes['value']).to eq(0)
         
     | 
| 
      
 62 
     | 
    
         
            +
                  expect(preference.value).to eq('red')
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
              end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
              describe '#definition' do
         
     | 
| 
      
 67 
     | 
    
         
            +
                it 'returns the preference definition' do
         
     | 
| 
      
 68 
     | 
    
         
            +
                  expect(preference.definition).to be_kind_of(UserPreferences::PreferenceDefinition)
         
     | 
| 
      
 69 
     | 
    
         
            +
                end
         
     | 
| 
      
 70 
     | 
    
         
            +
              end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
              private
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
              def stub_definition
         
     | 
| 
      
 75 
     | 
    
         
            +
                preference.stub(:attributes).and_return({'value' => 1})
         
     | 
| 
      
 76 
     | 
    
         
            +
                preference.stub(:value).and_return('white')
         
     | 
| 
      
 77 
     | 
    
         
            +
                preference.stub(:permitted_values).and_return(['white'])
         
     | 
| 
      
 78 
     | 
    
         
            +
              end
         
     | 
| 
      
 79 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,44 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'yaml'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            describe UserPreferences do
         
     | 
| 
      
 5 
     | 
    
         
            +
              it 'should be valid' do
         
     | 
| 
      
 6 
     | 
    
         
            +
                expect(UserPreferences).to be_a(Module)
         
     | 
| 
      
 7 
     | 
    
         
            +
              end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              describe '.[]' do
         
     | 
| 
      
 10 
     | 
    
         
            +
                it 'returns a preference definition instance for supplied category and name' do
         
     | 
| 
      
 11 
     | 
    
         
            +
                  result = UserPreferences[:food, :wine]
         
     | 
| 
      
 12 
     | 
    
         
            +
                  expect(result).to be_kind_of(UserPreferences::PreferenceDefinition)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  expect(result.category).to eq(:food)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  expect(result.name).to eq(:wine)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  expect(result.default).to eq('red')
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                context "the category doesn't exist" do
         
     | 
| 
      
 19 
     | 
    
         
            +
                  it 'returns nil' do
         
     | 
| 
      
 20 
     | 
    
         
            +
                    expect(UserPreferences[:fashion, :hats]).to be_nil
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                context "the name doesn't exist" do
         
     | 
| 
      
 25 
     | 
    
         
            +
                  it 'returns nil' do
         
     | 
| 
      
 26 
     | 
    
         
            +
                    expect(UserPreferences[:food, :dressing]).to be_nil
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              describe '.defaults' do
         
     | 
| 
      
 32 
     | 
    
         
            +
                it 'returns the defaults from definitions' do
         
     | 
| 
      
 33 
     | 
    
         
            +
                  expect_any_instance_of(UserPreferences::Defaults).to receive(:get)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  UserPreferences.defaults(:food)
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
              describe '.definitions' do
         
     | 
| 
      
 39 
     | 
    
         
            +
                it 'returns the loaded preference yml' do
         
     | 
| 
      
 40 
     | 
    
         
            +
                  file = File.expand_path("../fixtures/user_preferences.yml", __FILE__)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  expect(UserPreferences.definitions).to eq(YAML.load_file(file).with_indifferent_access)
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
      
 44 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # coding: utf-8
         
     | 
| 
      
 2 
     | 
    
         
            +
            lib = File.expand_path('../lib', __FILE__)
         
     | 
| 
      
 3 
     | 
    
         
            +
            $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'user_preferences/version'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            Gem::Specification.new do |spec|
         
     | 
| 
      
 7 
     | 
    
         
            +
              spec.name          = "user_preferences"
         
     | 
| 
      
 8 
     | 
    
         
            +
              spec.version       = UserPreferences::VERSION
         
     | 
| 
      
 9 
     | 
    
         
            +
              spec.authors       = ["Andy Dust"]
         
     | 
| 
      
 10 
     | 
    
         
            +
              spec.email         = ["adust@mubi.com"]
         
     | 
| 
      
 11 
     | 
    
         
            +
              spec.summary       = "User preferences gem for Rails."
         
     | 
| 
      
 12 
     | 
    
         
            +
              spec.description   = %Q{user_preference is a small library for setting and getting categorized user preferences.
         
     | 
| 
      
 13 
     | 
    
         
            +
                                  Supports both binary and multivalue preferences and default values. }
         
     | 
| 
      
 14 
     | 
    
         
            +
              spec.homepage      = "http://github.com/mubi/user_preferences"
         
     | 
| 
      
 15 
     | 
    
         
            +
              spec.license       = "MIT"
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              spec.files         = `git ls-files -z`.split("\x0")
         
     | 
| 
      
 18 
     | 
    
         
            +
              spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
         
     | 
| 
      
 19 
     | 
    
         
            +
              spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
         
     | 
| 
      
 20 
     | 
    
         
            +
              spec.require_paths = ["lib"]
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              spec.add_dependency 'activerecord', '>= 3.0'
         
     | 
| 
      
 23 
     | 
    
         
            +
              spec.add_dependency 'activesupport', '>= 3.0'
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              spec.add_development_dependency "bundler", "~> 1.7"
         
     | 
| 
      
 26 
     | 
    
         
            +
              spec.add_development_dependency "rake", "~> 10.0"
         
     | 
| 
      
 27 
     | 
    
         
            +
              spec.add_development_dependency 'rspec', '~> 2.14'
         
     | 
| 
      
 28 
     | 
    
         
            +
              spec.add_development_dependency 'sqlite3', '~> 1.3'
         
     | 
| 
      
 29 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    ADDED
    
    | 
         @@ -0,0 +1,167 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification
         
     | 
| 
      
 2 
     | 
    
         
            +
            name: user_preferences
         
     | 
| 
      
 3 
     | 
    
         
            +
            version: !ruby/object:Gem::Version
         
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.0.1
         
     | 
| 
      
 5 
     | 
    
         
            +
            platform: ruby
         
     | 
| 
      
 6 
     | 
    
         
            +
            authors:
         
     | 
| 
      
 7 
     | 
    
         
            +
            - Andy Dust
         
     | 
| 
      
 8 
     | 
    
         
            +
            autorequire: 
         
     | 
| 
      
 9 
     | 
    
         
            +
            bindir: bin
         
     | 
| 
      
 10 
     | 
    
         
            +
            cert_chain: []
         
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2014-09-29 00:00:00.000000000 Z
         
     | 
| 
      
 12 
     | 
    
         
            +
            dependencies:
         
     | 
| 
      
 13 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 14 
     | 
    
         
            +
              name: activerecord
         
     | 
| 
      
 15 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 16 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 17 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 18 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 19 
     | 
    
         
            +
                    version: '3.0'
         
     | 
| 
      
 20 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 21 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 22 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 23 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 24 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 25 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 26 
     | 
    
         
            +
                    version: '3.0'
         
     | 
| 
      
 27 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 28 
     | 
    
         
            +
              name: activesupport
         
     | 
| 
      
 29 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 30 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 31 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 32 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 33 
     | 
    
         
            +
                    version: '3.0'
         
     | 
| 
      
 34 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 35 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 36 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 37 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 38 
     | 
    
         
            +
                - - ! '>='
         
     | 
| 
      
 39 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 40 
     | 
    
         
            +
                    version: '3.0'
         
     | 
| 
      
 41 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 42 
     | 
    
         
            +
              name: bundler
         
     | 
| 
      
 43 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 44 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 45 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 46 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 47 
     | 
    
         
            +
                    version: '1.7'
         
     | 
| 
      
 48 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 49 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 50 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 51 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 52 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 53 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 54 
     | 
    
         
            +
                    version: '1.7'
         
     | 
| 
      
 55 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 56 
     | 
    
         
            +
              name: rake
         
     | 
| 
      
 57 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 58 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 59 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 60 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 61 
     | 
    
         
            +
                    version: '10.0'
         
     | 
| 
      
 62 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 63 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 64 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 65 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 66 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 67 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 68 
     | 
    
         
            +
                    version: '10.0'
         
     | 
| 
      
 69 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 70 
     | 
    
         
            +
              name: rspec
         
     | 
| 
      
 71 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 72 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 73 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 74 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 75 
     | 
    
         
            +
                    version: '2.14'
         
     | 
| 
      
 76 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 77 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 78 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 79 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 80 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 81 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 82 
     | 
    
         
            +
                    version: '2.14'
         
     | 
| 
      
 83 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 84 
     | 
    
         
            +
              name: sqlite3
         
     | 
| 
      
 85 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 86 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 87 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 88 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 89 
     | 
    
         
            +
                    version: '1.3'
         
     | 
| 
      
 90 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 91 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 92 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 93 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 94 
     | 
    
         
            +
                - - ~>
         
     | 
| 
      
 95 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 96 
     | 
    
         
            +
                    version: '1.3'
         
     | 
| 
      
 97 
     | 
    
         
            +
            description: ! "user_preference is a small library for setting and getting categorized
         
     | 
| 
      
 98 
     | 
    
         
            +
              user preferences.\n                      Supports both binary and multivalue preferences
         
     | 
| 
      
 99 
     | 
    
         
            +
              and default values. "
         
     | 
| 
      
 100 
     | 
    
         
            +
            email:
         
     | 
| 
      
 101 
     | 
    
         
            +
            - adust@mubi.com
         
     | 
| 
      
 102 
     | 
    
         
            +
            executables: []
         
     | 
| 
      
 103 
     | 
    
         
            +
            extensions: []
         
     | 
| 
      
 104 
     | 
    
         
            +
            extra_rdoc_files: []
         
     | 
| 
      
 105 
     | 
    
         
            +
            files:
         
     | 
| 
      
 106 
     | 
    
         
            +
            - .gitignore
         
     | 
| 
      
 107 
     | 
    
         
            +
            - Gemfile
         
     | 
| 
      
 108 
     | 
    
         
            +
            - LICENSE.txt
         
     | 
| 
      
 109 
     | 
    
         
            +
            - README.md
         
     | 
| 
      
 110 
     | 
    
         
            +
            - Rakefile
         
     | 
| 
      
 111 
     | 
    
         
            +
            - lib/generators/user_preferences/install_generator.rb
         
     | 
| 
      
 112 
     | 
    
         
            +
            - lib/generators/user_preferences/templates/migration.rb
         
     | 
| 
      
 113 
     | 
    
         
            +
            - lib/generators/user_preferences/templates/user_preferences.yml
         
     | 
| 
      
 114 
     | 
    
         
            +
            - lib/user_preferences.rb
         
     | 
| 
      
 115 
     | 
    
         
            +
            - lib/user_preferences/api.rb
         
     | 
| 
      
 116 
     | 
    
         
            +
            - lib/user_preferences/defaults.rb
         
     | 
| 
      
 117 
     | 
    
         
            +
            - lib/user_preferences/has_preferences.rb
         
     | 
| 
      
 118 
     | 
    
         
            +
            - lib/user_preferences/preference.rb
         
     | 
| 
      
 119 
     | 
    
         
            +
            - lib/user_preferences/preference_definition.rb
         
     | 
| 
      
 120 
     | 
    
         
            +
            - lib/user_preferences/railtie.rb
         
     | 
| 
      
 121 
     | 
    
         
            +
            - lib/user_preferences/version.rb
         
     | 
| 
      
 122 
     | 
    
         
            +
            - spec/fixtures/user_preferences.yml
         
     | 
| 
      
 123 
     | 
    
         
            +
            - spec/migrations/001_create_users.rb
         
     | 
| 
      
 124 
     | 
    
         
            +
            - spec/migrations/002_create_preferences.rb
         
     | 
| 
      
 125 
     | 
    
         
            +
            - spec/spec_helper.rb
         
     | 
| 
      
 126 
     | 
    
         
            +
            - spec/user_preferences/api_spec.rb
         
     | 
| 
      
 127 
     | 
    
         
            +
            - spec/user_preferences/defaults_spec.rb
         
     | 
| 
      
 128 
     | 
    
         
            +
            - spec/user_preferences/has_preferences_spec.rb
         
     | 
| 
      
 129 
     | 
    
         
            +
            - spec/user_preferences/preference_definition_spec.rb
         
     | 
| 
      
 130 
     | 
    
         
            +
            - spec/user_preferences/preference_spec.rb
         
     | 
| 
      
 131 
     | 
    
         
            +
            - spec/user_preferences_spec.rb
         
     | 
| 
      
 132 
     | 
    
         
            +
            - user_preferences.gemspec
         
     | 
| 
      
 133 
     | 
    
         
            +
            homepage: http://github.com/mubi/user_preferences
         
     | 
| 
      
 134 
     | 
    
         
            +
            licenses:
         
     | 
| 
      
 135 
     | 
    
         
            +
            - MIT
         
     | 
| 
      
 136 
     | 
    
         
            +
            metadata: {}
         
     | 
| 
      
 137 
     | 
    
         
            +
            post_install_message: 
         
     | 
| 
      
 138 
     | 
    
         
            +
            rdoc_options: []
         
     | 
| 
      
 139 
     | 
    
         
            +
            require_paths:
         
     | 
| 
      
 140 
     | 
    
         
            +
            - lib
         
     | 
| 
      
 141 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 142 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 143 
     | 
    
         
            +
              - - ! '>='
         
     | 
| 
      
 144 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 145 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 146 
     | 
    
         
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 147 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 148 
     | 
    
         
            +
              - - ! '>='
         
     | 
| 
      
 149 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 150 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 151 
     | 
    
         
            +
            requirements: []
         
     | 
| 
      
 152 
     | 
    
         
            +
            rubyforge_project: 
         
     | 
| 
      
 153 
     | 
    
         
            +
            rubygems_version: 2.2.1
         
     | 
| 
      
 154 
     | 
    
         
            +
            signing_key: 
         
     | 
| 
      
 155 
     | 
    
         
            +
            specification_version: 4
         
     | 
| 
      
 156 
     | 
    
         
            +
            summary: User preferences gem for Rails.
         
     | 
| 
      
 157 
     | 
    
         
            +
            test_files:
         
     | 
| 
      
 158 
     | 
    
         
            +
            - spec/fixtures/user_preferences.yml
         
     | 
| 
      
 159 
     | 
    
         
            +
            - spec/migrations/001_create_users.rb
         
     | 
| 
      
 160 
     | 
    
         
            +
            - spec/migrations/002_create_preferences.rb
         
     | 
| 
      
 161 
     | 
    
         
            +
            - spec/spec_helper.rb
         
     | 
| 
      
 162 
     | 
    
         
            +
            - spec/user_preferences/api_spec.rb
         
     | 
| 
      
 163 
     | 
    
         
            +
            - spec/user_preferences/defaults_spec.rb
         
     | 
| 
      
 164 
     | 
    
         
            +
            - spec/user_preferences/has_preferences_spec.rb
         
     | 
| 
      
 165 
     | 
    
         
            +
            - spec/user_preferences/preference_definition_spec.rb
         
     | 
| 
      
 166 
     | 
    
         
            +
            - spec/user_preferences/preference_spec.rb
         
     | 
| 
      
 167 
     | 
    
         
            +
            - spec/user_preferences_spec.rb
         
     |