thwart 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
 - data/.gitignore +24 -0
 - data/LICENSE +20 -0
 - data/README.rdoc +17 -0
 - data/Rakefile +30 -0
 - data/VERSION +1 -0
 - data/autotest/discover.rb +1 -0
 - data/examples/a_complete_example.rb +56 -0
 - data/examples/example_helper.rb +45 -0
 - data/lib/thwart/action_group_builder.rb +62 -0
 - data/lib/thwart/actions_store.rb +89 -0
 - data/lib/thwart/actor.rb +36 -0
 - data/lib/thwart/canable.rb +30 -0
 - data/lib/thwart/dsl.rb +43 -0
 - data/lib/thwart/enforcer.rb +15 -0
 - data/lib/thwart/resource.rb +27 -0
 - data/lib/thwart/role.rb +81 -0
 - data/lib/thwart/role_builder.rb +143 -0
 - data/lib/thwart/role_registry.rb +75 -0
 - data/lib/thwart.rb +67 -0
 - data/spec/action_group_builder_spec.rb +68 -0
 - data/spec/actions_store_spec.rb +74 -0
 - data/spec/actor_spec.rb +45 -0
 - data/spec/canable_spec.rb +41 -0
 - data/spec/dsl_spec.rb +103 -0
 - data/spec/enforcer_spec.rb +17 -0
 - data/spec/resource_spec.rb +42 -0
 - data/spec/role_builder_spec.rb +197 -0
 - data/spec/role_registry_spec.rb +137 -0
 - data/spec/role_spec.rb +146 -0
 - data/spec/spec_helper.rb +49 -0
 - data/spec/thwart_spec.rb +60 -0
 - metadata +139 -0
 
| 
         @@ -0,0 +1,143 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'active_support/core_ext/hash/deep_merge'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Thwart
         
     | 
| 
      
 4 
     | 
    
         
            +
              class ActionOrGroupNotFoundError < StandardError; end
         
     | 
| 
      
 5 
     | 
    
         
            +
              class OustideRoleDefinitionError < StandardError; end
         
     | 
| 
      
 6 
     | 
    
         
            +
              
         
     | 
| 
      
 7 
     | 
    
         
            +
              class RoleBuilder
         
     | 
| 
      
 8 
     | 
    
         
            +
                include ActiveSupport::Callbacks
         
     | 
| 
      
 9 
     | 
    
         
            +
                define_callbacks :build_role, :scope => [:kind, :name]
         
     | 
| 
      
 10 
     | 
    
         
            +
                
         
     | 
| 
      
 11 
     | 
    
         
            +
                attr_accessor :last_built_role
         
     | 
| 
      
 12 
     | 
    
         
            +
                attr_reader :actionables_store
         
     | 
| 
      
 13 
     | 
    
         
            +
                delegate :actionables, :to => :actionables_store
         
     | 
| 
      
 14 
     | 
    
         
            +
                
         
     | 
| 
      
 15 
     | 
    
         
            +
                def self.empty_role
         
     | 
| 
      
 16 
     | 
    
         
            +
                  Class.new do
         
     | 
| 
      
 17 
     | 
    
         
            +
                    include Thwart::Role
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
                
         
     | 
| 
      
 21 
     | 
    
         
            +
                def initialize(a_store)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @actionables_store = a_store
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
                
         
     | 
| 
      
 25 
     | 
    
         
            +
                def create_role(name, options = {}, &block)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @role = self.class.empty_role.new     # Start empty role
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @current_response = true              # Assume the first permission definitions are allows
         
     | 
| 
      
 28 
     | 
    
         
            +
                  run_callbacks :build_role do
         
     | 
| 
      
 29 
     | 
    
         
            +
                    # Add parents
         
     | 
| 
      
 30 
     | 
    
         
            +
                    @role.name = name
         
     | 
| 
      
 31 
     | 
    
         
            +
                    @role.parents = options[:parents] if options[:parents]
         
     | 
| 
      
 32 
     | 
    
         
            +
                  
         
     | 
| 
      
 33 
     | 
    
         
            +
                    # Run DSL block
         
     | 
| 
      
 34 
     | 
    
         
            +
                    if block_given?
         
     | 
| 
      
 35 
     | 
    
         
            +
                      @dsl ||= Thwart::Dsl.new
         
     | 
| 
      
 36 
     | 
    
         
            +
                      @dsl.all = true
         
     | 
| 
      
 37 
     | 
    
         
            +
                      @dsl.evaluate(self, &block)
         
     | 
| 
      
 38 
     | 
    
         
            +
                    end
         
     | 
| 
      
 39 
     | 
    
         
            +
                    self.last_built_role = @role
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
                  # Unset the @role instance variable to disable DSL methods and return the role
         
     | 
| 
      
 42 
     | 
    
         
            +
                  r = @role
         
     | 
| 
      
 43 
     | 
    
         
            +
                  @role = nil
         
     | 
| 
      
 44 
     | 
    
         
            +
                  return r
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
                
         
     | 
| 
      
 47 
     | 
    
         
            +
                def define_permission(name, *resources, &block)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  ensure_in_dsl! 
         
     | 
| 
      
 49 
     | 
    
         
            +
                  # Shift off first argument sent from method missing and convert any action groups to an array of actions
         
     | 
| 
      
 50 
     | 
    
         
            +
                  raise ArgumentError, "Unrecognized action or action group #{name}" if !self.actionables.has_key?(name)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  names = self.actionables[name]
         
     | 
| 
      
 52 
     | 
    
         
            +
                  
         
     | 
| 
      
 53 
     | 
    
         
            +
                  # Pop of last hash argument from method missing and merge in default options and :if block
         
     | 
| 
      
 54 
     | 
    
         
            +
                  options = {:if => false, :unless => false}
         
     | 
| 
      
 55 
     | 
    
         
            +
                  options.merge!(resources.pop) if resources.last.respond_to?(:keys)
         
     | 
| 
      
 56 
     | 
    
         
            +
                  options[:if] = block if block_given?
         
     | 
