diff --git a/lib/cancan/ability.rb b/lib/cancan/ability.rb index 1287938..5b6d846 100644 --- a/lib/cancan/ability.rb +++ b/lib/cancan/ability.rb @@ -206,6 +206,14 @@ module CanCan message.blank? ? nil : message end + def attributes_for(action, subject) + attributes = {} + relevant_can_definitions(action, subject).map do |can_definition| + attributes.merge!(can_definition.new_attributes) if can_definition.base_behavior + end + attributes + end + private def unauthorized_message_keys(action, subject) diff --git a/lib/cancan/can_definition.rb b/lib/cancan/can_definition.rb index dbe56ca..41442df 100644 --- a/lib/cancan/can_definition.rb +++ b/lib/cancan/can_definition.rb @@ -67,6 +67,14 @@ module CanCan hash end + def new_attributes + attributes = {} + @conditions.each do |key, value| + attributes[key] = value unless [Array, Range, Hash].include? value.class + end + attributes + end + private def subject_class?(subject) diff --git a/lib/cancan/controller_resource.rb b/lib/cancan/controller_resource.rb index ec5a87a..762dd67 100644 --- a/lib/cancan/controller_resource.rb +++ b/lib/cancan/controller_resource.rb @@ -51,7 +51,15 @@ module CanCan def build_resource method_name = @options[:singleton] ? "build_#{name}" : "new" - resource_base.send(*[method_name, @params[name]].compact) + resource = resource_base.send(*[method_name, @params[name]].compact) + initial_attributes.each do |name, value| + resource.send("#{name}=", value) + end + resource + end + + def initial_attributes + @controller.current_ability.attributes_for(@params[:action].to_sym, resource_class) end def find_resource diff --git a/spec/cancan/ability_spec.rb b/spec/cancan/ability_spec.rb index 160b320..94037f5 100644 --- a/spec/cancan/ability_spec.rb +++ b/spec/cancan/ability_spec.rb @@ -257,6 +257,14 @@ describe CanCan::Ability do @ability.can?(:read, 123 => Range).should be_true end + it "should have initial attributes based on hash conditions of 'new' action" do + @ability.can :manage, Range, :foo => "foo", :hash => {:skip => "hashes"} + @ability.can :create, Range, :bar => 123, :array => %w[skip arrays] + @ability.can :new, Range, :baz => "baz", :range => 1..3 + @ability.cannot :new, Range, :ignore => "me" + @ability.attributes_for(:new, Range).should == {:foo => "foo", :bar => 123, :baz => "baz"} + end + describe "unauthorized message" do after(:each) do I18n.backend = nil diff --git a/spec/cancan/controller_resource_spec.rb b/spec/cancan/controller_resource_spec.rb index 1683f70..b56dae5 100644 --- a/spec/cancan/controller_resource_spec.rb +++ b/spec/cancan/controller_resource_spec.rb @@ -5,6 +5,7 @@ describe CanCan::ControllerResource do @params = HashWithIndifferentAccess.new(:controller => "abilities") @controller = Object.new # simple stub for now stub(@controller).params { @params } + stub(@controller).current_ability.stub!.attributes_for { {} } end it "should load the resource into an instance variable if params[:id] is specified" do @@ -47,12 +48,16 @@ describe CanCan::ControllerResource do @controller.instance_variable_get(:@ability).should == :some_resource end - it "should build a new resource with no arguments if attribute hash isn't specified" do + it "should build a new resource with attributes from current ability" do + @params[:controller] = "people" @params[:action] = "new" - mock(Ability).new { :some_resource } + person = Object.new + mock(Person).new { person } + mock(person).name = "foobar" + stub(@controller).current_ability.stub!.attributes_for(:new, Person) { {:name => "foobar"} } resource = CanCan::ControllerResource.new(@controller) resource.load_resource - @controller.instance_variable_get(:@ability).should == :some_resource + @controller.instance_variable_get(:@person).should == person end it "should not build a resource when on index action" do