Module: EasyAttributes::ClassMethods

Defined in:
lib/easy_attributes.rb

Overview

EasyAttributes::ClassMethods - Module defining methods added to classes when included

Examples

class MyClass
  include EasyAttributes
  attr_values  :attr, name:value, ...
  attr_enum    :attr, :name, ....
  attr_define  :attr, :attr=>:defined_attr
  attr_bytes   :attr, ..., :base=>2
  attr_money   :attr, :precision=>2
  attr_fixed   :attr, :precision=>2

Instance Method Summary collapse

Instance Method Details

#attr_allowed(attribute, *args) ⇒ Object

Public: Defines an attribute as a set of values. For String-type attributes

attribute - symbolic name of the attribute args - a list of allowed string names (symbols can be used for convenience as well)

Examples

attr_allowed :type, "mammal", :bird, :insect

Creates the same methods as attr_values().

Returns nothing



756
757
758
759
760
761
762
# File 'lib/easy_attributes.rb', line 756

def attr_allowed(attribute, *args)
  opt = args.last.is_a?(Hash) ? args.pop : {}
  vals = {}
  args.flatten.map { |v| vals[v.to_sym] = v.to_s }
  defn = Definition.new(attribute, vals, opt)
  easy_attribute_accessors(attribute, defn)
end

#attr_bytes(*args) ⇒ Object

Public: Adds byte attributes helpers to the class attr_bytes allows manipultion and display as kb, mb, gb, tb, pb Adds method: attribute_bytes=() and attribute_bytes(:unit, :option=>value )

attribites - List of attribute names to generate helpers for options - Hash of byte helper options

Example

attr_bytes :bandwidth
attr_bytes :storage, :kb_size=>1000, :precision=>2

Adds the following helpers

bandwidth_bytes()         # => "10 GB"
bandwidth_bytes=("10 GB") # => 10_000_000_000


925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
# File 'lib/easy_attributes.rb', line 925

def attr_bytes(*args)
  define_easy_attribute_definition
  @easy_attribute_definitions ||= {}
  opt = args.last.is_a?(Hash) ? args.op : {}

  args.each do |attribute|
    attribute = attribute.to_sym
    unless EasyAttributes::Config.orm == :active_model || opt[:orm] == :active_model
      attr_accessor attribute if EasyAttributes::Config.orm == :attr
    end
    #defn = Definition.find_or_create(attribute, {}, opt)
    defn = Definition.new(attribute, {}, opt)
    @easy_attribute_definitions[attribute] = defn

    # <attribute>_bytes()
    # Returns the symbolic name of the current value of "attribute"
    define_method("#{attribute}_bytes") do |*bargs|
      self.class.easy_attribute_definition(attribute).format_bytes(self.send(attribute), *bargs)
    end

    # <attribute>_bytes=(new_symbol)
    # Sets the value of "attribute" to the associated value of the passed symbolic name
    define_method("#{attribute}_bytes=") do |sym|
      self.send("#{attribute}=", self.class.easy_attribute_definition(attribute).parse_bytes(sym))
    end
  end
end

#attr_define(attribute, *definition) ⇒ Object

Public: Sets global definitions into the class by attribute name or Hash mapping.

attributes - List of attributes to import from Config definitions mappings - Hash of atttribute names to a matching alternate name in the Config definitions

Examples (call like attr_values or attr_enum)

attr_define :status, :signup, :confirm, :unreachable, :delete
attr_define :status, signup:1, confirm:2, uncreachable:3, delete:4

Stores the definition in a symbol table for later attr_shared lookup



775
776
777
# File 'lib/easy_attributes.rb', line 775

def attr_define(attribute, *definition)
  find_or_create(attribute, *definition)
end

#attr_dollars(*args) ⇒ Object



1038
1039
1040
1041
# File 'lib/easy_attributes.rb', line 1038

def attr_dollars(*args)
  opt = args.last.is_a?(Hash) ? args.pop : {}
  attr_money(*args, opt.merge(method_suffix:'dollars'))
end

#attr_enum(attribute, *args) ⇒ Object

Public: Defines an attribute as an Enumeration of symbol name.

By default, the first symbol is mapped to 0 (zero), and increments by 1. The :start and :step values in the options hash can change those settings. Also, “#next()” is called on the start value, so any non-numeric object that supports that call can be used (such as a string). A nil in a position will skip that value, and any other non-symbol will reset the value of the next symbol to that one.

This is an alternate syntax for the attr_values() method

attribute - symbolic name of the attribute args - a list of symbol names, nil skip tokens, and value changes

* A symbol name will map to the current value
* A nil skips the current value in the list
* Any other value replaces the current value for the subsequent symbol

options - a optional key of :options=>name:value defines any options for the attribute

Examples

attr_enum :status, :active, :inactive             # => {active:0, inactive:1}
attr_enum :month,  :jan, :feb, ..., :dec, start:1 # => {jan:1, feb:2, ..., dec:12}
attr_enum :status, :active, :inactive, nil, :suspended, 99, :deleted, start:10, step:10
  # => same as: {active:10, inactive:20, suspended:40, deleted:99}

Creates the same methods as attr_values().

Returns nothing



738
739
740
741
742
# File 'lib/easy_attributes.rb', line 738

def attr_enum(attribute, *args)
  opt = args.last.is_a?(Hash) ? args.pop : {}
  defn = Definition.new(attribute, args, opt)
  easy_attribute_accessors(attribute, defn)
end

#attr_fixed(*args) ⇒ Object

Public: Adds methods to get and set a fixed-point value stored as an integer.

attributes - list of attribute names to define options - Optional hash of definitions for the given list of attributes

:method_suffix - Use this as the alternative name to the *_fixed method names
:units - Use this as an alternative suffix name to the money methods ('dollars' gives 'xx_dollars')
:precision - The number of digits implied after the decimal, default is 2
:separator - The character to use after the integer part, default is '.'
:delimiter - The character to use between every 3 digits of the integer part, default none
:positive - The sprintf format to use for positive numbers, default is based on precision
:negative - The sprintf format to use for negative numbers, default is same as :positive
:zero - The sprintf format to use for zero, default is same as :positive
:nil - The sprintf format to use for nil values, default none
:unit - Prepend this to the front of the money value, say '$', default none
:blank - Return this value when the money string is empty or has no digits on assignment
:negative_regex - A Regular Expression used to determine if a number is negative (and without a - sign)

Examples:

attr_fixed :gpa, precision:1
attr_fixed :price, precision:2

Adds the following helpers

gpa_fixed()               #=> "3.8"
gpa_fixed=("3.8")         #=> 38
gpa_float()               #=> 3.8

Returns nothing



981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
# File 'lib/easy_attributes.rb', line 981

def attr_fixed(*args)
  define_easy_attribute_definition
  @easy_attribute_definitions ||= {}
  opt = args.last.is_a?(Hash) ? args.pop : {}
  suffix = opt.fetch(:method_suffix) { 'fixed' }

  args.each do |attribute|
    attribute = attribute.to_sym
    unless EasyAttributes::Config.orm == :active_model || opt[:orm] == :active_model
      attr_accessor attribute if EasyAttributes::Config.orm == :attr
    end
    defn = Definition.new(attribute, {}, opt)
    @easy_attribute_definitions[attribute] = defn

    # <attribute>_fixed()
    # Returns the symbolic name of the current value of "attribute"
    define_method("#{attribute}_#{suffix}") do
      FixedPoint::integer_to_fixed_point(send(attribute), self.class.easy_attribute_definition(attribute).attr_options)
    end

    # <attribute>_fixed=(new_value)
    # Sets the value of "attribute" to the associated value of the passed symbolic name
    define_method("#{attribute}_#{suffix}=") do |val|
      self.send("#{attribute}=", FixedPoint::fixed_point_to_integer(val,
        self.class.easy_attribute_definition(attribute).attr_options))
    end

    # <attribute>_float()
    # Returns the value of the attribite as a float with desired precision point
    define_method("#{attribute}_float") do
      FixedPoint::integer_to_float(send(attribute), self.class.easy_attribute_definition(attribute).attr_options)
    end
  end
end

#attr_money(*args) ⇒ Object

Public: Alias of attr_fixed for a money type with suffix of ‘money’ and precision of 2.

args - list of money attributes options - hash of attr_fixed options.

Examples:

attr_money :price
attr_money :wager, method_suffix:'quatloos', precision:1, unit:'QL'

Adds the following helpers

price_money()               #=> "42.00"
price_money=("3.8")         #=> 380
price_float()               #=> 3.8


1031
1032
1033
1034
1035
1036
# File 'lib/easy_attributes.rb', line 1031

def attr_money(*args)
  opt = args.last.is_a?(Hash) ? args.pop : {}
  opt = {method_suffix:'money', precision:2}.merge(opt)
  args << opt
  attr_fixed(*args)
end

#attr_shared(*attributes) ⇒ Object

Public: Imports previously defined definitions into the class by attribute name or Hash mapping.

attributes - List of attributes to import from Config definitions mappings - Hash of atttribute names to a matching alternate name in the Config definitions

Examples

attr_shared :status, :role, widget_type: :general_type, colname: :sharedname
attr_shared employee_status:Employee.status_definition()

Calls attr_values with each definition



790
791
792
793
794
# File 'lib/easy_attributes.rb', line 790

def attr_shared(*attributes)
  mapping = attributes.last.is_a?(Hash) ? attributes.pop : {}
  attributes.each { |a| install_shared_attribute(a) }
  mapping.each { |a, shared| install_shared_attribute(a, shared) }
end

#attr_values(attribute, *args) ⇒ Object

Public: Defines an attribute with a Hash of symbolic synonyms for the values.

attribute - symbolic name of the attribute values - a hash of .… mappings

a optional key of :options=>{name:value} defines any options for the attribute

Examples

attr_values :status, active:1, inactive:2

Creates these instance methods (for a “status” attribute):

status_sym()                # Returns the symbolic name instead of value
status_sym=(:inactive)      # Used for setting the attrivute by symbolic name instead
status_in(symbol, ...)     # Returns true if the attribute symbol is in the list of symbols
status_cmp(symbol)          # Returns the comparison of the value <=> symbol