| 
      
 57 
     | 
    
         
            +
                  # Allow :all or blank resource specifiers
         
     | 
| 
      
 58 
     | 
    
         
            +
                  if resources.nil? || resources.empty? || resources.any? {|r| r == :all}
         
     | 
| 
      
 59 
     | 
    
         
            +
                    resources = [:_other]
         
     | 
| 
      
 60 
     | 
    
         
            +
                  end
         
     | 
| 
      
 61 
     | 
    
         
            +
                  
         
     | 
| 
      
 62 
     | 
    
         
            +
                  # Generate response based on @current_response and optional procs
         
     | 
| 
      
 63 
     | 
    
         
            +
                  generated_response = generate_response(options)
         
     | 
| 
      
 64 
     | 
    
         
            +
                  response_hash = hash_with_value(resources, generated_response)
         
     | 
| 
      
 65 
     | 
    
         
            +
                  
         
     | 
| 
      
 66 
     | 
    
         
            +
                  # Merge into existing role definition
         
     | 
| 
      
 67 
     | 
    
         
            +
                  @role.responses.deep_merge!(hash_with_value(names) do |k|
         
     | 
| 
      
 68 
     | 
    
         
            +
                    response_hash
         
     | 
| 
      
 69 
     | 
    
         
            +
                  end)
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
                
         
     | 
| 
      
 72 
     | 
    
         
            +
                def default(bool)
         
     | 
| 
      
 73 
     | 
    
         
            +
                  ensure_in_dsl!
         
     | 
| 
      
 74 
     | 
    
         
            +
                  @role.default_response = bool
         
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
                
         
     | 
| 
      
 77 
     | 
    
         
            +
                def include(*args)
         
     | 
| 
      
 78 
     | 
    
         
            +
                  ensure_in_dsl!
         
     | 
| 
      
 79 
     | 
    
         
            +
                  @role.parents += args
         
     | 
| 
      
 80 
     | 
    
         
            +
                end
         
     | 
| 
      
 81 
     | 
    
         
            +
                
         
     | 
| 
      
 82 
     | 
    
         
            +
                def allow(&block)
         
     | 
| 
      
 83 
     | 
    
         
            +
                  evaluate_with_response(true, &block)
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
                
         
     | 
| 
      
 86 
     | 
    
         
            +
                def deny(&block)
         
     | 
| 
      
 87 
     | 
    
         
            +
                  evaluate_with_response(false, &block)
         
     | 
| 
      
 88 
     | 
    
         
            +
                end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                def evaluate_with_response(response, &block)
         
     | 
| 
      
 91 
     | 
    
         
            +
                  ensure_in_dsl!
         
     | 
| 
      
 92 
     | 
    
         
            +
                  old = @current_response
         
     | 
| 
      
 93 
     | 
    
         
            +
                  @current_response = response
         
     | 
| 
      
 94 
     | 
    
         
            +
                  @dsl.evaluate(self, &block)
         
     | 
| 
      
 95 
     | 
    
         
            +
                  @current_response = old
         
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                def respond_to?(name)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  return true if self.actionables.has_key?(name)
         
     | 
| 
      
 100 
     | 
    
         
            +
                  super
         
     | 
| 
      
 101 
     | 
    
         
            +
                end
         
     | 
| 
      
 102 
     | 
    
         
            +
                
         
     | 
| 
      
 103 
     | 
    
         
            +
                def method_missing(name, *args, &block)
         
     | 
| 
      
 104 
     | 
    
         
            +
                  return define_permission(name, *args, &block) if self.respond_to?(name)
         
     | 
| 
      
 105 
     | 
    
         
            +
                  super
         
     | 
| 
      
 106 
     | 
    
         
            +
                end
         
     | 
| 
      
 107 
     | 
    
         
            +
                
         
     | 
| 
      
 108 
     | 
    
         
            +
                private
         
     | 
| 
      
 109 
     | 
    
         
            +
                def generate_response(options) 
         
     | 
| 
      
 110 
     | 
    
         
            +
                  # If a proc has been supplied
         
     | 
| 
      
 111 
     | 
    
         
            +
                  if(options[:if].respond_to?(:call) || options[:unless].respond_to?(:call)) 
         
     | 
| 
      
 112 
     | 
    
         
            +
                    # Copy variable for scope
         
     | 
| 
      
 113 
     | 
    
         
            +
                    check = @current_response
         
     | 
| 
      
 114 
     | 
    
         
            +
                    if options[:unless].respond_to?(:call)
         
     | 
| 
      
 115 
     | 
    
         
            +
                      check = !check 
         
     | 
| 
      
 116 
     | 
    
         
            +
                      options[:if] = options[:unless]
         
     | 
| 
      
 117 
     | 
    
         
            +
                    end
         
     | 
| 
      
 118 
     | 
    
         
            +
                    
         
     | 
| 
      
 119 
     | 
    
         
            +
                    return Proc.new do |*args|
         
     | 
| 
      
 120 
     | 
    
         
            +
                      check == options[:if].call(*args)
         
     | 
| 
      
 121 
     | 
    
         
            +
                    end
         
     | 
| 
      
 122 
     | 
    
         
            +
                  else
         
     | 
| 
      
 123 
     | 
    
         
            +
                    @current_response
         
     | 
| 
      
 124 
     | 
    
         
            +
                  end
         
     | 
| 
      
 125 
     | 
    
         
            +
                end
         
     | 
| 
      
 126 
     | 
    
         
            +
                
         
     | 
| 
      
 127 
     | 
    
         
            +
                def hash_with_value(array, val = nil, &block) 
         
     | 
| 
      
 128 
     | 
    
         
            +
                  array.inject({}) do |acc, k|
         
     | 
| 
      
 129 
     | 
    
         
            +
                    acc[k] = if block_given?
         
     | 
| 
      
 130 
     | 
    
         
            +
                      yield k
         
     | 
| 
      
 131 
     | 
    
         
            +
                    else
         
     | 
| 
      
 132 
     | 
    
         
            +
                      val
         
     | 
| 
      
 133 
     | 
    
         
            +
                    end
         
     | 
