ruby/trema/accessor.rb
#
# Copyright (C) 2008-2013 NEC Corporation
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2, as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
require_relative "mapping"
module Trema
#
# A base class for defining user defined like accessors.
#
class Accessor
# mixin mapping of ofp type to implementation class.
include Mapping
USER_DEFINED_TYPES = %w( ip_addr mac match packet_info array string bool )
class AttributeProperty
attr_reader :required_attributes, :default_attributes, :alias_attributes
def initialize
@required_attributes ||= []
@default_attributes ||= {}
@alias_attributes ||= {}
end
end
class << self
def attributes
@attributes ||= AttributeProperty.new
end
def search type, sub_key
key = "#{ type }_#{ sub_key }"
retrieve key
end
def inherited klass
map_ofp_type klass
primitive_sizes.each do | each |
define_accessor_meth :"unsigned_int#{ each }"
define_method :"check_unsigned_int#{ each }" do | number, name |
begin
unless number.send( "unsigned_#{ each }bit?" )
raise ArgumentError, "#{ name } must be an unsigned #{ each }-bit integer."
end
rescue NoMethodError
raise TypeError, "#{ name } must be a Number."
end
end
end
USER_DEFINED_TYPES.each{ | meth | define_accessor_meth meth }
end
############################################################################
private
############################################################################
def primitive_sizes
( 8..64 ).step( 8 ).select{ | i | i.to_s( 2 ).count( '1' ) == 1 }
end
def define_accessor_meth meth
self.class.class_eval do
define_method :"#{ meth }" do | *args |
attrs = args
opts = extract_options!( args )
check_args args
attrs.delete( opts ) unless opts.empty?
opts.store :attributes, attrs
opts.store :validate_with, "check_#{ meth }" if meth.to_s[ /unsigned_int\d\d/ ]
attrs.each do | attr_name |
define_accessor attr_name, opts
if opts.has_key? :alias
alias_method opts[ :alias ], attr_name
self.attributes.alias_attributes[ attr_name ] = opts[ :alias ] if opts.has_key? :alias
end
self.attributes.required_attributes << attr_name if opts.has_key? :presence
self.attributes.default_attributes[ attr_name ] = opts[ :default ] if opts.has_key? :default
end
end
end
end
def define_accessor attr_name, opts
self.class_eval do
define_method attr_name do
instance_variable_get "@#{ attr_name }"
end
define_method "#{ attr_name }=" do | v |
if opts.has_key? :presence
if opts[ :presence ] == true
if v.nil?
raise ArgumentError, "#{ attr_name } is a mandatory option."
end
end
end
validation_methods = opts.select { | key, _ | key == :within or key == :validate_with }
validation_methods.each { | _, meth | __send__( meth, v, attr_name ) }
instance_variable_set "@#{ attr_name }", v
end
end
end
def extract_options! args
if args.last.is_a?( Hash ) && args.last.instance_of?( Hash )
args.pop
else
{}
end
end
def check_args args
raise ArgumentError, "You need at least one attribute" if args.empty?
end
end
def initialize options=nil
setters = self.class.instance_methods.select{ | i | i.to_s =~ /[a-z].+=$/ }
required_attributes = self.class.attributes.required_attributes
if required_attributes.empty?
required_attributes = self.class.superclass.attributes.required_attributes
end
set_default setters
case options
when Hash
setters.each do | each |
opt_key = each.to_s.sub( '=', '' ).to_sym
if options.has_key? opt_key
public_send each, options[ opt_key ]
elsif options.has_key? self.class.attributes.alias_attributes[ opt_key ]
public_send each, options[ self.class.attributes.alias_attributes[ opt_key ] ]
else
raise ArgumentError, "Required option #{ opt_key } is missing for #{ self.class.name }" if required_attributes.include? opt_key
end
end
when Integer, String
unless setters.empty?
public_send setters[ 0 ], options
else
raise ArgumentError, "#{ self.class.name } accepts no options"
end
else
raise ArgumentError, "Required option #{ required_attributes.first } missing for #{ self.class.name }" unless required_attributes.empty?
end
end
def set_default setters
default_attributes = self.class.attributes.default_attributes
setters.each do | each |
opt_key = each.to_s.sub( '=', '' ).to_sym
if default_attributes.has_key? opt_key
if default_attributes[ opt_key ].respond_to? :call
public_send each, default_attributes[ opt_key ].call
else
public_send each, default_attributes[ opt_key ]
end
end
end
end
end
end
### Local variables:
### mode: Ruby
### coding: utf-8-unix
### indent-tabs-mode: nil
### End: