troles 0.5.1 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Design.textile +68 -0
- data/Gemfile +9 -7
- data/Gemfile.lock +81 -76
- data/README.textile +63 -170
- data/VERSION +1 -1
- data/lib/trole.rb +2 -2
- data/lib/trole_groups/storage/base_many.rb +16 -5
- data/lib/troles/adapters/active_record/config.rb +19 -5
- data/lib/troles/api/config.rb +1 -1
- data/lib/troles/api/core.rb +4 -4
- data/lib/troles/common/api/core.rb +13 -14
- data/lib/troles/common/api/read.rb +6 -0
- data/lib/troles/common/api.rb +3 -2
- data/lib/troles/common/config/schema/helpers.rb +30 -11
- data/lib/troles/common/config/schema/role_helpers.rb +5 -5
- data/lib/troles/common/config/valid_roles.rb +4 -3
- data/lib/troles/common/dependencies.rb +1 -1
- data/lib/troles/common/storage.rb +16 -4
- data/lib/troles/common.rb +2 -2
- data/lib/troles/config.rb +4 -4
- data/lib/troles/storage/join_ref_many.rb +13 -0
- data/lib/troles/storage.rb +6 -5
- data/lib/troles.rb +3 -8
- data/spec/active_record/migrations/many/custom_join.rb +31 -0
- data/spec/active_record/migrations/many/join_ref_many.rb +31 -0
- data/spec/active_record/migrations/many/ref_many.rb +0 -8
- data/spec/active_record/models/custom_join.rb +13 -0
- data/spec/active_record/models/join_ref_many.rb +13 -0
- data/spec/active_record/models/ref_many.rb +0 -2
- data/spec/active_record/strategies/many/custom_join_spec.rb +46 -0
- data/spec/active_record/strategies/many/join_ref_many_spec.rb +46 -0
- data/spec/active_record/strategies/many/ref_many_spec.rb +2 -2
- data/spec/generic/models/base_user.rb +2 -1
- data/spec/trole/strategies/embed_one_spec.rb +1 -1
- data/spec/trole/strategies/ref_one_spec.rb +1 -1
- data/spec/trole/strategies/string_one_spec.rb +1 -1
- data/spec/troles/common/multi_roles_spec.rb +7 -13
- data/spec/troles/strategies/bit_many_spec.rb +1 -1
- data/spec/troles/strategies/embed_many_spec.rb +1 -1
- data/spec/troles/strategies/ref_many_spec.rb +1 -1
- data/spec/troles/strategies/string_many_spec.rb +1 -1
- data/spec/troles/strategy_helper.rb +1 -1
- data/troles.gemspec +38 -24
- metadata +72 -42
    
        data/README.textile
    CHANGED
    
    | @@ -13,75 +13,6 @@ The roles list cache of a role subject (fx a user) is only updated (retrieved fr | |
| 13 13 |  | 
| 14 14 | 
             
            Note: Troles is a full redesign of _roles generic_ and company, using lessons learned. Troles uses a much cleaner design. It is aimed at being easy to extend and easy to create adapters for etc.
         | 
| 15 15 |  | 
| 16 | 
            -
            h2. Status June 7, 2011 
         | 
| 17 | 
            -
             | 
| 18 | 
            -
            I'm now in the process of implementing TroleGroups! Finally a fully integrated role groups solution for Ruby and Rails ;)
         | 
| 19 | 
            -
            Due to the flexible design ot troles and trole, I can reuse the same infrastructure/design/architecture to implement trole_groups! 
         | 
| 20 | 
            -
            *Yiii...haaa!*   
         | 
| 21 | 
            -
                             
         | 
| 22 | 
            -
            "A RoleGroup is simply another role_subject and can thus be configured with a given role strategy!" 
         | 
| 23 | 
            -
             | 
| 24 | 
            -
            Simple and Sweet :)
         | 
| 25 | 
            -
             | 
| 26 | 
            -
            h2. The Ruby Class Decorator (RCD) pattern is born!!!
         | 
| 27 | 
            -
             | 
| 28 | 
            -
            I realized a while ago that I had come upon a very cool kind of "class decorator" design pattern that can be generalized to any gem that "decorates" a class (or perhaps multiple classes) with certain behavior. Gems such as _act_as_xxx_ come to mind fx.
         | 
| 29 | 
            -
             | 
| 30 | 
            -
            I will soon try to generalize this pattern into a separate project and then have trole, troles and trole_groups all use this to leverage their functionality. Also, I will make use of an idea that I just had while taking a cold shower (try it sometime if you have a mind block!). 
         | 
| 31 | 
            -
            The idea is to have an internal default API, and an exposed API that by default is a reflection of the internal API. However the user is free to block parts of the API from being loaded or define his own external API that utilizes the inner API.
         | 
| 32 | 
            -
             | 
| 33 | 
            -
            Since this is a general purpose decorator pattern, there should also be a kind of Index where you can see which decorators have been applied to an object and what API each such decorator exposes! Cool stuff!!!!  
         | 
| 34 | 
            -
             | 
| 35 | 
            -
            Some example usage:
         | 
| 36 | 
            -
            <pre>
         | 
| 37 | 
            -
            return if !User.has_decorator?(:troles_group)
         | 
| 38 | 
            -
            # do some trole group stuff!!!
         | 
| 39 | 
            -
             | 
| 40 | 
            -
            # later ...
         | 
| 41 | 
            -
             | 
| 42 | 
            -
            # list all decorators currently applied to the User class
         | 