| 
      
 134 
     | 
    
         
            +
                    acc
         
     | 
| 
      
 135 
     | 
    
         
            +
                  end
         
     | 
| 
      
 136 
     | 
    
         
            +
                end
         
     | 
| 
      
 137 
     | 
    
         
            +
                
         
     | 
| 
      
 138 
     | 
    
         
            +
                def ensure_in_dsl!
         
     | 
| 
      
 139 
     | 
    
         
            +
                  raise OustideRoleDefinitionError, "You can only define role permissions inside a role defintion block!" if @role.nil?
         
     | 
| 
      
 140 
     | 
    
         
            +
                end
         
     | 
| 
      
 141 
     | 
    
         
            +
              end
         
     | 
| 
      
 142 
     | 
    
         
            +
              
         
     | 
| 
      
 143 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,75 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Thwart
         
     | 
| 
      
 2 
     | 
    
         
            +
              class DuplicateRoleError < StandardError; end
         
     | 
| 
      
 3 
     | 
    
         
            +
              class MissingRoleError < StandardError; end
         
     | 
| 
      
 4 
     | 
    
         
            +
              
         
     | 
| 
      
 5 
     | 
    
         
            +
              class RoleRegistry
         
     | 
| 
      
 6 
     | 
    
         
            +
                def roles
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @roles ||= []
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @roles
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
                
         
     | 
| 
      
 11 
     | 
    
         
            +
                def add(role)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  raise DuplicateRoleError, "Role #{role} already exists in the role registry!" if self.has_role?(role)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @roles << role
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
                
         
     | 
| 
      
 16 
     | 
    
         
            +
                def query(actor, resource, action)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  role = self.find_actor_role(actor)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  resource = self.find_resource_identifier(resource)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  if role.nil? || !self.has_role?(role) 
         
     | 
| 
      
 20 
     | 
    
         
            +
                    raise MissingRoleError, "Role #{role} could not be found in the registry!" if Thwart.actor_must_play_role
         
     | 
| 
      
 21 
     | 
    
         
            +
                  else
         
     | 
| 
      
 22 
     | 
    
         
            +
                    q = [role]
         
     | 
| 
      
 23 
     | 
    
         
            +
                    while r = q.shift
         
     | 
| 
      
 24 
     | 
    
         
            +
                      resp = r.query(actor, resource, action)
         
     | 
| 
      
 25 
     | 
    
         
            +
                      if resp != nil 
         
     | 
| 
      
 26 
     | 
    
         
            +
                        return resp # positive/negative response from the role, a rule governs the role on this query
         
     | 
| 
      
 27 
     | 
    
         
            +
                      else
         
     | 
| 
      
 28 
     | 
    
         
            +
                        q = q | r.parents # add this roles parents to the query queue
         
     | 
| 
      
 29 
     | 
    
         
            +
                      end
         
     | 
| 
      
 30 
     | 
    
         
            +
                    end
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
                
         
     | 
| 
      
 33 
     | 
    
         
            +
                  Thwart.default_query_response # return was not called above, return the default
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
              
         
     | 
| 
      
 36 
     | 
    
         
            +
                def has_role?(role)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  self.roles.include?(role)
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
              
         
     | 
| 
      
 40 
     | 
    
         
            +
                def find_actor_role(actor)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  r = actor.thwart_role if actor.respond_to?(:thwart_role)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  r ||= r.to_sym if r.respond_to?(:to_sym)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  if r.is_a?(Symbol)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    r = self.roles.find {|a| a.name == r}
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
                  r
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
              
         
     | 
| 
      
 49 
     | 
    
         
            +
                def find_resource_identifier(resource)
         
     | 
| 
      
 50 
     | 
    
         
            +
                  r ||= resource.thwart_name if resource.respond_to?(:thwart_name)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  if resource.class != Class
         
     | 
| 
      
 52 
     | 
    
         
            +
                    r ||= resource.class.thwart_name if resource.class.respond_to?(:thwart_name)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    r ||= resource.class.name.downcase.to_sym if Thwart.all_classes_are_resources
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
                  r
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
              
         
     | 
| 
      
 58 
     | 
    
         
            +
                def initialize(role_creator = nil)
         
     | 
| 
      
 59 
     | 
    
         
            +
                  self.monitor_builder(role_creator) if !role_creator.nil?
         
     | 
| 
      
 60 
     | 
    
         
            +
                  self
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
              
         
     | 
| 
      
 63 
     | 
    
         
            +
                def monitor_builder(role_creator)
         
     | 
| 
      
 64 
     | 
    
         
            +
                  registry = self
         
     | 
| 
      
 65 
     | 
    
         
            +
                  unless role_creator.nil?
         
     | 
| 
      
 66 
     | 
    
         
            +
                    role_creator.class.set_callback :build_role, :after do |object|
         
     | 
| 
      
 67 
     | 
    
         
            +
                      registry.add(object.last_built_role)
         
     | 
| 
      
 68 
     | 
    
         
            +
                    end
         
     | 
| 
      
 69 
     | 
    
         
            +
                  else
         
     | 
| 
      
 70 
     | 
    
         
            +
                    @role_creator.class.reset_callbacks(:build_role)
         
     | 
| 
      
 71 
     | 
    
         
            +
                  end
         
     | 
| 
      
 72 
     | 
    
         
            +
                  @role_creator = role_creator
         
     | 
| 
      
 73 
     | 
    
         
            +
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
              end
         
     | 
| 
      
 75 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/thwart.rb
    ADDED
    
    | 
         @@ -0,0 +1,67 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'active_support'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'active_support/callbacks'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'active_support/core_ext/module/attribute_accessors'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "active_support/core_ext/module/delegation"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "active_support/core_ext/array/wrap"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            require 'thwart/canable'
         
     | 
| 
      
 9 
     | 
    
         
            +
            require 'thwart/actions_store'
         
     | 
| 
      
 10 
     | 
    
         
            +
            require 'thwart/action_group_builder'
         
     | 
| 
      
 11 
     | 
    
         
            +
            require 'thwart/role_registry'
         
     | 
