ArrayFu

One of the first ruby projects I. It is a simple dsl for declaritive arrays. Hopefully the examples below show how it can be used!

Examples

example "Basic" do
  class Example1
    include ArrayFu

    array :names
  end
end

example 'Allow the array to have a read accessor' do
  class Example2
    include ArrayFu

    array(:names) { readable }
  end
  Example2.new.names.should_not be_nil
end

example 'Allow the array to have a write accessor' do
  class Example3
    include ArrayFu

    array(:names) { writeable }
  end
  instance = Example3.new
  new_names = []
  instance.names = new_names
  instance.instance_eval do
    @names.should == new_names
  end
end

example 'Allow the array to have a read and write accessor' do
  class Example4
    include ArrayFu

    array(:names) { read_and_write }
  end
  Example4.new.names.should_not be_nil
end

example 'Add a mutator method to the class that stores the array' do
  class Example5
    include ArrayFu

    array(:names) { mutator :add_item }
  end

  instance = Example5.new
  instance.add_item("JP")
  instance.instance_eval do
    @names.count.should == 1
  end
end

example 'Add multiple mutators to the class that stores the array' do
  class Example6
    include ArrayFu

    array :names do
      mutator :add_item, :add_it, :push_it
    end
  end

  instance = Example6.new
  instance.add_item("JP")
  instance.add_it("JP")
  instance.push_it("JP")
  instance.instance_eval do
    @names.count.should == 3
  end
end

example 'Add a mutator that ignores addition' do
  class Example7
    include ArrayFu

    array :names do
      mutator :add_item do|item|
      end
    end
  end

  instance = Example7.new
  instance.add_item("JP")
  instance.instance_eval do
    @names.count.should == 0
  end
end

example 'Add a mutator that does other custom logic as well as addition' do
  class Example8
    include ArrayFu

    array(:secondary) { readable }

    array :names do
      readable
      mutator :add_item do|item|
        @secondary.push item
        @names.push item
      end
    end
  end

  instance = Example8.new
  instance.add_item("JP")
  instance.names.count.should == 1
  instance.secondary.count.should == 1
end

example 'Add a singular constraint and failure condition to each of the mutators' do
  module NotBeJP
    extend self

    def matches?(item)
      return item != "JP"
    end

    def name
      return "The value should not be JP"
    end
  end

  module CriteriaViolation
    extend self

    def run(description,value)

    end
  end

  class Example9
    include ArrayFu

    array :names do
      readable
      mutator :add_item,:add_it
      new_item_must NotBeJP, CriteriaViolation
    end
  end

  instance = Example9.new
  instance.add_item("JP")
  instance.add_it("JP")
  instance.names.count.should == 0
end

example 'Add multiple constraints and a failure condition to each of the mutators' do
  module NotBeJP
    extend self

    def matches?(item)
      return item != "JP"
    end

    def name
      return "The value should not be JP"
    end
  end

  module NotBeNil
    extend self

    def matches?(item)
      return item != nil
    end

    def name
      return "The value should not be nil"
    end
  end

  module CriteriaViolation
    extend self

    def run(description,value)
      # puts "Criteria violated - #{description} - #{value}"
    end
  end

  class Example10
    include ArrayFu

    array :names do
      readable
      mutator :add_item,:add_it
      addition_constraint NotBeJP
      addition_constraint NotBeNil, CriteriaViolation
    end
  end

  instance = Example10.new
  instance.add_item("JP")
  instance.add_it("JP")
  instance.add_item(nil)
  instance.names.count.should == 0
end

example 'Add an explicit processing visitor to the array' do
  class DisplayItem
    @@number_of_items_displayed = 0
    class << self
      def run_using(item)
        @@number_of_items_displayed += 1
      end
      def item_count
        return @@number_of_items_displayed
      end
    end
  end

  class Example11
    include ArrayFu

    array :names do
      mutator :add_item
      process_using :display_all,DisplayItem
    end
  end

  instance = Example11.new
  (1..10).each{|item| instance.add_item(item)}
  instance.display_all
  DisplayItem.item_count.should == 10
end

example 'Add an method based processing visitor to the array based on a method that exists on the items in the array' do
  class Item
    def process
      Example12.increment
    end
  end

  class Example12
    @@items_visited = 0
    include ArrayFu

    array :names do
      mutator :add_item
      process_using :display_all, :process #the second symbol is the name of a method on an element in the array
    end


    #the process method of the Item class invokes this method (a little bit roundabout, but it hopefully demonstrates the capability
    def self.increment
      @@items_visited += 1
    end
    def self.number_of_items_visited
      @@items_visited
    end
  end

  instance = Example12.new
  (1..10).each{|item| instance.add_item(Item.new)}
  instance.display_all
  Example12.number_of_items_visited.should == 10
end

example 'Augment configuration using configuration block' do
  class ArrayConfigs
    def self.add_another_mutator
      return lambda{|item| item.mutator :another_push}
    end
  end

  class Example13
    include ArrayFu

    array :names do
      readable
      mutator :add_item
      configure_using ArrayConfigs.add_another_mutator
    end
  end

  instance = Example13.new
  instance.add_item("Yo")
  instance.another_push("Yo")
  instance.names.count.should == 2
end

example 'Augment configuration using inline configuration block' do
  class Example14
    include ArrayFu

    array :names do
      readable
      mutator :add_item
      configure_using -> (item) do
        item.mutator :another_pushes
      end
    end
  end

  instance = Example14.new
  instance.add_item("Yo")
  instance.another_pushes("Yo")
  instance.names.count.should == 2
end

example 'Augment configuration using configuration instance (anything that responds to configure with the array definition as the argument)' do

  module ArrayConfiguration
    extend self

    def configure(item)
      item.mutator :once_more
    end
  end

  class Example15
    include ArrayFu

    array :names do
      readable
      mutator :add_item
      configure_using ArrayConfiguration
    end
  end

  instance = Example15.new
  instance.add_item("Yo")
  instance.once_more("Yo")
  instance.names.count.should == 2
end

example 'Augment configuration using configuration block' do

  module ArrayConfiguration
    extend self

    def configuration_block
      Proc.new do|array|
        array.mutator :once_more
      end
    end
  end

  class Example16
    include ArrayFu

    array :names do
      readable
      mutator :add_item
      configure_using ArrayConfiguration.configuration_block
    end
  end

  instance = Example16.new
  instance.add_item("Yo")
  instance.once_more("Yo")
  instance.names.count.should == 2
end

example 'Augment configuration of an existing array' do

  module ExampleConfig1
    extend self

    def configuration_block
      -> (array) { array.mutator :once_more }
    end
  end

  class Example17
    include ArrayFu

    array :names do
      readable
      mutator :add_item
    end

    def initialize
      array :names do
        configure_using ExampleConfig1.configuration_block
      end
      super
    end
  end

  instance = Example17.new
  instance.add_item("Yo")
  instance.once_more("Yo")
  instance.names.count.should == 2
end

example 'Alternate way to augment configuration of an existing array' do

  module ExampleConfig3
    extend self

    def configure(array_definition)
      array_definition.mutator :once_more
    end
  end

  class Example18
    include ArrayFu

    array :names do
      readable
      mutator :add_item
    end

    def initialize(config)
      config.configure(array(:names))
      super
    end
  end

  instance = Example18.new(ExampleConfig3)
  instance.add_item("Yo")
  instance.once_more("Yo")
  instance.names.count.should == 2
end