| 43 | 
            -
            puts User.decorators
         | 
| 44 | 
            -
             | 
| 45 | 
            -
            # print the methods of the public Write API for the :troles_group decorator :)
         | 
| 46 | 
            -
            puts User.decorator(:troles_group).public_api(:write).methods.sort
         | 
| 47 | 
            -
             | 
| 48 | 
            -
            # print the methods of the internal Write API for the :troles_group decorator :)
         | 
| 49 | 
            -
            puts User.decorator(:troles_group).internal_api(:write).methods.sort
         | 
| 50 | 
            -
            </pre>
         | 
| 51 | 
            -
             | 
| 52 | 
            -
            I will use the new Decorator API very soon... sth like this:
         | 
| 53 | 
            -
             | 
| 54 | 
            -
            <pre>
         | 
| 55 | 
            -
            User.decorator(:trole_groups).configure(:strategy) :ref_one do |strategy|
         | 
| 56 | 
            -
              strategy.orm = :mongoid
         | 
| 57 | 
            -
              strategy.auto_load = true
         | 
| 58 | 
            -
            end.configure!  
         | 
| 59 | 
            -
            </pre>
         | 
| 60 | 
            -
             | 
| 61 | 
            -
            Awesome! 
         | 
| 62 | 
            -
             | 
| 63 | 
            -
            h3. Yard documentation
         | 
| 64 | 
            -
             | 
| 65 | 
            -
            I'm using "Yard":http://rubydoc.info/docs/yard/file/docs/GettingStarted.md for documentation.
         | 
| 66 | 
            -
             | 
| 67 | 
            -
            <pre>$ yard server
         | 
| 68 | 
            -
             | 
| 69 | 
            -
            From browser, go to: http://0.0.0.0:8808/  # then you can enjoy! the nice documentation :) 
         | 
| 70 | 
            -
            </pre>
         | 
| 71 | 
            -
             | 
| 72 | 
            -
            There is now support for Caching and invalidation of the _role_list_ when the roles change. 
         | 
| 73 | 
            -
            The specs now validate that caching works as it should.
         | 
| 74 | 
            -
             | 
| 75 | 
            -
            Please help out to finalize this project! :)
         | 
| 76 | 
            -
             | 
| 77 | 
            -
            h2. Bug hunting by running specs
         | 
| 78 | 
            -
             | 
| 79 | 
            -
            Run specs for at most one strategy at the time for now...
         | 
| 80 | 
            -
             | 
| 81 | 
            -
            @$ rspec spec/troles/strategies/bit_many_spec.rb@ 
         | 
| 82 | 
            -
             | 
| 83 | 
            -
            Please see the document _spec/Guide to running specs.textile_ where I advise on how best to do "bug hunting" to help get this project off the ground!
         | 
| 84 | 
            -
             | 
| 85 16 | 
             
            h2. Role strategies
         | 
| 86 17 |  | 
| 87 18 | 
             
            The following lists the role strategies to be supported
         | 
| @@ -131,47 +62,47 @@ These strategies can be implemented for any data store using any schema format. | |
| 131 62 |  | 
| 132 63 | 
             
            h3. Using roles strategies with Users and User accounts
         | 
| 133 64 |  | 
| 134 | 
            -
            Roles are assigned to role subject classes such as _User_ or _UserAccount_ (YES, Devise can have multiple user accounts!). The class that has a role strategy assigned is referred to as the _role subject class_. 
         | 
| 135 | 
            -
            Different role subject classes can have different role strategies! 
         | 
| 65 | 
            +
            Roles are assigned to role subject classes such as _User_ or _UserAccount_ (YES, Devise can have multiple user accounts!). The class that has a role strategy assigned is referred to as the _role subject class_. Different role subject classes can have different role strategies! 
         | 
| 136 66 |  | 
| 137 67 | 
             
            When using Devise this could translate fx into a UserAccount with a "many roles" strategy and an AdminAccount with a "single role" strategy (or vice versa). 
         | 
| 138 68 |  | 
| 139 69 | 
             
            Example:
         | 
| 140 70 |  | 
| 141 | 
            -
            <pre> 
         | 
| 142 | 
            -
             | 
| 143 | 
            -
              require 'troles/macros'
         | 
| 144 | 
            -
              
         | 
| 145 | 
            -
              class UserAccount
         | 
| 146 | 
            -
                troles_strategy(:string_many).configure!
         | 
| 147 | 
            -
              end
         | 
| 71 | 
            +
            <pre>require 'troles'
         | 
| 72 | 
            +
            require 'troles/macros'
         | 
| 148 73 |  | 
| 149 | 
            -
             | 
| 150 | 
            -
             | 
| 151 | 
            -
             | 
| 74 | 
            +
            class UserAccount
         | 
| 75 | 
            +
              troles_strategy(:string_many).configure!
         | 
| 76 | 
            +
            end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
            class AdminAccount
         | 
| 79 | 
            +
              troles_strategy(:bit_one, :static => true).configure!
         | 
| 80 | 
            +
            end  
         | 
| 152 81 | 
             
            </pre>
         | 
| 153 82 |  | 
| 154 | 
            -
            The special troles macros must be enabled my requiring the  | 
| 83 | 
            +
            The special troles macros must currently be enabled my requiring the _troles/macros_ file. 
         | 
| 84 | 
            +
            If the troles macros are not included like this, the troles DSL can be made available for an individual class by including a specific Strategy.
         | 