| 
      
 12 
     | 
    
         
            +
            require 'thwart/role_builder'
         
     | 
| 
      
 13 
     | 
    
         
            +
            require 'thwart/role'
         
     | 
| 
      
 14 
     | 
    
         
            +
            require 'thwart/resource'
         
     | 
| 
      
 15 
     | 
    
         
            +
            require 'thwart/actor'
         
     | 
| 
      
 16 
     | 
    
         
            +
            require 'thwart/enforcer'
         
     | 
| 
      
 17 
     | 
    
         
            +
            require 'thwart/dsl'
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            module Thwart
         
     | 
| 
      
 20 
     | 
    
         
            +
              # autoload :Cans, 'thwart/canable'
         
     | 
| 
      
 21 
     | 
    
         
            +
              # autoload :Ables, 'thwart/canable'
         
     | 
| 
      
 22 
     | 
    
         
            +
              # autoload :ActionsStore, 'thwart/actions_store'
         
     | 
| 
      
 23 
     | 
    
         
            +
              # autoload :ActionGroupBuilder, 'thwart/action_group_builder'
         
     | 
| 
      
 24 
     | 
    
         
            +
              # autoload :RoleRegistry, 'thwart/role_registry'
         
     | 
| 
      
 25 
     | 
    
         
            +
              # autoload :RoleBuilder, 'thwart/role_builder'
         
     | 
| 
      
 26 
     | 
    
         
            +
              # autoload :Role, 'thwart/role'
         
     | 
| 
      
 27 
     | 
    
         
            +
              # autoload :DefaultRole, 'thwart/role'
         
     | 
| 
      
 28 
     | 
    
         
            +
              # autoload :Resource, 'thwart/resource'
         
     | 
| 
      
 29 
     | 
    
         
            +
              # autoload :Actor, 'thwart/actor'
         
     | 
| 
      
 30 
     | 
    
         
            +
              # autoload :Dsl, 'thwart/dsl'
         
     | 
| 
      
 31 
     | 
    
         
            +
              
         
     | 
| 
      
 32 
     | 
    
         
            +
              # The default can => able methods for CRUD
         
     | 
| 
      
 33 
     | 
    
         
            +
              CrudActions = {:create => :creatable, :view => :viewable, :update => :updatable, :destroy => :destroyable}
         
     | 
| 
      
 34 
     | 
    
         
            +
              
         
     | 
| 
      
 35 
     | 
    
         
            +
              Actions       = ActionsStore.new
         
     | 
| 
      
 36 
     | 
    
         
            +
              Roles         = RoleRegistry.new
         
     | 
| 
      
 37 
     | 
    
         
            +
              
         
     | 
| 
      
 38 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 39 
     | 
    
         
            +
                attr_reader :actionables_dsl, :role_dsl
         
     | 
| 
      
 40 
     | 
    
         
            +
                attr_accessor :default_query_response, :role_registry, :actor_must_play_role, :all_classes_are_resources
         
     | 
| 
      
 41 
     | 
    
         
            +
                delegate :create_action, :to => "Thwart::Actions"
         
     | 
| 
      
 42 
     | 
    
         
            +
                delegate :create_action_group, :to => :actionables_dsl
         
     | 
| 
      
 43 
     | 
    
         
            +
                delegate :create_role, :to => :role_dsl
         
     | 
| 
      
 44 
     | 
    
         
            +
                delegate :query, :to => "Thwart::Roles"
         
     | 
| 
      
 45 
     | 
    
         
            +
                
         
     | 
| 
      
 46 
     | 
    
         
            +
                def configure(&block)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  # Create builder DSLs for this configuration block
         
     | 
| 
      
 48 
     | 
    
         
            +
                  @actionables_dsl = ActionGroupBuilder.new(Actions)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  @role_dsl = RoleBuilder.new(@actionables_dsl)
         
     | 
| 
      
 50 
     | 
    
         
            +
                  Roles.monitor_builder(@role_dsl)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  
         
     | 
| 
      
 52 
     | 
    
         
            +
                  # Configure
         
     | 
| 
      
 53 
     | 
    
         
            +
                  dsl = Thwart::Dsl.new(:role => :create_role, :action => :create_action, :action_group => :create_action_group)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  dsl.all = false
         
     | 
| 
      
 55 
     | 
    
         
            +
                  dsl.evaluate(self, &block)
         
     | 
| 
      
 56 
     | 
    
         
            +
                  
         
     | 
| 
      
 57 
     | 
    
         
            +
                  # Unset and stop monitoring builder DSLs so they can be GC'd
         
     | 
| 
      
 58 
     | 
    
         
            +
                  @actionables_dsl = nil
         
     | 
| 
      
 59 
     | 
    
         
            +
                  @role_dsl = nil
         
     | 
| 
      
 60 
     | 
    
         
            +
                  Roles.monitor_builder(nil)
         
     | 
| 
      
 61 
     | 
    
         
            +
                  self
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
              end
         
     | 
| 
      
 64 
     | 
    
         
            +
              
         
     | 
| 
      
 65 
     | 
    
         
            +
              class MissingAttributeError < StandardError; end
         
     | 
| 
      
 66 
     | 
    
         
            +
              class NoPermissionError < StandardError; end
         
     | 
| 
      
 67 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,68 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe Thwart::ActionGroupBuilder do
         
     | 
| 
      
 4 
     | 
    
         
            +
              before do
         
     | 
| 
      
 5 
     | 
    
         
            +
                @store = double("Actions Store")
         
     | 
| 
      
 6 
     | 
    
         
            +
                @store.class.stub(:set_callback)
         
     | 
| 
      
 7 
     | 
    
         
            +
                @builder = Thwart::ActionGroupBuilder.new(@store)
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              it "should add some simple actionables" do
         
     | 
| 
      
 11 
     | 
    
         
            +
                @builder.add_actionable(:sing)
         
     | 
| 
      
 12 
     | 
    
         
            +
                @builder.add_actionable(:dance, [:laugh, :play])
         
     | 
