Permalink
Browse files

There we go

  • Loading branch information...
1 parent d9e1f6f commit 3af639a2afafefb8a861d5076274c5e612612391 @seejohnrun seejohnrun committed Oct 13, 2012
View
@@ -1,4 +1,29 @@
-argh
-====
+# argh
-Easy command-line argument building in Ruby
+It can be a huge pain to build arguments for an external command.
+There's all of those annoying escaping issues, and you just shouldn't
+have to deal with that crap.
+
+Argh makes it easy to define how to serialize some attributes. Just
+include `Argh::Attributable` into any Ruby class, and you're off to
+the races.
+
+``` ruby
+class Thing
+
+ include Argh::Attributable
+
+ attr_reader :name
+
+ argh 'command_attributes' do
+ attribute(:name)
+ attribute(:reverse_name) { name.reverse }
+ attribute(:repeat_name, :from => :name)
+ end
+
+ def initialize(name)
+ @name = name
+ end
+
+end
+```
View
@@ -0,0 +1,9 @@
+module Argh
+
+ base = File.dirname(File.expand_path(__FILE__))
+
+ autoload :Attributable, base + '/argh/attributable'
+ autoload :Collector, base + '/argh/collector'
+ autoload :Processors, base + '/argh/processors'
+
+end
@@ -0,0 +1,26 @@
+module Argh
+ module Attributable
+
+ DEFAULT_PROCESSOR = Processors::JCommanderProcessor
+
+ def self.included(base)
+ base.extend(ClassMethods)
+ end
+
+ module ClassMethods
+
+ def argh(name, processor_klass = DEFAULT_PROCESSOR, &block)
+ collector = Collector.new(&block)
+ mod = Module.new
+ mod.send(:define_method, name) do
+ processor = processor_klass.new collector, self
+ processor.process
+ end
+ # Extend this module onto ourselves
+ self.send(:include, mod)
+ end
+
+ end
+
+ end
+end
View
@@ -0,0 +1,21 @@
+module Argh
+
+ class Collector
+
+ attr_reader :attributes
+
+ def initialize(&block)
+ @attributes = []
+ instance_eval(&block)
+ end
+
+ def attribute(name, &block)
+ @attributes << {
+ :name => name,
+ :lambda => block || lambda { |i| i.send(name) }
+ }
+ end
+
+ end
+
+end
@@ -0,0 +1,10 @@
+module Argh
+ module Processors
+
+ base = File.dirname(File.expand_path(__FILE__))
+
+ autoload :JCommanderProcessor, base + '/processors/jcommander_processor'
+ autoload :BaseProcessor, base + '/processors/base_processor'
+
+ end
+end
@@ -0,0 +1,26 @@
+module Argh
+ module Processors
+
+ class BaseProcessor
+
+ def initialize(collector, context)
+ @collector = collector
+ @context = context
+ end
+
+ def attributes(&block)
+ return enum_for(:attributes) unless block_given?
+ @collector.attributes.each do |attr|
+ value = @context.instance_eval(&attr[:lambda])
+ yield attr[:name], value
+ end
+ end
+
+ def process
+ raise 'BaseProcessor should be subclassed'
+ end
+
+ end
+
+ end
+end
@@ -0,0 +1,29 @@
+module Argh
+ module Processors
+
+ class JCommanderProcessor < BaseProcessor
+
+ def process
+ information = []
+ attributes.each do |name, value|
+ piece = escape(name, value)
+ information << piece unless piece.nil?
+ end
+ information.join ' '
+ end
+
+ private
+
+ def escape(name, value)
+ case value
+ when NilClass, FalseClass then nil
+ when TrueClass then "-#{name}"
+ when Fixnum then "-#{name} #{value}"
+ else "-#{name} '#{value.to_s.gsub(%q{'}) { %q{'\''} }}'"
+ end
+ end
+
+ end
+
+ end
+end
@@ -0,0 +1,64 @@
+require_relative '../spec_helper'
+
+# To facilitate example below
+class BasicExample
+
+ include Argh::Attributable
+
+ argh 'single' do
+ attribute :name
+ end
+
+ argh 'multiple' do
+ attribute :name
+ attribute :age
+ end
+
+ argh 'multiple_reverse' do
+ attribute :age
+ attribute :name
+ end
+
+ argh 'lambs' do
+ attribute(:name) { name.reverse }
+ end
+
+ private
+
+ def name
+ 'John Crepezzi'
+ end
+
+ def age
+ 26
+ end
+
+end
+
+##########
+
+describe Argh::Attributable do
+
+ let(:example) { BasicExample.new }
+
+ it 'better not define #attribute on example instance' do
+ example.should_not respond_to(:attribute)
+ end
+
+ it 'should get the value of existing properties' do
+ example.single.should == '-name \'John Crepezzi\''
+ end
+
+ it 'should be able to serialize multiple attributes' do
+ example.multiple.should == '-name \'John Crepezzi\' -age 26'
+ end
+
+ it 'should be always bring back attributes in order' do
+ example.multiple_reverse.should == '-age 26 -name \'John Crepezzi\''
+ end
+
+ it 'should be able to specify a lambda instead of the original' do
+ example.lambs.should == '-name \'izzeperC nhoJ\''
+ end
+
+end
@@ -0,0 +1,87 @@
+require_relative '../spec_helper'
+
+# To facilitate examples
+class JCommanderExample
+ attr_accessor :attr
+ include Argh::Attributable
+ argh 'console_attributes', Argh::Processors::JCommanderProcessor do
+ attribute :attr
+ end
+end
+
+##########
+
+describe Argh::Processors::JCommanderProcessor do
+
+ let(:attr) { nil }
+ let(:result) do
+ example = JCommanderExample.new
+ example.attr = attr
+ example.console_attributes
+ end
+
+ describe 'with a fixnum attribute' do
+
+ let(:attr) { 123 }
+
+ it 'should return the arguments properly' do
+ result.should == '-attr 123'
+ end
+
+ end
+
+ describe 'with a true attribute' do
+
+ let(:attr) { true }
+
+ it 'should return only the name of the attribute' do
+ result.should == '-attr'
+ end
+
+ end
+
+ describe 'with a false attribute' do
+
+ let(:attr) { false }
+
+ it 'should return an empty string' do
+ result.should == ''
+ end
+
+ end
+
+ describe 'with a nil attribute' do
+
+ let(:attr) { nil }
+
+ it 'should return an empty string' do
+ result.should == ''
+ end
+
+ end
+
+ describe 'with a string attribute' do
+
+ describe 'a clear string' do
+
+ let(:attr) { 'John Crepezzi' }
+
+ it 'should return the arguments properly' do
+ result.should == %q{-attr 'John Crepezzi'}
+ end
+
+ end
+
+ describe 'a string including single quotes' do
+
+ let(:attr) { 'John \' Crepezzi' }
+
+ it 'should return the arguments properly escaped' do
+ result.should == %q{-attr 'John '\'' Crepezzi'}
+ end
+
+ end
+
+ end
+
+end
View
@@ -0,0 +1 @@
+require_relative '../lib/argh'

0 comments on commit 3af639a

Please sign in to comment.