| 85 | 
            +
            Using the macros like in the above example is much easier and is recommended.
         | 
| 155 86 |  | 
| 156 87 | 
             
            h3. Macro options
         | 
| 157 88 |  | 
| 158 89 | 
             
            The @:static => true@ options is used to indicate, that the role can not be changed after being initially set. 
         | 
| 159 | 
            -
            But we are getting ahead of ourselves... (more on this later).
         | 
| 160 | 
            -
             | 
| 90 | 
            +
            But we are getting ahead of ourselves... (more on this later). Troles can easily be extended to support other macro options if needed.
         | 
| 91 | 
            +
            Note: This static roles functionality is currently in-progress... (used to work but under change).
         | 
| 161 92 |  | 
| 162 93 | 
             
            h2. Roles API
         | 
| 163 94 |  | 
| 164 95 | 
             
            The Roles API can be divided into:
         | 
| 165 96 |  | 
| 166 97 | 
             
            * Core
         | 
| 167 | 
            -
            * Event | 
| 98 | 
            +
            * Event
         | 
| 99 | 
            +
            * Cache
         | 
| 168 100 | 
             
            * Read
         | 
| 169 101 | 
             
            * Write
         | 
| 170 | 
            -
            * Store
         | 
| 171 102 | 
             
            * Validation
         | 
| 172 103 | 
             
            * Operations object
         | 
| 173 104 |  | 
| 174 | 
            -
            There is an  | 
| 105 | 
            +
            There is an equivalent Trole API for single role strategies.
         | 
| 175 106 |  | 
| 176 107 | 
             
            h3. Event/Cache API
         | 
| 177 108 |  | 
| @@ -181,46 +112,33 @@ can have subscribers to events. | |
| 181 112 |  | 
| 182 113 | 
             
            Also any write event to the datastore should be predicated on _#static_roles?_ not being true for the user (thus ensuring guest roles are never updated).
         | 
| 183 114 |  | 
| 184 | 
            -
             | 
| 185 | 
            -
            <pre>
         | 
| 186 | 
            -
            User
         | 
| 187 | 
            -
              after_save: update_roles # event handler
         | 
| 188 | 
            -
            </pre>
         | 
| 189 | 
            -
             | 
| 190 | 
            -
            <pre>
         | 
| 191 | 
            -
            module Troles::Api::Event
         | 
| 192 | 
            -
              def update_roles
         | 
| 193 | 
            -
              def publish_change event
         | 
| 194 | 
            -
            </pre>
         | 
| 115 | 
            +
            @User.after_save: update_roles # event handler@
         | 
| 195 116 |  | 
| 196 117 | 
             
            h3. Roles Read API
         | 
| 197 118 |  | 
| 198 119 | 
             
            This API operates directly on a user, fx _user#has_role?_
         | 
| 199 120 |  | 
| 200 | 
            -
            <pre> | 
| 201 | 
            -
             | 
| 202 | 
            -
             | 
| 203 | 
            -
              user.has_any_roles? :editor, :admin  
         | 
| 121 | 
            +
            <pre>user.has_role? :admin
         | 
| 122 | 
            +
            user.is_role? :editor
         | 
| 123 | 
            +
            user.has_any_roles? :editor, :admin  
         | 
| 204 124 | 
             
            </pre>
         | 
| 205 125 |  | 
| 206 126 | 
             
            h3. Roles Write API
         | 
| 207 127 |  | 
| 208 128 | 
             
            This API operates directly on a user, fx _user#has_role?_
         | 
| 209 129 |  | 
| 210 | 
            -
            <pre>
         | 
| 211 | 
            -
             | 
| 212 | 
            -
              user.remove_role :editor
         | 
| 130 | 
            +
            <pre>user.add_role :admin
         | 
| 131 | 
            +
            user.remove_role :editor
         | 
| 213 132 | 
             
            </pre>
         | 
| 214 133 |  | 
| 215 134 | 
             
            h3. Roles Operations object
         | 
| 216 135 |  | 
| 217 136 | 
             
            The Roles Operations object is available on user#roles
         | 
| 218 137 |  | 
| 219 | 
            -
            <pre>
         | 
| 220 | 
            -
             | 
| 221 | 
            -
             | 
| 222 | 
            -
             | 
| 223 | 
            -
              user.roles.clear!  
         | 
| 138 | 
            +
            <pre>user.roles + :admin
         | 
| 139 | 
            +
            user.roles - :editor
         | 
| 140 | 
            +
            user.roles << [:editor, :admin]
         | 
| 141 | 
            +
            user.roles.clear!  
         | 
| 224 142 | 
             
            </pre>
         | 
| 225 143 |  | 
| 226 144 | 
             
            h3. Creating a custom Data Store Adapter (DSA)
         | 
| @@ -231,11 +149,9 @@ Note that :single role strategies always have the namespace 'Trole' whereas for | |
| 231 149 |  | 
| 232 150 | 
             
            A custom Config class for _:single_ role strategies using _Mongoid_ could look sth. like this:
         | 
| 233 151 |  | 
| 234 | 
            -
            <pre>
         | 
| 235 | 
            -
            module Trole::Mongoid
         | 
| 152 | 
            +
            <pre>module Trole::Mongoid
         | 
| 236 153 | 
             
              class Config < Troles::Common::Config  
         | 
| 237 | 
            -
             | 
| 238 | 
            -
                def initialize subject_class, options = {}
         | 
