Class: Chef::Property
- Inherits:
-
Object
- Object
- Chef::Property
- Defined in:
- lib/chef/property.rb
Overview
Type and validation information for a property on a resource.
A property named “x” manipulates the “@x” instance variable on a resource. The presence of the variable (‘instance_variable_defined?(@x)`) tells whether the variable is defined; it may have any actual value, constrained only by validation.
Properties may have validation, defaults, and coercion, and have full support for lazy values.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#options ⇒ Object
readonly
The options this Property will use for get/set behavior and validation.
Class Method Summary collapse
-
.derive(**options) ⇒ Object
Create a reusable property type that can be used in multiple properties in different resources.
-
.emit_deprecated_alias(from, to, message, declared_in) ⇒ Object
This is to support #deprecated_property_alias, by emitting an alias and a deprecation warning when called.
Instance Method Summary collapse
-
#call(resource, value = NOT_PASSED) ⇒ Object
Handle the property being called.
-
#coerce(resource, value) ⇒ Object
Coerce an input value into canonical form for the property.
-
#declared_in ⇒ Class
The class this property was defined in.
-
#default ⇒ Object
The raw default value for this resource.
-
#default_description ⇒ String
A description of the default value of this property.
-
#derive(**modified_options) ⇒ Property
Derive a new Property that is just like this one, except with some added or changed options.
-
#description ⇒ String
A description of this property.
-
#desired_state? ⇒ Boolean
Whether this is part of desired state or not.
-
#emit_dsl ⇒ Object
Emit the DSL for this property into the resource class (‘declared_in`).
-
#equal_to ⇒ Array, NilClass
The equal_to field of this property.
-
#explicitly_accepts_nil?(resource) ⇒ Boolean
private
Find out whether this type accepts nil explicitly.
-
#get(resource, nil_set: false) ⇒ Object
Get the property value from the resource, handling lazy values, defaults, and validation.
- #get_value(resource) ⇒ Object private
-
#has_default? ⇒ Boolean
Whether this property has a default value.
-
#identity? ⇒ Boolean
Whether this is part of the resource’s natural identity or not.
-
#initialize(**options) ⇒ Property
constructor
Create a new property.
-
#instance_variable_name ⇒ Symbol
The instance variable associated with this property.
-
#introduced ⇒ String
When this property was introduced.
-
#is_set?(resource) ⇒ Boolean
Find out whether this property has been set.
-
#name ⇒ String
The name of this property.
-
#name_property? ⇒ Boolean
Whether this is name_property or not.
-
#required?(action = nil) ⇒ Boolean
Whether this property is required or not.
-
#reset(resource) ⇒ Object
Reset the value of this property so that is_set? will return false and the default will be returned in the future.
- #reset_value(resource) ⇒ Object private
-
#sensitive? ⇒ Boolean
Whether this property is sensitive or not.
-
#set(resource, value) ⇒ Object
Set the value of this property in the given resource.
- #set_value(resource, value) ⇒ Object private
-
#skip_docs? ⇒ Boolean
Whether this property should be skipped for documentation purposes.
- #to_s ⇒ Object
-
#validate(resource, value) ⇒ Object
Validate a value.
-
#validation_options ⇒ Hash<Symbol,Object>
Validation options.
- #value_is_set?(resource) ⇒ Boolean private
Constructor Details
#initialize(**options) ⇒ Property
Create a new property.
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 |
# File 'lib/chef/property.rb', line 124 def initialize(**) = .inject({}) { |memo, (key, value)| memo[key.to_sym] = value; memo } @options = [:name] = [:name].to_sym if [:name] [:instance_variable_name] = [:instance_variable_name].to_sym if [:instance_variable_name] # Replace name_attribute with name_property if .key?(:name_attribute) # If we have both name_attribute and name_property and they differ, raise an error if .key?(:name_property) raise ArgumentError, "name_attribute and name_property are functionally identical and both cannot be specified on a property at once. Use just one on property #{self}" end # replace name_property with name_attribute in place = Hash[.map { |k, v| k == :name_attribute ? [ :name_property, v ] : [ k, v ] }] @options = end if .key?(:default) && .key?(:name_property) raise ArgumentError, "A property cannot be both a name_property/name_attribute and have a default value. Use one or the other on property #{self}" end if [:name_property] [:desired_state] = false unless .key?(:desired_state) end # Recursively freeze the default if it isn't a lazy value. unless default.is_a?(DelayedEvaluator) visitor = lambda do |obj| case obj when Hash obj.each_value { |value| visitor.call(value) } when Array obj.each { |value| visitor.call(value) } end obj.freeze end visitor.call(default) end # Validate the default early, so the user gets a good error message, and # cache it so we don't do it again if so begin # If we can validate it all the way to output, do it. @stored_default = input_to_stored_value(nil, default, is_default: true) rescue Chef::Exceptions::CannotValidateStaticallyError # If the validation is not static (i.e. has procs), we will have to # coerce and validate the default each time we run end end |
Instance Attribute Details
#options ⇒ Object (readonly)
The options this Property will use for get/set behavior and validation.
609 610 611 |
# File 'lib/chef/property.rb', line 609 def @options end |
Class Method Details
.derive(**options) ⇒ Object
Create a reusable property type that can be used in multiple properties in different resources.
51 52 53 |
# File 'lib/chef/property.rb', line 51 def self.derive(**) new(**) end |
.emit_deprecated_alias(from, to, message, declared_in) ⇒ Object
This is to support #deprecated_property_alias, by emitting an alias and a deprecation warning when called.
63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/chef/property.rb', line 63 def self.emit_deprecated_alias(from, to, , declared_in) declared_in.class_eval <<-EOM, __FILE__, __LINE__ + 1 def #{from}(value=NOT_PASSED) Chef.deprecated(:property, "#{}") #{to}(value) end def #{from}=(value) Chef.deprecated(:property, "#{}") #{to} = value end EOM end |
Instance Method Details
#call(resource, value = NOT_PASSED) ⇒ Object
Handle the property being called.
The base implementation does the property get-or-set:
“‘ruby resource.myprop # get resource.myprop value # set “`
Subclasses may implement this with any arguments they want, as long as the corresponding DSL calls it correctly.
369 370 371 372 373 374 375 |
# File 'lib/chef/property.rb', line 369 def call(resource, value = NOT_PASSED) if NOT_PASSED == value # see https://github.com/chef/chef/pull/8781 before changing this get(resource) else set(resource, value) end end |
#coerce(resource, value) ⇒ Object
Coerce an input value into canonical form for the property.
After coercion, the value is suitable for storage in the resource. You must validate values after coercion, however.
Does no special handling for lazy values.
513 514 515 516 517 518 519 520 521 |
# File 'lib/chef/property.rb', line 513 def coerce(resource, value) if .key?(:coerce) # nil is never coerced unless value.nil? value = exec_in_resource(resource, [:coerce], value) end end value end |
#declared_in ⇒ Class
The class this property was defined in.
193 194 195 |
# File 'lib/chef/property.rb', line 193 def declared_in [:declared_in] end |
#default ⇒ Object
The raw default value for this resource.
Does not coerce or validate the default. Does not evaluate lazy values.
Defaults to ‘lazy { name }` if name_property is true; otherwise defaults to `nil`
238 239 240 241 242 243 |
# File 'lib/chef/property.rb', line 238 def default return [:default] if .key?(:default) return Chef::DelayedEvaluator.new { name } if name_property? nil end |
#default_description ⇒ String
A description of the default value of this property.
250 251 252 |
# File 'lib/chef/property.rb', line 250 def default_description [:default_description] end |
#derive(**modified_options) ⇒ Property
Derive a new Property that is just like this one, except with some added or changed options.
557 558 559 560 561 562 563 564 565 566 567 568 |
# File 'lib/chef/property.rb', line 557 def derive(**) # Since name_property, name_attribute and default override each other, # if you specify one of them in modified_options it overrides anything in # the original options. = self. if .key?(:name_property) || .key?(:name_attribute) || .key?(:default) = .reject { |k, v| %i{name_attribute name_property default}.include?(k) } end self.class.new(**.merge()) end |
#description ⇒ String
A description of this property.
202 203 204 |
# File 'lib/chef/property.rb', line 202 def description [:description] end |
#desired_state? ⇒ Boolean
Whether this is part of desired state or not.
Defaults to true.
279 280 281 282 283 |
# File 'lib/chef/property.rb', line 279 def desired_state? return true unless .key?(:desired_state) [:desired_state] end |
#emit_dsl ⇒ Object
Emit the DSL for this property into the resource class (‘declared_in`).
Creates a getter and setter for the property.
575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 |
# File 'lib/chef/property.rb', line 575 def emit_dsl # We don't create the getter/setter if it's a custom property; we will # be using the existing getter/setter to manipulate it instead. return unless instance_variable_name # Properties may override existing properties up the inheritance hierarchy, but # properties must not override inherited methods like Object#hash. When the Resource is # placed into the resource collection the ruby Hash object will call the # Object#hash method on the resource, and overriding that with a property will cause # very confusing results. if property_redefines_method? resource_name = declared_in.respond_to?(:resource_name) ? declared_in.resource_name : declared_in raise ArgumentError, "Property `#{name}` of resource `#{resource_name}` overwrites an existing method. A different name should be used for this property." end # We prefer this form because the property name won't show up in the # stack trace if you use `define_method`. declared_in.class_eval <<-EOM, __FILE__, __LINE__ + 1 def #{name}(value=NOT_PASSED) raise "Property `#{name}` of `\#{self}` was incorrectly passed a block. Possible property-resource collision. To call a resource named `#{name}` either rename the property or else use `declare_resource(:#{name}, ...)`" if block_given? self.class.properties[#{name.inspect}].call(self, value) end def #{name}=(value) raise "Property `#{name}` of `\#{self}` was incorrectly passed a block. Possible property-resource collision. To call a resource named `#{name}` either rename the property or else use `declare_resource(:#{name}, ...)`" if block_given? self.class.properties[#{name.inspect}].set(self, value) end EOM end |
#equal_to ⇒ Array, NilClass
The equal_to field of this property.
259 260 261 |
# File 'lib/chef/property.rb', line 259 def equal_to [:equal_to] end |
#explicitly_accepts_nil?(resource) ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Find out whether this type accepts nil explicitly.
A type accepts nil explicitly if “is” allows nil, it validates as nil, and is not simply an empty type.
A type is presumed to accept nil if it does coercion (which must handle nil).
These examples accept nil explicitly: “‘ruby property :a, [ String, nil ] property :a, [ String, NilClass ] property :a, [ String, proc { |v| v.nil? } ] “`
This does not (because the “is” doesn’t exist or doesn’t have nil):
“‘ruby property :x, String “`
These do not, even though nil would validate fine (because they do not have “is”):
“‘ruby property :a property :a, equal_to: [ 1, 2, 3, nil ] property :a, kind_of: [ String, NilClass ] property :a, respond_to: [ ] property :a, callbacks: { “a” => proc { |v| v.nil? } } “`
649 650 651 652 653 654 |
# File 'lib/chef/property.rb', line 649 def explicitly_accepts_nil?(resource) .key?(:coerce) || (.key?(:is) && Chef::Mixin::ParamsValidate.send(:_pv_is, { name => nil }, name, [:is])) rescue Chef::Exceptions::ValidationFailed, Chef::Exceptions::CannotValidateStaticallyError false end |
#get(resource, nil_set: false) ⇒ Object
Get the property value from the resource, handling lazy values, defaults, and validation.
-
If the property’s value is lazy, it is evaluated, coerced and validated.
-
If the property has no value, and is required, raises ValidationFailed.
-
If the property has no value, but has a lazy default, it is evaluated, coerced and validated. If the evaluated value is frozen, the resulting
-
If the property has no value, but has a default, the default value will be returned and frozen. If the default value is lazy, it will be evaluated, coerced and validated, and the result stored in the property.
-
If the property has no value, but is name_property, ‘resource.name` is retrieved, coerced, validated and stored in the property.
-
Otherwise, ‘nil` is returned.
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 |
# File 'lib/chef/property.rb', line 399 def get(resource, nil_set: false) # If it's set, return it (and evaluate any lazy values) value = nil if is_set?(resource) value = get_value(resource) value = stored_value_to_output(resource, value) else # We are getting the default value. if has_default? # If we were able to cache the stored_default, grab it. if defined?(@stored_default) value = @stored_default else # Otherwise, we have to validate it now. value = input_to_stored_value(resource, default, is_default: true) end value = deep_dup(value) value = stored_value_to_output(resource, value) # If the value is mutable (non-frozen), we set it on the instance # so that people can mutate it. (All constant default values are # frozen.) if !value.frozen? && !value.nil? set_value(resource, value) end end end if value.nil? && required?(resource_action(resource)) raise Chef::Exceptions::ValidationFailed, "#{name} is a required property" else value end end |
#get_value(resource) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
657 658 659 660 661 662 663 |
# File 'lib/chef/property.rb', line 657 def get_value(resource) if instance_variable_name resource.instance_variable_get(instance_variable_name) else resource.send(name) end end |
#has_default? ⇒ Boolean
Whether this property has a default value.
299 300 301 |
# File 'lib/chef/property.rb', line 299 def has_default? .key?(:default) || name_property? end |
#identity? ⇒ Boolean
Whether this is part of the resource’s natural identity or not.
268 269 270 |
# File 'lib/chef/property.rb', line 268 def identity? [:identity] end |
#instance_variable_name ⇒ Symbol
The instance variable associated with this property.
Defaults to ‘@<name>`
222 223 224 225 226 227 228 |
# File 'lib/chef/property.rb', line 222 def instance_variable_name if .key?(:instance_variable_name) [:instance_variable_name] elsif name :"@#{name}" end end |
#introduced ⇒ String
When this property was introduced
211 212 213 |
# File 'lib/chef/property.rb', line 211 def introduced [:introduced] end |
#is_set?(resource) ⇒ Boolean
Find out whether this property has been set.
This will be true if:
-
The user explicitly set the value
-
The property has a default, and the value was retrieved.
From this point of view, it is worth looking at this as “what does the user think this value should be.” In order words, if the user grabbed the value, even if it was a default, they probably based calculations on it. If they based calculations on it and the value changes, the rest of the world gets inconsistent.
482 483 484 |
# File 'lib/chef/property.rb', line 482 def is_set?(resource) value_is_set?(resource) end |
#name ⇒ String
The name of this property.
184 185 186 |
# File 'lib/chef/property.rb', line 184 def name [:name] end |
#name_property? ⇒ Boolean
Whether this is name_property or not.
290 291 292 |
# File 'lib/chef/property.rb', line 290 def name_property? [:name_property] end |
#required?(action = nil) ⇒ Boolean
Whether this property is required or not.
308 309 310 311 312 313 314 |
# File 'lib/chef/property.rb', line 308 def required?(action = nil) if !action.nil? && [:required].is_a?(Array) ([:required] & Array(action)).any? else !![:required] end end |
#reset(resource) ⇒ Object
Reset the value of this property so that is_set? will return false and the default will be returned in the future.
492 493 494 |
# File 'lib/chef/property.rb', line 492 def reset(resource) reset_value(resource) end |
#reset_value(resource) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
684 685 686 687 688 689 690 691 692 |
# File 'lib/chef/property.rb', line 684 def reset_value(resource) if instance_variable_name if value_is_set?(resource) resource.remove_instance_variable(instance_variable_name) end else raise ArgumentError, "Property #{name} has no instance variable defined and cannot be reset" end end |
#sensitive? ⇒ Boolean
Whether this property is sensitive or not.
Defaults to false.
334 335 336 |
# File 'lib/chef/property.rb', line 334 def sensitive? .fetch(:sensitive, false) end |
#set(resource, value) ⇒ Object
Set the value of this property in the given resource.
Non-lazy values are coerced and validated before being set. Coercion and validation of lazy values is delayed until they are first retrieved.
451 452 453 454 455 456 457 458 459 460 461 462 463 |
# File 'lib/chef/property.rb', line 451 def set(resource, value) value = set_value(resource, input_to_stored_value(resource, value)) if .key?(:deprecated) Chef.deprecated(:property, [:deprecated]) end if value.nil? && required?(resource_action(resource)) raise Chef::Exceptions::ValidationFailed, "#{name} is a required property" else value end end |
#set_value(resource, value) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
666 667 668 669 670 671 672 |
# File 'lib/chef/property.rb', line 666 def set_value(resource, value) if instance_variable_name resource.instance_variable_set(instance_variable_name, value) else resource.send(name, value) end end |
#skip_docs? ⇒ Boolean
Whether this property should be skipped for documentation purposes.
Defaults to false.
323 324 325 |
# File 'lib/chef/property.rb', line 323 def skip_docs? .fetch(:skip_docs, false) end |
#to_s ⇒ Object
175 176 177 |
# File 'lib/chef/property.rb', line 175 def to_s "#{name || "<property type>"}#{declared_in ? " of resource #{declared_in.resource_name}" : ""}" end |
#validate(resource, value) ⇒ Object
Validate a value.
Calls Chef::Mixin::ParamsValidate#validate with #validation_options as options.
536 537 538 539 540 541 542 543 544 545 546 |
# File 'lib/chef/property.rb', line 536 def validate(resource, value) # nils are not validated unless we have an explicit default value if !value.nil? || has_default? if resource resource.validate({ name => value }, { name => }) else name = self.name || :property_type Chef::Mixin::ParamsValidate.validate({ name => value }, { name => }) end end end |
#validation_options ⇒ Hash<Symbol,Object>
Validation options. (See Chef::Mixin::ParamsValidate#validate.)
343 344 345 346 347 |
# File 'lib/chef/property.rb', line 343 def @validation_options ||= .reject do |k, v| %i{declared_in name instance_variable_name desired_state identity default name_property coerce required nillable sensitive description introduced deprecated default_description skip_docs}.include?(k) end end |
#value_is_set?(resource) ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
675 676 677 678 679 680 681 |
# File 'lib/chef/property.rb', line 675 def value_is_set?(resource) if instance_variable_name resource.instance_variable_defined?(instance_variable_name) else true end end |