| 
      
 13 
     | 
    
         
            +
                @builder.actionables.should == {:sing => [:sing], :dance => [:laugh, :play]}      
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
              it "should have the crud action group if added" do
         
     | 
| 
      
 16 
     | 
    
         
            +
                @store.should_receive(:add_crud!)
         
     | 
| 
      
 17 
     | 
    
         
            +
                Thwart::CrudActions.keys.each do |k|
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @builder.add_actionable(k)
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
                @builder.add_crud_group!
         
     | 
| 
      
 21 
     | 
    
         
            +
                @builder.actionables.include?(:crud).should == true
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
              it "should set an action group" do
         
     | 
| 
      
 24 
     | 
    
         
            +
                @builder.add_actionable(:one)
         
     | 
| 
      
 25 
     | 
    
         
            +
                @builder.add_actionable(:two)
         
     | 
| 
      
 26 
     | 
    
         
            +
                @builder.create_action_group(:stuff, [:one, :two])
         
     | 
| 
      
 27 
     | 
    
         
            +
                @builder.actionables[:stuff].should == [:one, :two]
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
              
         
     | 
| 
      
 30 
     | 
    
         
            +
              it "should initialize a callback on the action creator" do
         
     | 
| 
      
 31 
     | 
    
         
            +
                store_class = Class.new do
         
     | 
| 
      
 32 
     | 
    
         
            +
                  include ActiveSupport::Callbacks
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
                store_class.should_receive(:set_callback)
         
     | 
| 
      
 35 
     | 
    
         
            +
                Thwart::ActionGroupBuilder.new(store_class.new)
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
              
         
     | 
| 
      
 38 
     | 
    
         
            +
              describe "action group resolution" do
         
     | 
| 
      
 39 
     | 
    
         
            +
                it "should find existing actions" do 
         
     | 
| 
      
 40 
     | 
    
         
            +
                  @builder.stub(:actionables).and_return({:view => [:view]})
         
     | 
| 
      
 41 
     | 
    
         
            +
                  @builder.resolve_action_group(:view).should == [:view]
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                context "with crud and one group" do
         
     | 
| 
      
 45 
     | 
    
         
            +
                  before do
         
     | 
| 
      
 46 
     | 
    
         
            +
                    @actions = [:create, :update, :destroy]
         
     | 
| 
      
 47 
     | 
    
         
            +
                    @actionables = @actions.inject({}) {|acc, k| acc[k] = [k]; acc}.merge({:manage => @actions})
         
     | 
| 
      
 48 
     | 
    
         
            +
                    @builder.stub(:actionables).and_return(@actionables)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
                  it "should find arrays of existing actions" do        
         
     | 
| 
      
 51 
     | 
    
         
            +
                    @builder.resolve_action_group(@actions).should == @actions
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
                  it "should build action groups of other action groups" do
         
     | 
| 
      
 54 
     | 
    
         
            +
                    @builder.resolve_action_group(:manage).should == @actions
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end    
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  context "and extra actions" do
         
     | 
| 
      
 58 
     | 
    
         
            +
                    before do
         
     | 
| 
      
 59 
     | 
    
         
            +
                      @actionables = @actionables.merge([:one, :two, :three].inject({}) {|acc, k| acc[k] = [k]; acc}).merge({:another => [:one, :two]})
         
     | 
| 
      
 60 
     | 
    
         
            +
                      @builder.stub(:actionables).and_return(@actionables)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    end
         
     | 
| 
      
 62 
     | 
    
         
            +
                    it "should build action groups of other action groups and other actions" do
         
     | 
| 
      
 63 
     | 
    
         
            +
                      @builder.resolve_action_group([:manage, :another, :three]).should include(:one, :two, :three, :create, :update, :destroy)
         
     | 
| 
      
 64 
     | 
    
         
            +
                    end
         
     | 
| 
      
 65 
     | 
    
         
            +
                  end
         
     | 
| 
      
 66 
     | 
    
         
            +
                end    
         
     | 
| 
      
 67 
     | 
    
         
            +
              end
         
     | 
| 
      
 68 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,74 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe Thwart::ActionsStore do
         
     | 
| 
      
 4 
     | 
    
         
            +
              before do
         
     | 
| 
      
 5 
     | 
    
         
            +
                @actions = Thwart::ActionsStore.new
         
     | 
| 
      
 6 
     | 
    
         
            +
              end
         
     | 
| 
      
 7 
     | 
    
         
            +
              describe "with no actions set up" do
         
     | 
| 
      
 8 
     | 
    
         
            +
                before do
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @actions.should_receive(:actions).and_return({})
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
                it "shouldn't find any can methods" do
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @actions.find_can(:can_view?).should == false
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
                it "shouldn't find any able methods" do
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @actions.find_able(:viewable_by?).should == false
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
              describe "with a custom action" do
         
     | 
| 
      
 19 
     | 
    
         
            +
                before do
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @actions.create_action(:foo, :fooable)
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
                it "should have the can and able present" do
         
     | 
| 
      
 23 
     | 
    
         
            +
                  @actions.has_can?(:foo).should == true
         
     | 
| 
      
 24 
     | 
    
         
            +
                  @actions.has_able?(:fooable).should == true
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
                it "shouln't have any other cans or ables present" do
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @actions.has_can?(:bar).should == false
         
     | 
| 
      
 28 
     | 
    
         
            +
                  @actions.has_able?(:barable).should == false
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
                it "should find the can method" do
         
     | 
| 
      
 31 
     | 
    
         
            +
                  @actions.find_can(:can_foo?).should == :foo
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
                it "should find the able method" do
         
     | 
| 
      
 34 
     | 
    
         
            +
                  @actions.find_able(:fooable_by?).should == :fooable
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
                it "should get the can from the able" do
         
     | 
| 
      
 37 
     | 
    
         
            +
                  @actions.can_from_able(:fooable).should == :foo
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
              it "should create several actions from an array" do
         
     | 
| 
      
 41 
     | 
    
         
            +
                some_actions = {:bleh => :blehable, :beef => :beefable}
         
     | 
| 
      
 42 
     | 
    
         
            +
                @actions.create_action(some_actions)
         
     | 
