Skip to content

Commit

Permalink
Renaming Attribute Protection and solving problem modifying the provi…
Browse files Browse the repository at this point in the history
…ded hash to the #attributes= method
  • Loading branch information
samlown committed Oct 22, 2010
1 parent 1d1d815 commit d0ed97e
Show file tree
Hide file tree
Showing 12 changed files with 65 additions and 112 deletions.
6 changes: 3 additions & 3 deletions README.md
Expand Up @@ -192,14 +192,14 @@ documents and retrieve them using the CastedModel module. Simply include the mod
a Hash (or other model that responds to the [] and []= methods) and set any properties
you'd like to use. For example:

class CatToy << Hash
class CatToy < Hash
include CouchRest::Model::CastedModel

property :name, String
property :purchased, Date
end

class Cat << CouchRest::Model::Base
class Cat < CouchRest::Model::Base
property :name, String
property :toys, [CatToy]
end
Expand All @@ -218,7 +218,7 @@ Ruby will bring up a missing constant error. To avoid this, or if you have a rea
you'd like to model, the latest version of CouchRest Model (> 1.0.0) supports creating
anonymous classes:

class Cat << CouchRest::Model::Base
class Cat < CouchRest::Model::Base
property :name, String

property :toys do |toy|
Expand Down
2 changes: 1 addition & 1 deletion Rakefile
Expand Up @@ -27,7 +27,7 @@ begin
gemspec.extra_rdoc_files = %w( README.md LICENSE THANKS.md )
gemspec.files = %w( LICENSE README.md Rakefile THANKS.md history.txt couchrest.gemspec) + Dir["{examples,lib,spec}/**/*"] - Dir["spec/tmp"]
gemspec.has_rdoc = true
gemspec.add_dependency("couchrest", "~> 1.0.0")
gemspec.add_dependency("couchrest", "~> 1.0.1")
gemspec.add_dependency("mime-types", "~> 1.15")
gemspec.add_dependency("activemodel", "~> 3.0.0.rc")
gemspec.add_dependency("tzinfo", "~> 0.3.22")
Expand Down
68 changes: 0 additions & 68 deletions lib/couchrest/model/attributes.rb

This file was deleted.

4 changes: 2 additions & 2 deletions lib/couchrest/model/base.rb
Expand Up @@ -13,7 +13,7 @@ class Base < Document
include CouchRest::Model::ExtendedAttachments
include CouchRest::Model::ClassProxy
include CouchRest::Model::Collection
include CouchRest::Model::AttributeProtection
include CouchRest::Model::PropertyProtection
include CouchRest::Model::Associations
include CouchRest::Model::Validations

Expand Down Expand Up @@ -47,7 +47,7 @@ def self.inherited(subklass)
# * :directly_set_attributes: true when data comes directly from database
#
def initialize(doc = {}, options = {})
prepare_all_attributes(doc, options)
doc = prepare_all_attributes(doc, options)
super(doc)
unless self['_id'] && self['_rev']
self[self.model_type_key] = self.class.to_s
Expand Down
2 changes: 1 addition & 1 deletion lib/couchrest/model/casted_model.rb
Expand Up @@ -7,7 +7,7 @@ module CastedModel
include CouchRest::Model::Configuration
include CouchRest::Model::Callbacks
include CouchRest::Model::Properties
include CouchRest::Model::AttributeProtection
include CouchRest::Model::PropertyProtection
include CouchRest::Model::Associations
include CouchRest::Model::Validations
attr_accessor :casted_by
Expand Down
2 changes: 1 addition & 1 deletion lib/couchrest/model/configuration.rb
Expand Up @@ -12,7 +12,7 @@ module Configuration
add_config :mass_assign_any_attribute

configure do |config|
config.model_type_key = 'model'
config.model_type_key = 'couchrest-type' # 'model'?
config.mass_assign_any_attribute = false
end
end
Expand Down
12 changes: 9 additions & 3 deletions lib/couchrest/model/properties.rb
Expand Up @@ -61,7 +61,7 @@ def prepare_all_attributes(doc = {}, options = {})
if options[:directly_set_attributes]
directly_set_read_only_attributes(doc)
else
remove_protected_attributes(doc)
doc = remove_protected_attributes(doc)
end
directly_set_attributes(doc) unless doc.nil?
end
Expand All @@ -72,12 +72,18 @@ def find_property!(property)
prop
end

# Set all the attributes and return a hash with the attributes
# that have not been accepted.
def directly_set_attributes(hash)
hash.each do |attribute_name, attribute_value|
hash.reject do |attribute_name, attribute_value|
if self.respond_to?("#{attribute_name}=")
self.send("#{attribute_name}=", hash.delete(attribute_name))
self.send("#{attribute_name}=", attribute_value)
true
elsif mass_assign_any_attribute # config option
self[attribute_name] = attribute_value
true
else
false
end
end
end
Expand Down
@@ -1,9 +1,9 @@
module CouchRest
module Model
module AttributeProtection
module PropertyProtection
extend ActiveSupport::Concern

# Attribute protection from mass assignment to CouchRest::Model properties
# Property protection from mass assignment to CouchRest::Model properties
#
# Protected methods will be removed from
# * new
Expand All @@ -22,7 +22,7 @@ module AttributeProtection
#
# 3) Mix and match, and assume all unspecified properties are protected.
# property :name, :accessible => true
# property :admin, :protected => true
# property :admin, :protected => true # ignored
# property :phone # this will be automatically protected
#
# Note: the timestamps! method protectes the created_at and updated_at properties
Expand All @@ -34,11 +34,16 @@ def self.included(base)