| 154 | 
            +
                def initialize subject, options = {}
         | 
| 239 155 | 
             
                  super
         | 
| 240 156 | 
             
                end
         | 
| 241 157 |  | 
| @@ -265,8 +181,7 @@ See the _schema_helpers.rb_ file for more details! | |
| 265 181 |  | 
| 266 182 | 
             
            Example: Config class for :many roles strategies with _Mongoid_
         | 
| 267 183 |  | 
| 268 | 
            -
            <pre>
         | 
| 269 | 
            -
            module Troles::Mongoid
         | 
| 184 | 
            +
            <pre>module Troles::Mongoid
         | 
| 270 185 | 
             
              class Config < Troles::Common::Config  
         | 
| 271 186 |  | 
| 272 187 | 
             
                def initialize subject_class, options = {}
         | 
| @@ -305,8 +220,7 @@ Note: #set_role is only required for :single role strategies. In this case #set_ | |
| 305 220 |  | 
| 306 221 | 
             
            Example custom SSA (encrypted role string):
         | 
| 307 222 |  | 
| 308 | 
            -
            <pre>
         | 
| 309 | 
            -
            module Trole::Storage
         | 
| 223 | 
            +
            <pre>module Trole::Storage
         | 
| 310 224 | 
             
              module EncryptedStringOne < BaseOne
         | 
| 311 225 | 
             
                def initialize role_subject        
         | 
| 312 226 | 
             
                  super
         | 
| @@ -344,8 +258,7 @@ h3. Custom Storage for an ORM (or data store) | |
| 344 258 |  | 
| 345 259 | 
             
            In some cases it is useful to rewrite part of the base Storage functionality. One such method is #find_roles.
         | 
| 346 260 |  | 
| 347 | 
            -
            <pre>
         | 
| 348 | 
            -
            module Troles::Storage
         | 
| 261 | 
            +
            <pre>module Troles::Storage
         | 
| 349 262 | 
             
              class BaseMany < Troles::Common::Storage
         | 
| 350 263 | 
             
                def find_roles *roles
         | 
| 351 264 | 
             
                  role_model.where(:name => roles.flatten).all
         | 
| @@ -357,8 +270,7 @@ end | |
| 357 270 | 
             
            Active Record and Mongoid both implement the above API, so no need to customize this method in the Storage adapter.
         | 
| 358 271 | 
             
            For most other ORMs/data stores you will likely have to write your own logic to achieve this.
         | 
| 359 272 |  | 
| 360 | 
            -
            <pre>
         | 
| 361 | 
            -
            module Troles::MongoidStorage
         | 
| 273 | 
            +
            <pre>module Troles::MongoidStorage
         | 
| 362 274 | 
             
              class RefMany < Troles::Storage::BaseMany
         | 
| 363 275 |  | 
| 364 276 | 
             
                def find_roles *roles
         | 
| @@ -371,10 +283,9 @@ h3. Custom data marshaller | |
| 371 283 |  | 
| 372 284 | 
             
            You can also use a custom "Marshaller":http://en.wikipedia.org/wiki/Marshalling_%28computer_science%29 to store the roles in a non-standard format. This is used for the _BitMany_ strategy, which has a special _Marshaller::BitMask_ class which handles conversion between a list of symbols to an _Integer_ bitmap representing it (relative to a valid roles list!). You can create your own _Marshaller_, fx to encrypt the roles info or whatever you like!
         | 
| 373 285 |  | 
| 374 | 
            -
            <pre>
         | 
| 375 | 
            -
            module Troles::Marshaller
         | 
| 286 | 
            +
            <pre>module Troles::Marshaller
         | 
| 376 287 | 
             
              class Encryption < Generic
         | 
| 377 | 
            -
                def initialize  | 
| 288 | 
            +
                def initialize subject
         | 
| 378 289 | 
             
                  super
         | 
| 379 290 | 
             
                end
         | 
| 380 291 |  | 
| @@ -395,8 +306,7 @@ h3. Using a custom Marshaller in a Storage implementation | |
| 395 306 |  | 
| 396 307 | 
             
            The following example is taken from the BitOne Storage implementation that is part of troles:
         | 
| 397 308 |  | 
| 398 | 
            -
            <pre>
         | 
| 399 | 
            -
            require 'troles/common/marshaller'
         | 
| 309 | 
            +
            <pre>require 'troles/common/marshaller'
         | 
| 400 310 |  | 
| 401 311 | 
             
            module Trole::Storage 
         | 
| 402 312 | 
             
              class BitOne < BaseOne
         | 
| @@ -432,10 +342,9 @@ Note that the same _BitMask_ marshaller is also reused in the _BitMany_ storage! | |
| 432 342 |  | 
| 433 343 | 
             
            The _#bitmask_ method returns an instance of the _Bitmask_ marshaller, instantiated with the _role_subject_ (the instance that has the #role_list method, typically the user or user account). To use the _Encryption_ marshaller in an Encryption storage we could create a _#marshaller_ method:
         | 
| 434 344 |  | 
| 435 | 
            -
            <pre>
         | 
| 436 | 
            -
               | 
| 437 | 
            -
             | 
| 438 | 
            -
              end  
         | 
| 345 | 
            +
            <pre>def marshaller
         | 
| 346 | 
            +
              @marshaller ||= Troles::Marshaller::Encryption.new role_subject
         | 
| 347 | 
            +
            end  
         | 
| 439 348 | 
             
            </pre>
         | 