| 
      
 43 
     | 
    
         
            +
                @actions.actions.should == some_actions
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
              describe "with the crud actions" do
         
     | 
| 
      
 46 
     | 
    
         
            +
                before do
         
     | 
| 
      
 47 
     | 
    
         
            +
                  @actions.add_crud!
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
                # This isnt DRY at all but I want to know which one is failing
         
     | 
| 
      
 50 
     | 
    
         
            +
                it "should have C for create" do
         
     | 
| 
      
 51 
     | 
    
         
            +
                  @actions.has_can?(:create).should == true
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
                it "should have R for ...er... view" do
         
     | 
| 
      
 54 
     | 
    
         
            +
                  @actions.has_can?(:view).should == true
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
                it "should have U for update" do
         
     | 
| 
      
 57 
     | 
    
         
            +
                  @actions.has_can?(:update).should == true
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
                it "should have D for delete" do
         
     | 
| 
      
 60 
     | 
    
         
            +
                  @actions.has_can?(:destroy).should == true
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
              end
         
     | 
| 
      
 63 
     | 
    
         
            +
              
         
     | 
| 
      
 64 
     | 
    
         
            +
              it "should fire the add callback and set the last added" do
         
     | 
| 
      
 65 
     | 
    
         
            +
                klass = Thwart::ActionsStore.clone
         
     | 
| 
      
 66 
     | 
    
         
            +
                actionz = klass.new
         
     | 
| 
      
 67 
     | 
    
         
            +
                receiver = double('receiver')
         
     | 
| 
      
 68 
     | 
    
         
            +
                receiver.should_receive(:before).with(actionz)
         
     | 
| 
      
 69 
     | 
    
         
            +
                klass.set_callback :add, :before, receiver
         
     | 
| 
      
 70 
     | 
    
         
            +
                
         
     | 
| 
      
 71 
     | 
    
         
            +
                actionz.create_action(:do, :doable)
         
     | 
| 
      
 72 
     | 
    
         
            +
                actionz.last_action.should == :do
         
     | 
| 
      
 73 
     | 
    
         
            +
              end
         
     | 
| 
      
 74 
     | 
    
         
            +
            end
         
     | 
    
        data/spec/actor_spec.rb
    ADDED
    
    | 
         @@ -0,0 +1,45 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe Thwart::Actor do
         
     | 
| 
      
 4 
     | 
    
         
            +
              it "should not add the thwart_role method until thwart_access is called" do
         
     | 
| 
      
 5 
     | 
    
         
            +
                actor_class = generic_model("Generic Resource") do 
         
     | 
| 
      
 6 
     | 
    
         
            +
                  include Thwart::Actor
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
                actor_class.new.should_not respond_to(:thwart_role)
         
     | 
| 
      
 9 
     | 
    
         
            +
                actor_class.thwart_access
         
     | 
| 
      
 10 
     | 
    
         
            +
                actor_class.new.should respond_to(:thwart_role)
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              context "thwart_role defining and finding" do
         
     | 
| 
      
 14 
     | 
    
         
            +
                before do
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @actor_class = generic_model("Generic Resource") do 
         
     | 
| 
      
 16 
     | 
    
         
            +
                    include Thwart::Actor
         
     | 
| 
      
 17 
     | 
    
         
            +
                    def arbitrary_attribute
         
     | 
| 
      
 18 
     | 
    
         
            +
                      :arb_return
         
     | 
| 
      
 19 
     | 
    
         
            +
                    end
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
                it "should return a default role" do
         
     | 
| 
      
 23 
     | 
    
         
            +
                  @actor_class.thwart_access do
         
     | 
| 
      
 24 
     | 
    
         
            +
                    role :a_role
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @actor_class.new.thwart_role.should == :a_role
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
                it "should allow the specifcation of a method" do
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @actor_class.thwart_access do
         
     | 
| 
      
 30 
     | 
    
         
            +
                    role_method :arbitrary_attribute
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
                  @actor_class.new.thwart_role.should == :arb_return
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
                it "should allow the specification of a proc" do
         
     | 
| 
      
 35 
     | 
    
         
            +
                  @actor_class.thwart_access do
         
     | 
| 
      
 36 
     | 
    
         
            +
                    role_proc Proc.new { |a|
         
     | 
| 
      
 37 
     | 
    
         
            +
                      a.nil?.should == false
         
     | 
| 
      
 38 
     | 
    
         
            +
                      :proc_return
         
     | 
| 
      
 39 
     | 
    
         
            +
                    }
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
                  @actor_class.new.thwart_role.should == :proc_return
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
      
 44 
     | 
    
         
            +
                
         
     | 
| 
      
 45 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,41 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe "with some inheriting models set up" do
         
     | 
| 
      
 4 
     | 
    
         
            +
              before do
         
     | 
| 
      
 5 
     | 
    
         
            +
                @with_cans = generic_model do 
         
     | 
| 
      
 6 
     | 
    
         
            +
                  include Thwart::Cans
         
     | 
| 
      
 7 
     | 
    
         
            +
                end.new
         
     | 
| 
      
 8 
     | 
    
         
            +
                @with_ables = generic_model do 
         
     | 
| 
      
 9 
     | 
    
         
            +
                  include Thwart::Ables
         
     | 
| 
      
 10 
     | 
    
         
            +
                end.new
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
              
         
     | 
| 
      
 13 
     | 
    
         
            +
              describe Thwart::Cans do
         
     | 
| 
      
 14 
     | 
    
         
            +
                it "shouldn't find non existant methods" do
         
     | 
| 
      
 15 
     | 
    
         
            +
                  Thwart::Actions.should_receive(:find_can).with(:can_view?).at_least(1).and_return(false)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  lambda {@with_cans.can_view?(@with_ables) }.should raise_error(NoMethodError)
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
                it "should find a method" do
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # rspec seems to call respond_to which calls this and it needs to work        
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # Thwart::Actions.should_receive(:find_can).with(:can_view?).at_least(1).and_return(:view) 
         
     | 
