Module: Agio::Flags::ClassMethods

Defined in:
lib/agio/flags.rb

Overview

Meta-programming methods to help create flags and accessor and test methods for those flags.

Instance Method Summary collapse

Instance Method Details

#array_flag(name, options = {}) ⇒ Object

Defines a flag optimized for working with arrays.



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/agio/flags.rb', line 256

def array_flag(name, options = {})
  options = { :default => lambda { [] } }.merge(options)
  options = options.merge(:type => :array)
  flag_builder(name, options) do |type, meth, ivar|
    case type
    when :setter
      define_method(meth) do |value|
        value = [ value ] unless value.nil? or value.kind_of? Array
        instance_variable_set(ivar, value)
      end
    when :tester
      define_method(meth) do
        value = instance_variable_get(ivar)
        !(value.nil? or value.empty?)
      end
    end
  end
end

#boolean_flag(name, options = {}) ⇒ Object

Defines a flag optimized for working with Boolean (true or false) values.



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/agio/flags.rb', line 203

def boolean_flag(name, options = {})
  options = { :default => false }.merge(options).merge(:type => :boolean)
  flag_builder(name, options) do |type, meth, ivar|
    case type
    when :setter
      define_method(meth) do |value|
        instance_variable_set(ivar, !!value)
      end
    when :tester
      define_method(meth) do
        !!instance_variable_get(ivar)
      end
    end
  end
end

#flag_builder(name, options = {}) ⇒ Object

Creates a flag. This method is the core method for building flags. Flags must have a name and may specify one of two options, :default or :public.

flag_builder returns a Hash describing the flag.

Options

:default

The default value for the flag. If not specified, or not overridden by one of the type helper methods, the default will be nil. If the default is not an immediate value, the default should be specified in a Proc, as is done for Array flags: lambda { [] }.

:public

If true, indicates that the flag is internal and should not be exposed to the user. The default is that flags are private (e.g., :public => false).

Methods Defined

Four methods are always defined by flag_builder or a type helper method. The type helper methods mentioned below may override the default behaviours described below.

Init

“init_name”. This is always private, and should only be called through Flags#reset_flags. Sets the value of the flag to the default value. If the default value returns to #call, it will be called to provide the default value. Uses the flag’s Setter.

Getter

“name”. Returns the value of the flag.

Setter

“set_name” if private or “name=” if public. Sets the flag to the provided value.

Tester

name?” Returns true or false for the value with double negation (e.g., !!value).

When calling flag_builder from a helper method, you can provide a block that will allow the customization of the Setter or the Tester. The other methods cannot be overridden.

Type Helper Methods

There are five type helpers defined:

string_flag

Creates a flag that works with String values. The default value is nil, unless otherwise specified. If a non-nil value is provided, the default value will be wrapped in a Proc that will create a new String object for every reset. The Setter converts all values (except nil) to a String using #to_s. The Tester will return false if the value is nil or the String is empty.

boolean_flag

Creates a flag that works with Boolean values (true or false). The default value for a boolean_flag is false, unless otherwise specified. The Setter converts all values to true or false through double negation (e.g., !!value). The Tester forces the instance variable to true or false through double negation.

integer_flag

Creates a flag that works with integer values. The default value for an integer_flag is zero, unless otherwise specified. The Setter converts all values to integer with #to_i. The Tester returns true if the value is non-zero. Private integer flags also define two additional methods, incr_name and decr_name, that will increment or decrement the integer value by the value provided.

hash_flag

Creates a flag that works with Hash values. The default value is a lambda that creates an empty Hash. The Tester returns false if the value is nil or the Hash is empty.

array_flag

Creates a flag that works with Array values. The default value is a lambda that creates an empty Array. The Tester returns false if the value is nil or the Array is empty.

Raises:

  • (SyntaxError)


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
# File 'lib/agio/flags.rb', line 105