| 440 349 |  | 
| 441 350 | 
             
            Then use @marshaller.write(*roles)@ in the _set_xxxx_ methods and @marshaller.read@ in _#display_roles_ method of the storage, as needed.
         | 
| @@ -456,8 +365,7 @@ You rarely need to implement a custom Strategy module or custom API implementati | |
| 456 365 | 
             
            Here is an example of a custom Strategy implementation for _EncryptedStringMany_ that simply wraps the _BaseMany_ strategy implementation.
         | 
| 457 366 | 
             
            This example demonstrates how you can easily override functionality with custom implementations by including modules "on top".
         | 
| 458 367 |  | 
| 459 | 
            -
            <pre>
         | 
| 460 | 
            -
            module Troles::Strategy
         | 
| 368 | 
            +
            <pre>module Troles::Strategy
         | 
| 461 369 | 
             
              module EncryptedStringMany
         | 
| 462 370 |  | 
| 463 371 | 
             
                # What to add to the role subject class when this role strategy is included
         | 
| @@ -485,28 +393,25 @@ In some cases you need a custom Base strategy that contains common functionality | |
| 485 393 |  | 
| 486 394 | 
             
            Example: _BaseMany_, used as the base for all Many roles strategies
         | 
| 487 395 |  | 
| 488 | 
            -
            <pre>
         | 
| 489 | 
            -
              module  | 
| 490 | 
            -
                module  | 
| 491 | 
            -
                   | 
| 492 | 
            -
             | 
| 493 | 
            -
             | 
| 494 | 
            -
                     | 
| 495 | 
            -
             | 
| 496 | 
            -
             | 
| 497 | 
            -
             | 
| 498 | 
            -
                      # base.extend ClassMethods            
         | 
| 499 | 
            -
                    end
         | 
| 396 | 
            +
            <pre>module Troles::Mongoid
         | 
| 397 | 
            +
              module Strategy    
         | 
| 398 | 
            +
                module BaseMany
         | 
| 399 | 
            +
                  # @param [Class] the role subject class for which to include the Role strategy (fx User Account)
         | 
| 400 | 
            +
                  #
         | 
| 401 | 
            +
                  def self.included(base)
         | 
| 402 | 
            +
                    base.send :include, Troles::Strategy::BaseMany        
         | 
| 403 | 
            +
             | 
| 404 | 
            +
                    # base.send :include, InstanceMethods      
         | 
| 405 | 
            +
                    # base.extend ClassMethods            
         | 
| 500 406 | 
             
                  end
         | 
| 501 407 | 
             
                end
         | 
| 502 408 | 
             
              end
         | 
| 409 | 
            +
            end
         | 
| 503 410 | 
             
            </pre>
         | 
| 504 411 |  | 
| 505 412 | 
             
            To use your adapter, simply pass an extra option to the _troles_strategy_ macro:
         | 
| 506 413 |  | 
| 507 | 
            -
             | 
| 508 | 
            -
              User.troles_strategy(:bit_one, :orm => :mongoid).configure!
         | 
| 509 | 
            -
            </pre> 
         | 
| 414 | 
            +
            @User.troles_strategy(:bit_one, :orm => :mongoid).configure!@
         | 
| 510 415 |  | 
| 511 416 | 
             
            This even allows you to use different ORM role strategies/storages for different user accounts simultaneously!!!
         | 
| 512 417 |  | 
| @@ -514,22 +419,17 @@ Using the :auto_load option will 'auto load' (i.e require) the orm adapter from | |
| 514 419 | 
             
            You can include a specific custom (or 3rd party) adapter manually. In the future it will be possible to configure troles with adapters 
         | 
| 515 420 | 
             
            and specify how/where to load them from as part of this configuration!
         | 
| 516 421 |  | 
| 517 | 
            -
             | 
| 518 | 
            -
              User.troles_strategy(:bit_one, :orm => :mongoid, :auto_load => true).configure!
         | 
| 519 | 
            -
            </pre> 
         | 
| 422 | 
            +
            @User.troles_strategy(:bit_one, :orm => :mongoid, :auto_load => true).configure!@
         | 
| 520 423 |  | 
| 521 424 | 
             
            You can also specify some of the options relevant to model configuration on the call to #configure if you like ;) 
         | 
| 522 425 |  | 
| 523 | 
            -
             | 
| 524 | 
            -
              User.troles_strategy(:bit_one, :orm => :active_record, :auto_load => true).configure! :role_model => 'Troll'
         | 
| 525 | 
            -
            </pre> 
         | 
| 426 | 
            +
            @User.troles_strategy(:bit_one, :orm => :active_record, :auto_load => true).configure! :role_model => 'Troll'@
         | 
| 526 427 |  | 
| 527 428 | 
             
            The _troles_strategy_ macro will yield the Config object if you pass it a block. 
         | 
| 528 429 | 
             
            This allows you to configure your stategy with troles inside the block and then call _configure!_ on end of the block.
         | 
| 529 430 | 
             
            Using all this in combination, you could configure it all doing sth. like this:
         | 
| 530 431 |  | 
| 531 | 
            -
            <pre>  
         | 
| 532 | 
            -
            require 'my/own/active_record/adapter'  
         | 
| 432 | 
            +
            <pre>require 'my/own/active_record/adapter'  
         | 
| 533 433 |  | 
| 534 434 | 
             
            User.troles_strategy :bit_one, :orm => :active_record do |c|
         | 
| 535 435 | 
             
              c.auto_load = false
         | 