| 
      
 21 
     | 
    
         
            +
                  Thwart::Actions.should_receive(:actions).any_number_of_times.and_return({:view => :viewable})
         
     | 
| 
      
 22 
     | 
    
         
            +
                  Thwart.should_receive(:query).with(@with_cans, @with_ables, :view)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  @with_cans.can_view?(@with_ables)
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
              
         
     | 
| 
      
 27 
     | 
    
         
            +
              describe Thwart::Ables do
         
     | 
| 
      
 28 
     | 
    
         
            +
                it "shouldn't find any methods" do
         
     | 
| 
      
 29 
     | 
    
         
            +
                  Thwart::Actions.should_receive(:find_able).with(:viewable_by?).at_least(1).and_return(false)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  lambda {@with_ables.viewable_by?(@with_cans) }.should raise_error(NoMethodError)
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
                it "should find a method" do
         
     | 
| 
      
 33 
     | 
    
         
            +
                  # rspec seems to call respond_to which calls this and it needs to work properly
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # Thwart::Actions.should_receive(:find_able).with(:viewable_by?).at_least(1).and_return(:viewable)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  Thwart::Actions.should_receive(:actions).any_number_of_times.and_return({:view => :viewable})
         
     | 
| 
      
 36 
     | 
    
         
            +
                  Thwart::Actions.should_receive(:can_from_able).with(:viewable).and_return(:view)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  Thwart.should_receive(:query).with(@with_cans, @with_ables, :view)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  @with_ables.viewable_by?(@with_cans)
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
            end
         
     | 
    
        data/spec/dsl_spec.rb
    ADDED
    
    | 
         @@ -0,0 +1,103 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe Thwart::Dsl do
         
     | 
| 
      
 4 
     | 
    
         
            +
              before do
         
     | 
| 
      
 5 
     | 
    
         
            +
                @target = double("target")
         
     | 
| 
      
 6 
     | 
    
         
            +
              end
         
     | 
| 
      
 7 
     | 
    
         
            +
              describe "with no given map" do
         
     | 
| 
      
 8 
     | 
    
         
            +
                before do
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @dsl = Thwart::Dsl.new
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
                
         
     | 
| 
      
 12 
     | 
    
         
            +
                it "should pass methods on to the target" do
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @target.should_receive(:foo)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @dsl.evaluate(@target) do
         
     | 
| 
      
 15 
     | 
    
         
            +
                    foo
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
                
         
     | 
| 
      
 19 
     | 
    
         
            +
                it "should pass writer methods on to the target" do
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @target.should_receive(:foo=).with("bar")
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @dsl.evaluate(@target) do
         
     | 
| 
      
 22 
     | 
    
         
            +
                    foo "bar"
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
                
         
     | 
| 
      
 26 
     | 
    
         
            +
                it "shouldn't find non existant methods" do
         
     | 
| 
      
 27 
     | 
    
         
            +
                  lambda { @dsl.evaluate(@target) do
         
     | 
| 
      
 28 
     | 
    
         
            +
                    foo "bar"
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end }.should raise_error(NoMethodError)
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
              end
         
     | 
| 
      
 32 
     | 
    
         
            +
              
         
     | 
| 
      
 33 
     | 
    
         
            +
              describe "with an extra map" do
         
     | 
| 
      
 34 
     | 
    
         
            +
                before do
         
     | 
| 
      
 35 
     | 
    
         
            +
                  @dsl = Thwart::Dsl.new :imperative => :foo=
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
                it "should map attributes on to the target" do 
         
     | 
| 
      
 38 
     | 
    
         
            +
                  @target.should_receive(:foo=).with("bar")
         
     | 
| 
      
 39 
     | 
    
         
            +
                  @dsl.evaluate(@target) do
         
     | 
| 
      
 40 
     | 
    
         
            +
                    imperative "bar"
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
      
 44 
     | 
    
         
            +
              
         
     | 
| 
      
 45 
     | 
    
         
            +
              describe "with method_missing defined on the target" do
         
     | 
| 
      
 46 
     | 
    
         
            +
                before do
         
     | 
| 
      
 47 
     | 
    
         
            +
                  target_class = Class.new do
         
     | 
| 
      
 48 
     | 
    
         
            +
                    def respond_to?(name)
         
     | 
| 
      
 49 
     | 
    
         
            +
                      return true if [:test1, :test2].include?(name)
         
     | 
| 
      
 50 
     | 
    
         
            +
                      super
         
     | 
| 
      
 51 
     | 
    
         
            +
                    end
         
     | 
| 
      
 52 
     | 
    
         
            +
                    def method_missing(name, *args)
         
     | 
| 
      
 53 
     | 
    
         
            +
                      return self.test_method_called(name, *args) if self.respond_to?(name)
         
     | 
| 
      
 54 
     | 
    
         
            +
                      super
         
     | 
| 
      
 55 
     | 
    
         
            +
                    end
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
                  @target = target_class.new
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
                it "should have a proper target to test with" do
         
     | 
| 
      
 60 
     | 
    
         
            +
                  @target.should_receive(:test_method_called).with(:test1)
         
     | 
| 
      
 61 
     | 
    
         
            +
                  @target.should_receive(:test_method_called).with(:test2)
         
     | 
| 
      
 62 
     | 
    
         
            +
                  @target.should_receive(:test_method_called).with(:test1, :foo, :bar)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  @target.respond_to?(:test1).should == true
         
     | 
| 
      
 64 
     | 
    
         
            +
                  @target.respond_to?(:test2).should == true
         
     | 
| 
      
 65 
     | 
    
         
            +
                  @target.test1
         
     | 
| 
      
 66 
     | 
    
         
            +
                  @target.test2
         
     | 
| 
      
 67 
     | 
    
         
            +
                  @target.test1 :foo, :bar
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
                context "with a all = true DSL" do
         
     | 
| 
      
 70 
     | 
    
         
            +
                  before do 
         
     | 
| 
      
 71 
     | 
    
         
            +
                    @dsl = Thwart::Dsl.new :test2 => :something_else
         
     | 
| 
      
 72 
     | 
    
         
            +
                    @dsl.all = true
         
     | 