def flag_builder(name, options = {})
  raise SyntaxError, "Flag #{name} already defined" if flags.has_key? name.to_sym

  default     = options[:default]
  type        = options[:type]
  is_public   = options[:public]

  flag = {
    :ivar     => "@flag_#{name}",
    :init     => "init_#{name}".to_sym,
    :getter   => name.to_sym,
    :setter   => (is_public ? "#{name}=" : "set_#{name}").to_sym,
    :tester   => "#{name}?".to_sym,
    :public   => is_public,
    :type     => type,
    :default  => default,
  }

  # Define the flag initializer
  define_method(flag[:init]) do
    value = if default.respond_to? :call
              default.call
            else
              default
            end
    __send__(flag[:setter], value)
  end
  private flag[:init]

  if is_public
    public_flag_inits << flag[:init]
  else
    flag_inits << flag[:init]
  end

  # Define the flag getter
  define_method(flag[:getter]) do
    instance_variable_get(flag[:ivar])
  end
  private flag[:getter] unless is_public

  # Define the flag setter
  defined = yield :setter, flag[:setter], flag[:ivar] if block_given?

  unless defined
    define_method(flag[:setter]) do |value|
      instance_variable_set(flag[:ivar], value)
    end
  end
  private flag[:setter] unless is_public

  # Define the flag tester
  defined = yield :tester, flag[:tester], flag[:ivar] if block_given?

  unless defined
    define_method(flag[:tester]) do
      !!instance_variable_get(ivar)
    end
  end
  private flag[:tester] unless is_public

  flags[name.to_sym] = flag
end

#flag_initsObject

An array of initializer method symbols created so that Flags#reset_flags can reset flags to their default values.



299
300
301
# File 'lib/agio/flags.rb', line 299

def flag_inits
  @flag_inits ||= []
end

#flagsObject

The flags that have been defined.



312
313
314
# File 'lib/agio/flags.rb', line 312

def flags
  @flags ||= {}
end

#hash_flag(name, options = {}) ⇒ Object

Defines a flag optimized for working with hashes.



277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/agio/flags.rb', line 277

def hash_flag(name, options = {})
  options = { :default => lambda { {} } }.merge(options)
  options = options.merge(:type => :hash)
  flag_builder(name, options) do |type, meth, ivar|
    case type
    when :setter
      define_method(meth) do |value|
        raise ArgumentError unless value.nil? or value.kind_of? Hash
        instance_variable_set(ivar, value)
      end
    when :tester
      define_method(meth) do
        value = instance_variable_get(ivar)
        !(value.nil? or value.empty?)
      end
    end
  end
end

#integer_flag(name, options = {}) ⇒ Object

Defines a flag optimized for working with integer values.



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/agio/flags.rb', line 221

def integer_flag(name, options = {})
  options = { :default => 0 }.merge(options).merge(:type => :integer)

  flag = flag_builder(name, options) do |type, meth, ivar|
    case type
    when :setter
      define_method(meth) do |value|
        instance_variable_set(ivar, value.to_i)
      end
    when :tester
      define_method(meth) do
        instance_variable_get(ivar).nonzero?
      end
    end
  end

  unless flag[:public]
    incr = "incr_#{name}".to_sym
    define_method(incr) do |value|
      value = instance_variable_get(flag[:ivar]) + value.to_i
      instance_variable_set(flag[:ivar], value)
    end
    private incr

    decr = "decr_#{name}".to_sym
    define_method(decr) do |value|
      value = instance_variable_get(flag[:ivar]) - value.to_i
      instance_variable_set(flag[:ivar], value)
    end
    private decr
  end
end

#public_flag_initsObject

An array of initializer method symbols created so that Flags#reset_flags does its work appropriately for public flags.



306
307
308
# File 'lib/agio/flags.rb', line 306

def public_flag_inits
  @public_flag_inits ||= []
end

#string_flag(name, options = {}) ⇒ Object

Defines a flag optimized for working with strings.



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
# File 'lib/agio/flags.rb', line 171

def string_flag(name, options = {})
  options = { :default => nil }.merge(options).merge(:type => :string)

  value = options[:default]
  options[:default] = case value
                      when nil
                        nil
                      when String
                        lambda { value.dup }
                      else
                        lambda { value.to_s }
                      end

  flag_builder(name, options) do |type, meth, ivar|
    case type
    when :setter
      define_method(meth) do |value|
        value = value.to_s unless value.nil?
        instance_variable_set(ivar, value)
      end
    when :tester
      define_method(meth) do
        value = instance_variable_get(ivar)
        !(value.nil? or value.empty?)
      end
    end
  end
end