| @@ -591,14 +491,13 @@ _troles_ will be part of a larger project under development that will go under t | |
| 591 491 |  | 
| 592 492 | 
             
            h3. Guest users
         | 
| 593 493 |  | 
| 594 | 
            -
            From the Devise wiki: | 
| 494 | 
            +
            From the "Devise wiki":https://github.com/plataformatec/devise/wiki/How-To:-Create-a-guest-user
         | 
| 595 495 |  | 
| 596 496 | 
             
            "In some applications, it's useful to have a guest User object to pass around even before the (human) user has registered or logged in. Normally, you want this guest user to persist as long as the browser session persists.
         | 
| 597 497 |  | 
| 598 498 | 
             
            Our approach is to create a guest user object in the database and store its id in session[:guest_user_id]. When (and if) the user registers or logs in, we delete the guest user and clear the session variable. A helper function, current_or_guest_user, returns guest_user if the user is not logged in and current_user if the user is logged in."
         | 
| 599 499 |  | 
| 600 | 
            -
            <pre>
         | 
| 601 | 
            -
            module ApplicationHelper
         | 
| 500 | 
            +
            <pre>module ApplicationHelper
         | 
| 602 501 | 
             
              ...
         | 
| 603 502 | 
             
              # if user is logged in, return current_user, else return guest_user
         | 
| 604 503 | 
             
              def current_or_guest_user
         | 
| @@ -631,9 +530,7 @@ end | |
| 631 530 |  | 
| 632 531 | 
             
            In the new system, I propose the following:
         | 
| 633 532 |  | 
| 634 | 
            -
            <pre | 
| 635 | 
            -
             | 
| 636 | 
            -
            # this will make the current user the guest user account for the given scope!
         | 
| 533 | 
            +
            <pre># this will make the current user the guest user account for the given scope!
         | 
| 637 534 | 
             
            def sign_in_guest scope, options = {}
         | 
| 638 535 | 
             
              warden.set_user(guest_user.account, options.merge!(:scope => scope))
         | 
| 639 536 | 
             
            end
         | 
| @@ -665,8 +562,7 @@ def guest_user | |
| 665 562 | 
             
            end  
         | 
| 666 563 | 
             
            </pre>
         | 
| 667 564 |  | 
| 668 | 
            -
            <pre> | 
| 669 | 
            -
            class GuestUserAccount
         | 
| 565 | 
            +
            <pre>class GuestUserAccount
         | 
| 670 566 | 
             
              troles_strategy(:static_one, :role => :guest) do |c|
         | 
| 671 567 | 
             
                # c.valid_roles = [:guest] not needed!
         | 
| 672 568 | 
             
              end.configure!
         | 
| @@ -685,8 +581,7 @@ end | |
| 685 581 |  | 
| 686 582 | 
             
            Here the special _:static_many_ strategy is used, which means that whatever the _role_list_ is first set to can never change for that user.
         | 
| 687 583 |  | 
| 688 | 
            -
            <pre>
         | 
| 689 | 
            -
            module BaseAccount
         | 
| 584 | 
            +
            <pre>module BaseAccount
         | 
| 690 585 | 
             
              # transfer guest settings to logged_in user/account
         | 
| 691 586 | 
             
              def transfer_guest(guest_user)
         | 
| 692 587 | 
             
              end  
         | 
| @@ -711,8 +606,7 @@ end | |
| 711 606 |  | 
| 712 607 | 
             
            And here the User setup:
         | 
| 713 608 |  | 
| 714 | 
            -
            <pre>
         | 
| 715 | 
            -
            module BaseUser
         | 
| 609 | 
            +
            <pre>module BaseUser
         | 
| 716 610 | 
             
            end
         | 
| 717 611 |  | 
| 718 612 | 
             
            class User
         | 
| @@ -726,8 +620,7 @@ class User | |
| 726 620 | 
             
            end  
         | 
| 727 621 | 
             
            </pre>
         | 
| 728 622 |  | 
| 729 | 
            -
            <pre>
         | 
| 730 | 
            -
            class GuestUser
         | 
| 623 | 
            +
            <pre>class GuestUser
         | 
| 731 624 | 
             
              include BaseUser
         | 
| 732 625 |  | 
| 733 626 | 
             
              def account
         | 
    
        data/VERSION
    CHANGED
    
    | @@ -1 +1 @@ | |
| 1 | 
            -
            0.5. | 
| 1 | 
            +
            0.5.2
         | 
    
        data/lib/trole.rb
    CHANGED
    
    
| @@ -28,7 +28,11 @@ module TroleGroups | |
| 28 28 | 
             
                  # sets the value of the role field (@trole or @troles) and persists the value (in the data store)
         | 
| 29 29 | 
             
                  # @param [Object] the value to set on the role field of the role subject
         | 
| 30 30 | 
             
                  def set_ds_field value
         | 
| 31 | 
            -
                    return if ds_field_value == value
         | 
| 31 | 
            +
                    return if ds_field_value == value 
         | 
| 32 | 
            +
                    if Troles::Common::Config.log_on?
         | 
| 33 | 
            +
                      puts "TroleGroups::Storage::BaseMany.set_ds_field:"
         | 
| 34 | 
            +
                      puts "#{rolegroup_subject}.#{ds_field_name} = #{value}"
         | 
| 35 | 
            +
                    end
         | 
| 32 36 | 
             
                    rolegroup_subject.send(:"#{ds_field_name}=", value)
         | 