And these class methods:

status_definition()
  Returns the EasyAttributes::Definition for the attribute, on which you can call cool things like
  value_of(), symbol_of(), select_options(), etc.

Returns nothing



705
706
707
708
709
# File 'lib/easy_attributes.rb', line 705

def attr_values(attribute, *args)
  #defn = Definition.find_or_create(attribute, *args)
  defn = Definition.new(attribute, *args)
  easy_attribute_accessors(attribute, defn)
end

#define_easy_attribute_definitionObject

Adds once to class: Returns the EasyAttribute::Definition of the passed



900
901
902
903
904
905
906
# File 'lib/easy_attributes.rb', line 900

def define_easy_attribute_definition
  unless self.respond_to?(:easy_attribute_definition)
    define_singleton_method(:easy_attribute_definition) do |attrib|
      @easy_attribute_definitions.fetch(attrib.to_sym) { raise "EasyAttribute #{attrib} not found" }
    end
  end
end

#easy_attribute_accessors(attribute, defn) ⇒ Object

Private: Creates attribute accessors for the attribute /definition for attr_values

Creates these methods dynamically on the host class

self.<attribute>_definition()
<attribute>_sym()
<attribute>_sym=()
<attribute>_in()
<attribute>_cmp()

If Config.constantize, create ATTRIBUTE_SYMBOL=value constants



813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
# File 'lib/easy_attributes.rb', line 813

def easy_attribute_accessors(attribute, defn)
  attribute = attribute.to_sym
  @easy_attribute_definitions ||= {}
  @easy_attribute_definitions[attribute] = defn
  opt = defn.options
  code = ''

  if (EasyAttributes::Config.orm == :active_model || opt[:orm] == :active_model) &&
      self.respond_to?(:validates_inclusion_of)
    self.validates_inclusion_of attribute, :in=>defn.symbols.values
    # Add named_scope (scope) for each value
    if opt[:named_scope]
      defn.symbols.each { |k,v| code += "named_scope :#{k}, :conditions=>{:#{attribute}=>#{v.inspect}}\n" }
    end
    if opt[:scope]
      defn.symbols.each { |k,v| code += "scope :#{k}, where({:#{attribute}=>#{v.inspect}})\n" }
    end
  else
    attr_accessor attribute
  end

  #------------------------------------------------------------------------
  # Class Methods
  #------------------------------------------------------------------------

  define_easy_attribute_definition

  # <attribute>_options() Returns an array of (HTML Select) option pairs
  # => [["Option Name", :symbol], ...]
  self.define_singleton_method("#{attribute}_options") do |*args|
    easy_attribute_definition(attribute).select_option_symbols(*args)
  end

  # <attribute>_of(:sym [,:default_sym]) Returns the symbol/value hash for the attribute
  self.define_singleton_method("#{attribute}_of") do |*args, &block|
    easy_attribute_definition(attribute).symbols.fetch(args.first.to_sym) do |sym| 
      if args.size>1
        easy_attribute_definition(attribute).symbols.fetch(args[1].to_sym, &block)
      else
        raise "#{attribute} symbolic name #{sym} not found" 
      end
    end
  end

  # Define Constants Model::ATTRIBUTE_SYMBOL = value
  if Config::constantize
    easy_attribute_definition(attribute).symbols.each do |sym, value|
      const_set("#{attribute.upcase}_#{sym.to_s.upcase}", value)
    end
  end

  #------------------------------------------------------------------------
  # Instance Methods
  #------------------------------------------------------------------------

  # <attribute>_definition()
  # Returns the definition ojbect for the easy attribute
  define_method("#{attribute}_definition") do
    self.class.easy_attribute_definition(attribute)
  end

  # <attribute>_sym()
  # Returns the symbolic name of the current value of "attribute"
  define_method("#{attribute}_sym") do
    self.class.easy_attribute_definition(attribute).symbol_of(self.send(attribute))
  end

  # <attribute>_sym=(new_symbol)
  # Sets the value of "attribute" to the associated value of the passed symbolic name
  define_method("#{attribute}_sym=") do |sym|
    self.send("#{attribute}=", self.class.easy_attribute_definition(attribute).value_of(sym))
  end

  # <attribute>_in(*names)
  # Returns true if the symbolic name of the current value of "attribute" is in the list of names.
  define_method("#{attribute}_in") do |*args|
    self.class.easy_attribute_definition(attribute).value_in(self.send(attribute),*args)
  end

  # <attribute>_cmp(other)
  # Standard "cmp" or <=> compare for "attribute" against a symbolic name.
  define_method("#{attribute}_cmp") do |other|
    self.class.easy_attribute_definition(attribute).cmp(self.send(attribute),other)
  end
end

#install_shared_attribute(name, shared_name = nil) ⇒ Object



796
797
798
799
800
# File 'lib/easy_attributes.rb', line 796

def install_shared_attribute(name, shared_name=nil)
  shared_name ||= name
  defn = Definition.find_or_create(shared_name)
  easy_attribute_accessors(name, defn)
end