alias 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,25 @@
1
+ # Creates instance methods which call class methods. These are delegations rather than aliases.
2
+ # Expects a hash of classes/modules of the instance method mapped
3
+ # to a hash of the class methods and the instance method names.
4
+ # For example, the hash {"MyDate"=>{'Date.today'=>'t'}} would create a MyDate.t instance method
5
+ # which directly calls Date.today.
6
+ class Alias::Creators::ClassToInstanceMethodCreator < Alias::Creator
7
+ map do |config|
8
+ config.inject([]) {|t,(klass,hash)|
9
+ t += hash.map {|k,v|
10
+ {:class=>klass, :to_class=>k.split('.')[0], :name=>k.split('.')[1], :alias=>v}
11
+ }
12
+ }
13
+ end
14
+
15
+ valid :class, :if=>:class
16
+ valid :to_class, :if=>:class
17
+ valid :alias, :unless=>:instance_method, :with=>[:class, :alias], :optional=>true
18
+ valid :to_method, :if=>:class_method, :with=>[:to_class, :name]
19
+
20
+ generate do |aliases|
21
+ aliases.map {|e|
22
+ %[#{class_or_module(e[:class])} ::#{e[:class]}; def #{e[:alias]}(*args, &block); #{e[:to_class]}.__send__(:#{e[:name]}, *args, &block); end; end]
23
+ }.join("\n")
24
+ end
25
+ end
@@ -0,0 +1,11 @@
1
+ # Creates constants of aliases expecting a hash of existing constants mapped to their aliases.
2
+ class Alias::Creators::ConstantCreator < Alias::Creator
3
+ map {|config| config.map {|k,v| {:name=>k, :alias=>v}} }
4
+
5
+ valid :alias, :unless=>:constant, :optional=>true
6
+ valid :name, :if=>:constant
7
+
8
+ generate do |aliases|
9
+ aliases.map {|e| "::#{e[:alias]} = ::#{e[:name]}"}.join("\n")
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ # Creates aliases of instance methods. Expects a hash of classes/modules mapped to a hash of instance methods and their aliases
2
+ # i.e. {"String"=>{'to_s'=>'s'}}.
3
+ class Alias::Creators::InstanceMethodCreator < Alias::Creator
4
+ map do |config|
5
+ config.inject([]) {|t,(klass,aliases)|
6
+ t += aliases.map {|k,v| {:class=>klass, :name=>k, :alias=>v} }
7
+ }
8
+ end
9
+
10
+ valid :class, :if=>:class
11
+ valid :instance_method, :if=>:instance_method, :with=>[:class, :name]
12
+ valid :alias, :unless=>:instance_method, :with=>[:class, :alias], :optional=>true
13
+
14
+ generate do |aliases|
15
+ aliases.map {|e|
16
+ "#{class_or_module(e[:class])} ::#{e[:class]}; alias_method :#{e[:alias]}, :#{e[:name]}; end"
17
+ }.join("\n")
18
+ end
19
+ end
@@ -0,0 +1,140 @@
1
+ module Alias
2
+ # This class manages creation, searching and saving of aliases. Aliases are created as follows:
3
+ # * Alias hashes are read in by Alias.create and/or input through the console via Alias::Console.create_aliases.
4
+ # * These alias hashes are passed to Alias::Manager.create_aliases. An Alias::Manager object passes each alias hash to
5
+ # to the correct Alias::Creator subclass by interpreting the creator type.
6
+ # * Each creator converts their alias hash to an alias hash array and runs them through their defined validators, Alias::Validator objects.
7
+ # * The aliases that meet the validation conditions are then created using Kernel.eval.
8
+ class Manager
9
+
10
+ def initialize #:nodoc:
11
+ @creators = {}
12
+ @verbose = false
13
+ @force = false
14
+ end
15
+
16
+ # When true, all failed validations print their message. Takes an array of creator type symbols or a boolean to set all creators.
17
+ attr_accessor :verbose
18
+ # When true, optional validations will be skipped. Takes an array of creator type symbols or a boolean to set all creators.
19
+ attr_accessor :force
20
+ # A hash of creator objects that have been used by creator type.
21
+ attr_reader :creators
22
+ # A hash of created aliases by creator type.
23
+ attr_reader :created_aliases
24
+
25
+ # The main method for creating aliases. Takes a creator type, a hash of aliases whose format is defined per creator and the
26
+ # following options:
27
+ #
28
+ # [:verbose] Sets the verbose flag to print a message whenever an alias validation fails. Default is the creator's verbose flag.
29
+ # [:force] Sets the force flag to bypass optional validations. Default is the creator's manager flag.
30
+ # [:pretend] Instead of creating aliases, prints out the ruby code that would be evaluated by Kernel.eval to create the aliases.
31
+ # Default is false.
32
+ def create_aliases(creator_type, aliases_hash, options={})
33
+ return unless (creator = create_creator(creator_type))
34
+ creator.verbose = options[:verbose] ? options[:verbose] : verbose_creator?(creator_type)
35
+ creator.force = options[:force] ? options[:force] : force_creator?(creator_type)
36
+ creator.create(aliases_hash.dup, options[:pretend] || false)
37
+ true
38
+ rescue Creator::AbstractMethodError
39
+ $stderr.puts $!.message
40
+ rescue Creator::FailedAliasCreationError
41
+ $stderr.puts "'#{creator.class}' failed to create aliases with error:\n#{$!.message}"
42
+ end
43
+
44
+ # Creates aliases in the same way as create_aliases while keeping track of what's created. But differs in that creator types can
45
+ # be accessed with just the first few unique letters of a type. For example, you can pass :in to mean :instance_method. Also, the
46
+ # verbose flag is set by default.
47
+ # Examples:
48
+ # console_create_aliases :in, "String"=>{"to_s"=>"s"}
49
+ # console_create_aliases :con, {"ActiveRecord::Base"=>"AB"}, :pretend=>true
50
+ def console_create_aliases(creator_type, aliases_hash, options={})
51
+ options = {:verbose=>true}.update(options)
52
+ @created_aliases ||= {}
53
+ creator_type = (all_creator_types.sort.find {|e| e[/^#{creator_type}/] } || creator_type).to_sym
54
+ if create_aliases(creator_type, aliases_hash, options)
55
+ @created_aliases[creator_type] = aliases_hash
56
+ true
57
+ else
58
+ false
59
+ end
60
+ end
61
+
62
+ # Saves aliases that were created by console_create_aliases. Can take an optional file to save to. See Alias::Console.save_aliases
63
+ # for default files this method saves to.
64
+ def save_aliases(file=nil)
65
+ if @created_aliases
66
+ Alias.add_to_config_file(@created_aliases, file)
67
+ true
68
+ else
69
+ puts "Didn't save. No created aliases detected."
70
+ false
71
+ end
72
+ end
73
+
74
+ # Searches all created alias hashes with a hash or a string. If a string, the alias key searched is :name.
75
+ # If a hash, the key should should be an alias key and the value the search term.
76
+ # All values are treated as regular expressions. Alias keys vary per creator but some of the common ones are :name, :class and :alias.
77
+ # Multiple keys for a hash will AND the searches.
78
+ # Examples:
79
+ # search 'to_'
80
+ # search :class=>"Array", :name=>'to'
81
+ def search(search_hash)
82
+ result = nil
83
+ reset_all_aliases
84
+ search_hash = {:name=>search_hash} unless search_hash.is_a?(Hash)
85
+ search_hash.each do |k,v|
86
+ new_result = simple_search(k,v)
87
+ #AND's searches
88
+ result = intersection_of_two_arrays(new_result, result)
89
+ end
90
+ #duplicate results in case they are modified
91
+ result = result.map {|e| e.dup} if result
92
+ result
93
+ end
94
+
95
+ # Returns an array of all created alias hashes. The alias hash will have a :type key which contains the creator type it belongs to.
96
+ def all_aliases
97
+ @all_aliases ||= @creators.inject([]) do |t, (type, creator)|
98
+ t += creator.aliases.each {|e| e[:type] = type.to_s}
99
+ end
100
+ end
101
+
102
+ #:stopdoc:
103
+ def all_creator_types
104
+ Creator.creators.map {|e| Util.underscore(e.to_s[/::(\w+)Creator$/,1]) }
105
+ end
106
+
107
+ def aliases_of(creator_type)
108
+ @creators[creator_type] && @creators[creator_type].aliases
109
+ end
110
+
111
+ def verbose_creator?(creator_type)
112
+ @verbose.is_a?(Array) ? @verbose.include?(creator_type.to_sym) : @verbose
113
+ end
114
+
115
+ def force_creator?(creator_type)
116
+ @force.is_a?(Array) ? @force.include?(creator_type.to_sym) : @force
117
+ end
118
+
119
+ def create_creator(creator_type)
120
+ creator_class_string = "Alias::Creators::#{Util.camelize(creator_type.to_s)}Creator"
121
+ if creator_class = Util.any_const_get(creator_class_string)
122
+ @creators[creator_type.to_sym] ||= creator_class.new
123
+ else
124
+ $stderr.puts "Creator class '#{creator_class_string}' not found."
125
+ nil
126
+ end
127
+ end
128
+
129
+ def simple_search(field, search_term)
130
+ all_aliases.select {|e| e[field] =~ /#{search_term}/ }
131
+ end
132
+
133
+ def intersection_of_two_arrays(arr1, arr2)
134
+ arr2.nil? ? arr1 : arr1.select {|e| arr2.include?(e)}
135
+ end
136
+
137
+ def reset_all_aliases; @all_aliases = nil; end
138
+ #:startdoc:
139
+ end
140
+ end
@@ -0,0 +1,86 @@
1
+ module Alias
2
+ # A collection of utility functions used throughout.
3
+ module Util #:nodoc:
4
+ extend self
5
+ # simplified from ActiveSupport
6
+ def slice(hash, *keys)
7
+ hash.reject {|key,| !keys.include?(key) }
8
+ end
9
+
10
+ def slice_off!(hash, *keys) #:nod
11
+ new_hash = slice(hash,*keys)
12
+ keys.each {|e| hash.delete(e)}
13
+ new_hash
14
+ end
15
+
16
+ #from ActiveSupport
17
+ def symbolize_keys(hash)
18
+ hash.inject({}) do |options, (key, value)|
19
+ options[key.to_sym] = value
20
+ options
21
+ end
22
+ end
23
+
24
+ # Recursively merge hash1 with hash2.
25
+ def recursive_hash_merge(hash1, hash2)
26
+ hash1.merge(hash2) {|k,o,n| (o.is_a?(Hash)) ? recursive_hash_merge(o,n) : n}
27
+ end
28
+
29
+ def any_const_get(name)
30
+ Util.const_cache[name] ||= Util.uncached_any_const_get(name)
31
+ end
32
+
33
+ def const_cache
34
+ @const_cache ||= {}
35
+ end
36
+
37
+ def uncached_any_const_get(name)
38
+ begin
39
+ klass = Object
40
+ name.split('::').each {|e|
41
+ klass = klass.const_get(e)
42
+ }
43
+ klass
44
+ rescue
45
+ nil
46
+ end
47
+ end
48
+
49
+ #simplified version from ActiveSupport
50
+ def camelize(string)
51
+ string.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
52
+ end
53
+
54
+ # from ActiveSupport
55
+ def underscore(camel_cased_word)
56
+ camel_cased_word.to_s.gsub(/::/, '/').
57
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
58
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
59
+ tr("-", "_").
60
+ downcase
61
+ end
62
+
63
+ def make_shortest_aliases(unaliased_strings)
64
+ shortest_aliases = {}
65
+ possible_alias = ''
66
+ unaliased_strings.each {|s|
67
+ possible_alias = ''
68
+ s.split('').each { |e|
69
+ possible_alias += e
70
+ if ! shortest_aliases.values.include?(possible_alias)
71
+ shortest_aliases[s] = possible_alias
72
+ break
73
+ end
74
+ }
75
+ }
76
+ shortest_aliases
77
+ end
78
+
79
+ def silence_warnings
80
+ old_verbose, $VERBOSE = $VERBOSE, nil
81
+ yield
82
+ ensure
83
+ $VERBOSE = old_verbose
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,94 @@
1
+ module Alias
2
+ # Creates validations for use with Alias::Creator.valid.
3
+ class Validator
4
+ class MissingConditionError < StandardError; end
5
+ class InvalidValidatorError < StandardError; end
6
+
7
+ attr_reader :validation_proc, :message
8
+ # Options are described in Alias::Creator.valid.
9
+ def initialize(options={})
10
+ raise ArgumentError unless options[:key] && options[:creator]
11
+ @condition = options[:if] || options[:unless] || raise(MissingConditionError)
12
+ inherits(Validator.validators[@condition]) if Validator.validators[@condition]
13
+ raise InvalidValidatorError unless @condition.is_a?(Proc)
14
+ @optional = options[:optional] || false
15
+ @validation_proc = options[:unless] ? lambda {|e| ! @condition.clone.call(e) } : @condition
16
+ @options = options
17
+ @message = options[:message] if options[:message]
18
+ @message = default_message unless @message.is_a?(Proc)
19
+ end
20
+
21
+ # Validates a given alias hash with the validator proc defined by :if or :unless in Alias::Creator.valid.
22
+ # Default arguments that these procs receive works as follows:
23
+ # If the validation key is the same name as any of the keys in the alias hash, then only the value of that
24
+ # that alias key is passed to the procs. If not, then the whole alias hash is passed.
25
+ def validate(current_creator, alias_hash, current_attribute)
26
+ return true if @optional && current_creator.force
27
+ arg = create_proc_arg(alias_hash, current_attribute)
28
+ result = !!@validation_proc.call(arg)
29
+ puts create_message(arg) if result != true && current_creator.verbose
30
+ result
31
+ end
32
+
33
+ #:stopdoc:
34
+ def inherits(parent_validator)
35
+ @condition = parent_validator.validation_proc.clone
36
+ @message = parent_validator.message.clone
37
+ end
38
+
39
+ def default_message
40
+ lambda {|e| "Validation failed for #{@options[:creator]}'s #{@options[:key]} since it doesn't exist"}
41
+ end
42
+
43
+ def create_proc_arg(alias_hash, current_attribute) #:nodoc:
44
+ @options[:with] ? @options[:with].map {|f| alias_hash[f] } : (alias_hash[current_attribute] || alias_hash)
45
+ end
46
+
47
+ def create_message(arg)
48
+ result = @message.call(arg)
49
+ @options[:unless] ? result.gsub("doesn't exist", 'already exists') : result
50
+ end
51
+ #:startdoc:
52
+
53
+ class <<self
54
+ # Hash of registered validators.
55
+ attr_reader :validators
56
+
57
+ # Registers validators which creators can reference as symbols in Alias::Creator.valid.
58
+ def register_validators(validators)
59
+ @validators ||= {}
60
+ validators.each do |e|
61
+ @validators[e[:key]] ||= Validator.new(e.merge(:creator=>self))
62
+ end
63
+ end
64
+
65
+ # Default validators are :constant, :class, :instance_method and :class_method.
66
+ def default_validators
67
+ [
68
+ {:key=>:constant, :if=>lambda {|e| any_const_get(e) }, :message=>lambda {|e| "Constant '#{e}' not created since it doesn't exist"}},
69
+ {:key=>:class, :if=>lambda {|e| ((klass = any_const_get(e)) && klass.is_a?(Module)) },
70
+ :message=>lambda {|e| "Alias for class '#{e}' not created since the class doesn't exist"}},
71
+ {:key=>:instance_method, :if=> lambda {|e| instance_method?(*e) },
72
+ :message=>lambda {|e| "Alias for instance method '#{e[0]}.#{e[1]}' not created since it doesn't exist" }},
73
+ {:key=>:class_method, :if=>lambda {|e| class_method?(*e) },
74
+ :message=>lambda {|e| "Alias for class method '#{e[0]}.#{e[1]}' not created since it doesn't exist" }}
75
+ ]
76
+ end
77
+
78
+ #:stopdoc:
79
+ def any_const_get(name)
80
+ Util.any_const_get(name)
81
+ end
82
+
83
+ def instance_method?(klass, method)
84
+ (klass = any_const_get(klass)) && (klass.method_defined?(method) || klass.private_method_defined?(method))
85
+ end
86
+
87
+ def class_method?(klass, method)
88
+ (klass = any_const_get(klass)) && klass.respond_to?(method, true)
89
+ end
90
+ #:startdoc:
91
+ end
92
+ end
93
+ end
94
+ Alias::Validator.register_validators(Alias::Validator.default_validators)
@@ -0,0 +1,63 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper.rb')
2
+
3
+ class AliasTest < Test::Unit::TestCase
4
+ context "Alias" do
5
+ test "loads config file config/alias.yml if found" do
6
+ File.expects(:exists?).with('config/alias.yml').returns(true)
7
+ Alias.config_file.should == 'config/alias.yml'
8
+ end
9
+
10
+ context "create" do
11
+ before(:each) { Alias.instance_eval "@manager = @config = @config_file = nil"}
12
+
13
+ test "with aliases option creates aliases" do
14
+ options = {:aliases=>{:constant=> {'Array'=>'Arr'}, :instance_method=>{'String'=>{'to_s'=>'s'}} } , :file=>false}
15
+ Alias.create options
16
+ Alias.manager.aliases_of(:instance_method).empty?.should be(false)
17
+ Alias.manager.aliases_of(:constant).empty?.should be(false)
18
+ Alias.config.should == options
19
+ end
20
+
21
+ test "with file option creates aliases" do
22
+ Alias.create :file=>File.join(File.dirname(__FILE__),'aliases.yml')
23
+ Alias.manager.aliases_of(:instance_method).empty?.should be(false)
24
+ Alias.manager.aliases_of(:class_method).empty?.should be(false)
25
+ Alias.manager.aliases_of(:constant).empty?.should be(false)
26
+ Alias.manager.aliases_of(:class_to_instance_method).empty?.should be(false)
27
+ end
28
+
29
+ test "with false file option doesn't load config file" do
30
+ Alias.create :file=>'blah'
31
+ File.expects(:exists?).never
32
+ Alias.create :file=>false
33
+ end
34
+
35
+ test "with invalid file option creates nothing" do
36
+ Alias.create :file=>'blah'
37
+ Alias.config.should == {:aliases=>{}}
38
+ end
39
+
40
+ test "with verbose option sets manager's verbose" do
41
+ Alias.manager.verbose.should == false
42
+ Alias.create :verbose=>true, :aliases=>{}, :file=>false
43
+ Alias.manager.verbose.should == true
44
+ end
45
+
46
+ test "with force option sets manager's verbose" do
47
+ Alias.manager.force.should == false
48
+ Alias.create :force=>true, :aliases=>{}
49
+ Alias.manager.force.should == true
50
+ end
51
+
52
+ test "called twice recursively merges config" do
53
+ hash1 = {:constant=>{"Blah"=>"B"}}
54
+ Alias.manager.expects(:create_aliases).with(:constant, hash1[:constant])
55
+ Alias.create :aliases=>hash1, :file=>false
56
+ hash2 = {:constant=>{"Blah2"=>"B2"}}
57
+ Alias.manager.expects(:create_aliases).with(:constant, hash2[:constant])
58
+ Alias.create :aliases=>hash2, :file=>false
59
+ Alias.config.should == {:aliases=>{:constant=>{"Blah"=>"B", "Blah2"=>"B2"}} }
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,15 @@
1
+ :aliases:
2
+ constant:
3
+ Object: O
4
+
5
+ class_method:
6
+ Object:
7
+ const_get: cg
8
+
9
+ instance_method:
10
+ String:
11
+ to_s: ts
12
+ bling: bl
13
+ class_to_instance_method:
14
+ String:
15
+ Date.civil: civ