| 33 37 | 
             
                    persist_role_changes!
         | 
| 34 38 | 
             
                  end
         | 
| @@ -42,15 +46,22 @@ module TroleGroups | |
| 42 46 | 
             
                  # the current value of the role field
         | 
| 43 47 | 
             
                  # @return [Object] the value
         | 
| 44 48 | 
             
                  def ds_field_value
         | 
| 49 | 
            +
                    # puts "#{rolegroup_subject}.#{ds_field_name}" if Troles::Common::Config.log_on?
         | 
| 45 50 | 
             
                    rolegroup_subject.send(ds_field_name)
         | 
| 46 51 | 
             
                  end      
         | 
| 47 52 |  | 
| 48 53 | 
             
                  # Attempts to persist the role field changes
         | 
| 49 54 | 
             
                  # @return [true, false, error] true if saved, false if no save! method, Error on some error
         | 
| 50 | 
            -
                  def persist_role_changes! | 
| 51 | 
            -
                     | 
| 52 | 
            -
                    rolegroup_subject.save | 
| 53 | 
            -
             | 
| 55 | 
            +
                  def persist_role_changes!
         | 
| 56 | 
            +
                    puts "TroleGroups::Storage::BaseMany.persist_role_changes!" if Troles::Common::Config.log_on?
         | 
| 57 | 
            +
                    if !rolegroup_subject.respond_to? :save
         | 
| 58 | 
            +
                      puts "could not save since no #save method on subject: #{rolegroup_subject}" if Troles::Common::Config.log_on?
         | 
| 59 | 
            +
                      return false 
         | 
| 60 | 
            +
                    else      
         | 
| 61 | 
            +
                      puts "#{rolegroup_subject}.save" if Troles::Common::Config.log_on?         
         | 
| 62 | 
            +
                      rolegroup_subject.save
         | 
| 63 | 
            +
                      rolegroup_subject.publish_change :role_groups
         | 
| 64 | 
            +
                    end
         | 
| 54 65 | 
             
                  end 
         | 
| 55 66 |  | 
| 56 67 | 
             
                  protected
         | 
| @@ -7,8 +7,10 @@ module Troles::ActiveRecord | |
| 7 7 |  | 
| 8 8 | 
             
                def configure_relation
         | 
| 9 9 | 
             
                  case strategy
         | 
| 10 | 
            +
                  when :join_ref_many
         | 
| 11 | 
            +
                    configure_join_model
         | 
| 10 12 | 
             
                  when :ref_many
         | 
| 11 | 
            -
                    return configure_join_model if  | 
| 13 | 
            +
                    return configure_join_model if join_model
         | 
| 12 14 | 
             
                    has_and_belongs_many subject_class, object_model, :key => :accounts         
         | 
| 13 15 | 
             
                  when :embed_many
         | 
| 14 16 | 
             
                    raise "Embed many configuration not yet implemented for ActiveRecord" 
         | 
| @@ -25,6 +27,11 @@ module Troles::ActiveRecord | |
| 25 27 | 
             
                  role_model
         | 
| 26 28 | 
             
                end
         | 
| 27 29 |  | 
| 30 | 
            +
                def join_model
         | 
| 31 | 
            +
                  role_join_model
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
             | 
| 28 35 | 
             
                def role_join_model
         | 
| 29 36 | 
             
                  @join_model_found ||= begin
         | 
| 30 37 | 
             
                    models = [@join_model, join_model_best_guess].select do |class_name|
         | 
| @@ -50,10 +57,17 @@ module Troles::ActiveRecord | |
| 50 57 | 
             
                  make_key role_join_model
         | 
| 51 58 | 
             
                end
         | 
| 52 59 |  | 
| 53 | 
            -
                def configure_join_model
         | 
| 60 | 
            +
                def configure_join_model           
         | 
| 61 | 
            +
                  if Troles::Common::Config.log_on?
         | 
| 62 | 
            +
                    puts "configuring join model..." 
         | 
| 63 | 
            +
                    puts "Subject class: #{subject_class}"
         | 
| 64 | 
            +
                    puts "Role class: #{object_model}"
         | 
| 65 | 
            +
                    puts "Join class: #{join_model}"
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
             | 
| 54 68 | 
             
                  # UserAccount
         | 
| 55 69 | 
             
                  # has_many :troles, :class_name => 'Role', :through => :users_roles
         | 
| 56 | 
            -
                  has_many_for subject_class,  | 
| 70 | 
            +
                  has_many_for subject_class, object_model, :opts => {:through => join_key.to_sym}
         | 
| 57 71 | 
             
                  # has_many :user_roles, :class_name => 'UserRole'
         | 
| 58 72 | 
             
                  has_many_for subject_class, role_join_model, :key => join_key
         | 
| 59 73 |  | 
| @@ -65,10 +79,10 @@ module Troles::ActiveRecord | |
| 65 79 |  | 
| 66 80 | 
             
                  # Role
         | 
| 67 81 | 
             
                  # has_many :accounts, :class_name => 'User', :through => :user_roles      
         | 
| 68 | 
            -
                  has_many_for  | 
| 82 | 
            +
                  has_many_for object_model, subject_class, :key => :accounts, :opts => {:through => join_key.to_sym}
         | 
| 69 83 |  | 
| 70 84 | 
             
                  # has_many :user_roles, :class_name => 'UserRole'
         | 
| 71 | 
            -
                  has_many_for  | 
| 85 | 
            +
                  has_many_for object_model, role_join_model, :key => join_key      
         | 