module ClassMethods
def accessible_properties
properties.select { |prop| prop.options[:accessible] }
props = properties.select { |prop| prop.options[:accessible] }
if props.empty?
props = properties.select { |prop| !prop.options[:protected] }
end
props
end

def protected_properties
properties.select { |prop| prop.options[:protected] }
accessibles = accessible_properties
properties.reject { |prop| accessibles.include?(prop) }
end
end

Expand All @@ -50,28 +55,17 @@ def protected_properties
self.class.protected_properties
end

# Return a new copy of the attributes hash with protected attributes
# removed.
def remove_protected_attributes(attributes)
protected_names = properties_to_remove_from_mass_assignment.map { |prop| prop.name }
return attributes if protected_names.empty?
protected_names = protected_properties.map { |prop| prop.name }
return attributes if protected_names.empty? or attributes.nil?

attributes.reject! do |property_name, property_value|
attributes.reject do |property_name, property_value|
protected_names.include?(property_name.to_s)
end if attributes

attributes || {}
end

private

def properties_to_remove_from_mass_assignment
to_remove = protected_properties

unless accessible_properties.empty?
to_remove += properties.reject { |prop| prop.options[:accessible] }
end

to_remove
end

end
end
end
2 changes: 1 addition & 1 deletion lib/couchrest_model.rb
Expand Up @@ -35,6 +35,7 @@
require "couchrest/model/persistence"
require "couchrest/model/typecast"
require "couchrest/model/property"
require "couchrest/model/property_protection"
require "couchrest/model/casted_array"
require "couchrest/model/properties"
require "couchrest/model/validations"
Expand All @@ -45,7 +46,6 @@
require "couchrest/model/extended_attachments"
require "couchrest/model/class_proxy"
require "couchrest/model/collection"
require "couchrest/model/attribute_protection"
require "couchrest/model/associations"
require "couchrest/model/configuration"

Expand Down
11 changes: 2 additions & 9 deletions spec/couchrest/configuration_spec.rb
Expand Up @@ -62,20 +62,13 @@
@default_model_key = 'model'
end

it "should set default configuration options on Model::Base" do
CouchRest::Model::Base.model_type_key.should eql(@default_model_key)
end

it "should provide options from instance" do
cat = Cat.new
cat.model_type_key.should eql(@default_model_key)
end

it "should be possible to override on class using configure method" do
default_model_key = Cat.model_type_key
Cat.instance_eval do
model_type_key 'cat-type'
end
CouchRest::Model::Base.model_type_key.should eql(@default_model_key)
CouchRest::Model::Base.model_type_key.should eql(default_model_key)
Cat.model_type_key.should eql('cat-type')
cat = Cat.new
cat.model_type_key.should eql('cat-type')
Expand Down
Expand Up @@ -34,6 +34,12 @@ class NoProtection < CouchRest::Model::Base
user.name.should == "will"
user.phone.should == "555-5555"
end

it "should provide a list of all properties as accessible" do
user = NoProtection.new(:name => "will", :phone => "555-5555")
user.accessible_properties.length.should eql(2)
user.protected_properties.should be_empty
end
end

describe "Model Base", "accessible flag" do
Expand Down Expand Up @@ -65,6 +71,12 @@ class WithAccessible < CouchRest::Model::Base
user.name.should == "will"
user.admin.should == false
end

it "should provide correct accessible and protected property lists" do
user = WithAccessible.new(:name => 'will', :admin => true)
user.accessible_properties.map{|p| p.to_s}.should eql(['name'])
user.protected_properties.map{|p| p.to_s}.should eql(['admin'])
end
end

describe "Model Base", "protected flag" do
Expand Down Expand Up @@ -96,6 +108,21 @@ class WithProtected < CouchRest::Model::Base
user.name.should == "will"
user.admin.should == false
end

it "should not modify the provided attribute hash" do
user = WithProtected.new
attrs = {:name => "will", :admin => true}
user.attributes = attrs
attrs[:admin].should be_true
attrs[:name].should eql('will')
end

it "should provide correct accessible and protected property lists" do
user = WithProtected.new(:name => 'will', :admin => true)
user.accessible_properties.map{|p| p.to_s}.should eql(['name'])
user.protected_properties.map{|p| p.to_s}.should eql(['admin'])
end

end

describe "Model Base", "mixing protected and accessible flags" do
Expand All @@ -115,6 +142,7 @@ class WithBothAndUnspecified < CouchRest::Model::Base
user.admin.should == false
user.phone.should == 'unset phone number'
end

end

describe "from database" do
Expand Down
2 changes: 1 addition & 1 deletion spec/couchrest/subclass_spec.rb
Expand Up @@ -93,7 +93,7 @@ class Dog < Animal; end
end

it "should have an all view with a guard clause for model == subclass name in the map function" do
OnlineCourse.design_doc['views']['all']['map'].should =~ /if \(doc\['model'\] == 'OnlineCourse'\)/
OnlineCourse.design_doc['views']['all']['map'].should =~ /if \(doc\['#{OnlineCourse.model_type_key}'\] == 'OnlineCourse'\)/
end
end

0 comments on commit d0ed97e

Please sign in to comment.