/usr/lib/ruby/vendor_ruby/active_record/attribute_assignment.rb is in ruby-activerecord-3.2 3.2.16-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | require 'active_support/concern'
module ActiveRecord
module AttributeAssignment
extend ActiveSupport::Concern
include ActiveModel::MassAssignmentSecurity
module ClassMethods
private
# The primary key and inheritance column can never be set by mass-assignment for security reasons.
def attributes_protected_by_default
default = [ primary_key, inheritance_column ]
default << 'id' unless primary_key.eql? 'id'
default
end
end
# Allows you to set all the attributes at once by passing in a hash with keys
# matching the attribute names (which again matches the column names).
#
# If any attributes are protected by either +attr_protected+ or
# +attr_accessible+ then only settable attributes will be assigned.
#
# class User < ActiveRecord::Base
# attr_protected :is_admin
# end
#
# user = User.new
# user.attributes = { :username => 'Phusion', :is_admin => true }
# user.username # => "Phusion"
# user.is_admin? # => false
def attributes=(new_attributes)
return unless new_attributes.is_a?(Hash)
assign_attributes(new_attributes)
end
# Allows you to set all the attributes for a particular mass-assignment
# security role by passing in a hash of attributes with keys matching
# the attribute names (which again matches the column names) and the role
# name using the :as option.
#
# To bypass mass-assignment security you can use the :without_protection => true
# option.
#
# class User < ActiveRecord::Base
# attr_accessible :name
# attr_accessible :name, :is_admin, :as => :admin
# end
#
# user = User.new
# user.assign_attributes({ :name => 'Josh', :is_admin => true })
# user.name # => "Josh"
# user.is_admin? # => false
#
# user = User.new
# user.assign_attributes({ :name => 'Josh', :is_admin => true }, :as => :admin)
# user.name # => "Josh"
# user.is_admin? # => true
#
# user = User.new
# user.assign_attributes({ :name => 'Josh', :is_admin => true }, :without_protection => true)
# user.name # => "Josh"
# user.is_admin? # => true
def assign_attributes(new_attributes, options = {})
return if new_attributes.blank?
attributes = new_attributes.stringify_keys
multi_parameter_attributes = []
nested_parameter_attributes = []
@mass_assignment_options = options
unless options[:without_protection]
attributes = sanitize_for_mass_assignment(attributes, mass_assignment_role)
end
attributes.each do |k, v|
if k.include?("(")
multi_parameter_attributes << [ k, v ]
elsif respond_to?("#{k}=")
if v.is_a?(Hash)
nested_parameter_attributes << [ k, v ]
else
send("#{k}=", v)
end
else
raise(UnknownAttributeError, "unknown attribute: #{k}")
end
end
# assign any deferred nested attributes after the base attributes have been set
nested_parameter_attributes.each do |k,v|
send("#{k}=", v)
end
@mass_assignment_options = nil
assign_multiparameter_attributes(multi_parameter_attributes)
end
protected
def mass_assignment_options
@mass_assignment_options ||= {}
end
def mass_assignment_role
mass_assignment_options[:as] || :default
end
private
# Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
# by calling new on the column type or aggregation type (through composed_of) object with these parameters.
# So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
# written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
# parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum,
# f for Float, s for String, and a for Array. If all the values for a given attribute are empty, the
# attribute will be set to nil.
def assign_multiparameter_attributes(pairs)
execute_callstack_for_multiparameter_attributes(
extract_callstack_for_multiparameter_attributes(pairs)
)
end
def instantiate_time_object(name, values)
if self.class.send(:create_time_zone_conversion_attribute?, name, column_for_attribute(name))
Time.zone.local(*values)
else
Time.time_with_datetime_fallback(self.class.default_timezone, *values)
end
end
def execute_callstack_for_multiparameter_attributes(callstack)
errors = []
callstack.each do |name, values_with_empty_parameters|
begin
send(name + "=", read_value_from_parameter(name, values_with_empty_parameters))
rescue => ex
errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name}", ex, name)
end
end
unless errors.empty?
raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
end
end
def read_value_from_parameter(name, values_hash_from_param)
klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
if values_hash_from_param.values.all?{|v|v.nil?}
nil
elsif klass == Time
read_time_parameter_value(name, values_hash_from_param)
elsif klass == Date
read_date_parameter_value(name, values_hash_from_param)
else
read_other_parameter_value(klass, name, values_hash_from_param)
end
end
def read_time_parameter_value(name, values_hash_from_param)
# If Date bits were not provided, error
raise "Missing Parameter" if [1,2,3].any?{|position| !values_hash_from_param.has_key?(position)}
max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param, 6)
# If Date bits were provided but blank, then return nil
return nil if (1..3).any? {|position| values_hash_from_param[position].blank?}
set_values = (1..max_position).collect{|position| values_hash_from_param[position] }
# If Time bits are not there, then default to 0
(3..5).each {|i| set_values[i] = set_values[i].blank? ? 0 : set_values[i]}
instantiate_time_object(name, set_values)
end
def read_date_parameter_value(name, values_hash_from_param)
return nil if (1..3).any? {|position| values_hash_from_param[position].blank?}
set_values = [values_hash_from_param[1], values_hash_from_param[2], values_hash_from_param[3]]
begin
Date.new(*set_values)
rescue ArgumentError # if Date.new raises an exception on an invalid date
instantiate_time_object(name, set_values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
end
end
def read_other_parameter_value(klass, name, values_hash_from_param)
max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param)
values = (1..max_position).collect do |position|
raise "Missing Parameter" if !values_hash_from_param.has_key?(position)
values_hash_from_param[position]
end
klass.new(*values)
end
def extract_max_param_for_multiparameter_attributes(values_hash_from_param, upper_cap = 100)
[values_hash_from_param.keys.max,upper_cap].min
end
def extract_callstack_for_multiparameter_attributes(pairs)
attributes = { }
pairs.each do |pair|
multiparameter_name, value = pair
attribute_name = multiparameter_name.split("(").first
attributes[attribute_name] = {} unless attributes.include?(attribute_name)
parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value
end
attributes
end
def type_cast_attribute_value(multiparameter_name, value)
multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
end
def find_parameter_position(multiparameter_name)
multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i
end
end
end
|