| 72 86 | 
             
                end
         | 
| 73 87 | 
             
              end
         | 
| 74 88 | 
             
            end
         | 
    
        data/lib/troles/api/config.rb
    CHANGED
    
    
    
        data/lib/troles/api/core.rb
    CHANGED
    
    
| @@ -5,18 +5,18 @@ | |
| 5 5 | 
             
            #
         | 
| 6 6 | 
             
            module Troles::Common::Api
         | 
| 7 7 | 
             
              module Core
         | 
| 8 | 
            -
             | 
| 8 | 
            +
             | 
| 9 9 | 
             
                # Access to the Troles operations API
         | 
| 10 10 | 
             
                # @return [Troles::Operations] the operations API object 
         | 
| 11 11 | 
             
                def roles
         | 
| 12 12 | 
             
                  @roles ||= Troles::Operations.new(self)
         | 
| 13 | 
            -
                end | 
| 13 | 
            +
                end
         | 
| 14 14 |  | 
| 15 15 | 
             
                # Sets the roles of the subject
         | 
| 16 16 | 
             
                # (see #set_roles)
         | 
| 17 17 | 
             
                def roles= *new_roles
         | 
| 18 18 | 
             
                  roles.set_roles new_roles
         | 
| 19 | 
            -
                end | 
| 19 | 
            +
                end
         | 
| 20 20 |  | 
| 21 21 | 
             
                # If this role subject instance should have static (immutable) roles
         | 
| 22 22 | 
             
                # @return [true, false] defaults to false so a role subject is allowed to change roles 
         | 
| @@ -25,28 +25,27 @@ module Troles::Common::Api | |
| 25 25 | 
             
                end
         | 
| 26 26 |  | 
| 27 27 | 
             
                def troles_config
         | 
| 28 | 
            -
                  self.class.troles_config | 
| 28 | 
            +
                  self.class.troles_config
         | 
| 29 29 | 
             
                end
         | 
| 30 | 
            -
             | 
| 31 | 
            -
                module ClassMethods | 
| 32 | 
            -
             | 
| 30 | 
            +
             | 
| 31 | 
            +
                module ClassMethods
         | 
| 32 | 
            +
             | 
| 33 33 | 
             
                  def valid_roles
         | 
| 34 34 | 
             
                    troles_config.valid_roles
         | 
| 35 35 | 
             
                  end
         | 
| 36 36 |  | 
| 37 | 
            -
                   | 
| 38 | 
            -
             | 
| 39 | 
            -
                   | 
| 40 | 
            -
                  # end
         | 
| 37 | 
            +
                  def valid_roles= *roles
         | 
| 38 | 
            +
                    troles_config.valid_roles = roles.flatten.map{|r| r.to_s.alpha_numeric}.map(&:to_sym).uniq
         | 
| 39 | 
            +
                  end
         | 
| 41 40 |  | 
| 42 41 | 
             
                  # If all role subjects using this strategy should have static (immutable) roles
         | 
| 43 42 | 
             
                  #
         | 
| 44 | 
            -
                  # @note Should also proxy Config object? | 
| 43 | 
            +
                  # @note Should also proxy Config object?
         | 
| 45 44 | 
             
                  #
         | 
| 46 45 | 
             
                  # @return [true, false] if role subjects have static roles or not (default: false)
         | 
| 47 46 | 
             
                  def static_roles?
         | 
| 48 47 | 
             
                    troles_config.static_roles?
         | 
| 49 | 
            -
                  end | 
| 48 | 
            +
                  end
         | 
| 50 49 | 
             
                end
         | 
| 51 50 | 
             
              end
         | 
| 52 | 
            -
            end
         | 
| 51 | 
            +
            end
         | 
| @@ -39,6 +39,12 @@ module Troles::Common::Api | |
| 39 39 | 
             
                # (see #has_roles?)
         | 
| 40 40 | 
             
                def has_any_role? *roles
         | 
| 41 41 | 
             
                  !(role_list & roles.to_symbols).empty?
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                # Checks if the role subject has any of the listed roles
         | 
| 45 | 
            +
                # (see #has_roles?)
         | 
| 46 | 
            +
                def has_all_roles? *roles
         | 
| 47 | 
            +
                  (roles.to_symbols - role_list).empty?
         | 
| 42 48 | 
             
                end    
         | 
| 43 49 | 
             
              end
         | 
| 44 50 | 
             
            end
         | 
    
        data/lib/troles/common/api.rb
    CHANGED
    
    | @@ -15,14 +15,15 @@ module Troles::Common | |
| 15 15 | 
             
                  end
         | 
| 16 16 |  | 
| 17 17 | 
             
                  def included(base)
         | 
| 18 | 
            +
                    puts "Included #{base}"
         | 
| 18 19 | 
             
                    apis.each do |api|
         | 
| 19 20 | 
             
                      begin
         | 
| 20 21 | 
             
                        base.include_and_extend :"#{api.to_s.camelize}"
         | 
| 21 22 | 
             
                      rescue
         | 
| 22 23 | 
             
                      end
         | 
| 23 | 
            -
                    end | 
| 24 | 
            +
                    end
         | 
| 24 25 | 
             
                  end
         | 
| 25 26 | 
             
                end
         | 
| 26 27 | 
             
                extend ClassMethods
         | 
| 27 28 | 
             
              end
         | 
| 28 | 
            -
            end
         | 
| 29 | 
            +
            end
         |