alias 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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