| 
      
 73 
     | 
    
         
            +
                  end
         
     | 
| 
      
 74 
     | 
    
         
            +
                  it "should call method missing on the target if the DSL doesn't have the method defined" do
         
     | 
| 
      
 75 
     | 
    
         
            +
                    @target.should_receive(:test_method_called).with(:test1)
         
     | 
| 
      
 76 
     | 
    
         
            +
                    @dsl.evaluate @target do
         
     | 
| 
      
 77 
     | 
    
         
            +
                      test1
         
     | 
| 
      
 78 
     | 
    
         
            +
                    end
         
     | 
| 
      
 79 
     | 
    
         
            +
                  end
         
     | 
| 
      
 80 
     | 
    
         
            +
                  it "should call the method in the method map if the DSL has the method in the map" do
         
     | 
| 
      
 81 
     | 
    
         
            +
                    @target.should_not_receive(:method_missing)
         
     | 
| 
      
 82 
     | 
    
         
            +
                    @target.should_receive(:something_else)
         
     | 
| 
      
 83 
     | 
    
         
            +
                    @dsl.evaluate @target do
         
     | 
| 
      
 84 
     | 
    
         
            +
                      test2
         
     | 
| 
      
 85 
     | 
    
         
            +
                    end
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
                  it "should properly pass arguments to the missing method" do
         
     | 
| 
      
 88 
     | 
    
         
            +
                    @target.should_receive(:test_method_called).with(:test1, :foo, :baz)
         
     | 
| 
      
 89 
     | 
    
         
            +
                    @dsl.evaluate @target do
         
     | 
| 
      
 90 
     | 
    
         
            +
                      test1 :foo, :baz
         
     | 
| 
      
 91 
     | 
    
         
            +
                    end
         
     | 
| 
      
 92 
     | 
    
         
            +
                  end
         
     | 
| 
      
 93 
     | 
    
         
            +
                end
         
     | 
| 
      
 94 
     | 
    
         
            +
                it "shouldn't call method missing on the target if the DSL doesn't have the method defined and all is false" do
         
     | 
| 
      
 95 
     | 
    
         
            +
                  @target.should_not_receive(:test_method_called).with(:test1)
         
     | 
| 
      
 96 
     | 
    
         
            +
                  @dsl = Thwart::Dsl.new
         
     | 
| 
      
 97 
     | 
    
         
            +
                  @dsl.all = false
         
     | 
| 
      
 98 
     | 
    
         
            +
                  lambda { @dsl.evaluate @target do
         
     | 
| 
      
 99 
     | 
    
         
            +
                    test1
         
     | 
| 
      
 100 
     | 
    
         
            +
                  end }.should raise_error(NameError)
         
     | 
| 
      
 101 
     | 
    
         
            +
                end
         
     | 
| 
      
 102 
     | 
    
         
            +
              end
         
     | 
| 
      
 103 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,17 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            describe Thwart::Enforcer do
         
     | 
| 
      
 5 
     | 
    
         
            +
              context "access enforcment" do
         
     | 
| 
      
 6 
     | 
    
         
            +
                it "should need the current user method defined on the controller" do
         
     | 
| 
      
 7 
     | 
    
         
            +
                  lambda { instance_with_module(Thwart::Enforcer).thwart_access }.should raise_error(ArgumentError)
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
                it "should need the params hash to have an action key" do
         
     | 
| 
      
 10 
     | 
    
         
            +
                  class_with_module(Thwart::Enforcer)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  lambda { @controller.thwart_access }.should raise_error(ArgumentError)
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
                it "should need the params[:actions] to be a recognized action"
         
     | 
| 
      
 14 
     | 
    
         
            +
                it "should thwart access by raising an error if the user doesn't have permission"
         
     | 
| 
      
 15 
     | 
    
         
            +
                it "should return true if the user does have permission"
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,42 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe Thwart::Resource do
         
     | 
| 
      
 4 
     | 
    
         
            +
              describe "without a special name" do
         
     | 
| 
      
 5 
     | 
    
         
            +
                before do
         
     | 
| 
      
 6 
     | 
    
         
            +
                  resource_class = generic_model("Generic Resource") do 
         
     | 
| 
      
 7 
     | 
    
         
            +
                    include Thwart::Resource
         
     | 
| 
      
 8 
     | 
    
         
            +
                    thwart_access
         
     | 
| 
      
 9 
     | 
    
         
            +
                  end
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @resource = resource_class.new
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
                
         
     | 
| 
      
 13 
     | 
    
         
            +
                it "should have set its own name" do
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @resource.class.thwart_name.should == "generic"
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
              
         
     | 
| 
      
 18 
     | 
    
         
            +
              describe "with a special name" do
         
     | 
| 
      
 19 
     | 
    
         
            +
                before do
         
     | 
| 
      
 20 
     | 
    
         
            +
                  resource_class = generic_model("Generic Resource") do 
         
     | 
| 
      
 21 
     | 
    
         
            +
                    include Thwart::Resource
         
     | 
| 
      
 22 
     | 
    
         
            +
                    thwart_access do
         
     | 
| 
      
 23 
     | 
    
         
            +
                      name "special"
         
     | 
| 
      
 24 
     | 
    
         
            +
                    end
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @resource = resource_class.new
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
                
         
     | 
| 
      
 29 
     | 
    
         
            +
                it "should have a different name" do
         
     | 
| 
      
 30 
     | 
    
         
            +
                  @resource.class.thwart_name.should == "special"
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
              
         
     | 
| 
      
 34 
     | 
    
         
            +
              describe "without a name" do
         
     | 
| 
      
 35 
     | 
    
         
            +
                it "should raise an error if no name could be found" do
         
     | 
| 
      
 36 
     | 
    
         
            +
                  lambda { @resource = Class.new do
         
     | 
| 
      
 37 
     | 
    
         
            +
                    include Thwart::Resource
         
     | 
| 
      
 38 
     | 
    
         
            +
                    thwart_access
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end }.should raise_error(Thwart::MissingAttributeError)
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
            end
         
     |