alias 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,16 @@
1
+ == 0.2.0
2
+ * Complete refactoring.
3
+ * New alias types can be created easily with the DSL for Creator classes.
4
+ * Added two new alias types, any_to_instance_method and class_to_instance_method.
5
+ * Better tests and docs.
6
+
7
+ == 0.1.2
8
+ * Added search and console functionality.
9
+ * Added better tests.
10
+
11
+ == 0.1.1
12
+ * Added checking for existing aliases.
13
+ * Added config block to Alias.init.
14
+
15
+ == 0.1.0
16
+ * Initial release
@@ -0,0 +1,22 @@
1
+ The MIT LICENSE
2
+
3
+ Copyright (c) 2009 Gabriel Horner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,88 @@
1
+ == Description
2
+
3
+ Creates aliases for class methods, instance methods, constants, delegated methods and more. Aliases
4
+ can be easily searched or saved as YAML config files to load later. Custom alias types are easy to
5
+ create with the DSL Alias provides. Although Alias was created with the irb user in mind, any Ruby
6
+ console program can hook into Alias for creating configurable aliases.
7
+
8
+ == Setup
9
+
10
+ Install the gem with:
11
+
12
+ sudo gem install cldwalker-alias -s http://gems.github.com
13
+
14
+ The basic setup is simply drop two lines in your .irbrc:
15
+
16
+ require 'alias'
17
+ Alias.create
18
+
19
+ This will assume a file in config/alias.yml or ~/.alias.yml. If you want it somewhere else,
20
+ pass a :file option to create():
21
+
22
+ Alias.create :file=>"/path/to/my/clandestine_aliases.yml"
23
+
24
+ If you'd like to define your aliases without a config file, pass Alias.create() an :aliases option:
25
+
26
+ Alias.create :verbose=>true, :aliases=>{
27
+ :constant=>{'Array' = 'A'},
28
+ :instance_method=>{'String'=>{'downcase'=>'dc' }, 'Array'=>{'select'=>'s'}}
29
+ }
30
+
31
+ == Usage
32
+
33
+ An example within Rails' script/console:
34
+
35
+ bash> script/console
36
+ >> require 'alias'
37
+ => true
38
+
39
+ # Import alias methods
40
+ >> extend Alias::Console
41
+ => main
42
+
43
+ # First let's see what ruby code Alias generates to create an alias.
44
+ >> create_aliases :class_method, {"ActiveRecord::Base"=>{'find'=>'[]'}}, :pretend=>true
45
+
46
+ class ::ActiveRecord::Base; class<< self; alias_method :[], :find; end; end
47
+ => true
48
+
49
+ # Create the above class method alias
50
+ >> create_aliases :class_method, "ActiveRecord::Base"=>{'find'=>'[]'}
51
+ => true
52
+
53
+ # Create the above constant alias
54
+ >> create_aliases :constant, "ActiveRecord::Base"=>"AB"
55
+ => true
56
+ # Verify that it worked
57
+ >> AB
58
+ => ActiveRecord::Base
59
+
60
+ # If we try to create the constant alias again, Alias prevents us and warns us
61
+ >> create_aliases :constant, "ActiveRecord::Base"=>"AB"
62
+ Constant 'AB' not created since it already exists
63
+ => false
64
+ # We can force Alias to override a method, class or constant that already exists
65
+ >> create_aliases :constant, {"ActiveRecord::Base"=>"AB"}, :force=>true
66
+ => true
67
+
68
+ # Create the above instance method alias
69
+ >> create_aliases :instance_method, "ActiveRecord::Base"=>{"update_attribute"=>'ua'}
70
+ => true
71
+
72
+ # By default aliases are saved to config/alias.yml in rails or ~/.alias.yml if not.
73
+ >> save_aliases
74
+ Saved created aliases to config/alias.yml.
75
+ => true
76
+
77
+ == Configuration
78
+
79
+ For an example config file see test/aliases.yml.
80
+ For an explanation of the config file format see Alias.config_file.
81
+
82
+ == Creating Custom Alias Types
83
+
84
+ See Alias::Creator.
85
+
86
+ == Todo
87
+ * Allow loading of select aliases in a file.
88
+ * Provide a way to autogenerate aliases with a given proc.
@@ -0,0 +1,50 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ begin
5
+ require 'rcov/rcovtask'
6
+
7
+ Rcov::RcovTask.new do |t|
8
+ t.libs << 'test'
9
+ t.test_files = FileList['test/**/*_test.rb']
10
+ t.rcov_opts = ["-T -x '/Library/Ruby/*'"]
11
+ t.verbose = true
12
+ end
13
+ rescue LoadError
14
+ puts "Rcov not available. Install it for rcov-related tasks with: sudo gem install rcov"
15
+ end
16
+
17
+ begin
18
+ require 'jeweler'
19
+ Jeweler::Tasks.new do |s|
20
+ s.name = "alias"
21
+ s.summary = "Creates, manages and saves aliases for class methods, instance methods, constants, delegated methods and more."
22
+ s.description = "Creates aliases for class methods, instance methods, constants, delegated methods and more. Aliases can be easily searched or saved as YAML config files to load later. Custom alias types are easy to create with the DSL Alias provides. Although Alias was created with the irb user in mind, any Ruby console program can hook into Alias for creating configurable aliases."
23
+ s.email = "gabriel.horner@gmail.com"
24
+ s.homepage = "http://tagaholic.me/alias/"
25
+ s.authors = ["Gabriel Horner"]
26
+ s.rubyforge_project = 'tagaholic'
27
+ s.has_rdoc = true
28
+ s.extra_rdoc_files = ["README.rdoc", "LICENSE.txt"]
29
+ s.files = FileList["Rakefile", "VERSION.yml", "README.rdoc", "CHANGELOG.rdoc", "LICENSE.txt", "{lib,test}/**/*"]
30
+ end
31
+
32
+ rescue LoadError
33
+ puts "Jeweler not available. Install it for jeweler-related tasks with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
34
+ end
35
+
36
+ Rake::TestTask.new do |t|
37
+ t.libs << 'lib'
38
+ t.pattern = 'test/**/*_test.rb'
39
+ t.verbose = false
40
+ end
41
+
42
+ Rake::RDocTask.new do |rdoc|
43
+ rdoc.rdoc_dir = 'rdoc'
44
+ rdoc.title = 'test'
45
+ rdoc.options << '--line-numbers' << '--inline-source'
46
+ rdoc.rdoc_files.include('README*')
47
+ rdoc.rdoc_files.include('lib/**/*.rb')
48
+ end
49
+
50
+ task :default => :test
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 2
4
+ :patch: 0
@@ -0,0 +1,85 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) ||
2
+ $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+ require 'yaml'
4
+ require 'alias/manager'
5
+ require 'alias/validator'
6
+ require 'alias/creator'
7
+ require 'alias/creators/constant_creator'
8
+ require 'alias/creators/instance_method_creator'
9
+ require 'alias/creators/class_method_creator'
10
+ require 'alias/creators/class_to_instance_method_creator'
11
+ require 'alias/creators/any_to_instance_method_creator'
12
+ require 'alias/util'
13
+ require 'alias/console'
14
+
15
+ # Most of the core Alias actions are run through Alias::Manager except for Alias.create.
16
+ # See Alias::Manager for an explanation of how aliases are created.
17
+ module Alias
18
+ extend self
19
+
20
+ # Creates aliases from Alias.config_file if it exists and merges them with any explicit aliases. This method takes
21
+ # the same keys used by config files (see Alias.config_file) and also the following options:
22
+ # * :file : Specifies a config file to override Alias.config_file. If set to false, no config file is loaded.
23
+ # Examples:
24
+ # # Loads any default files and the ones in :aliases.
25
+ # # Sets global verbosity for creators.
26
+ # create :aliases=>{:constant=>{"Array"=>"A"}}, :verbose=>true
27
+ # # Loads the given file and sets verbosity just for the :instance_method creator.
28
+ # create :file=>"some file", :verbose=>[:instance_method]
29
+ def create(options={})
30
+ file_config = load_config_file(options.delete(:file))
31
+ new_config = Util.recursive_hash_merge(file_config, options)
32
+ manager.verbose = new_config[:verbose] if new_config[:verbose]
33
+ manager.force = new_config[:force] if new_config[:force]
34
+ (new_config[:aliases] || {}).each do |creator_type, aliases|
35
+ manager.create_aliases(creator_type, aliases)
36
+ end
37
+ @config = Util.recursive_hash_merge(config, new_config)
38
+ end
39
+
40
+ # By default, looks for existing files in config/alias.yml and then ~/.alias.yml. A config file has the following keys:
41
+ # [:aliases] This takes a hash mapping creators to their config hashes. Valid creators are :instance_method, :class_method, :constant,
42
+ # :class_to_instance_method and :any_to_instance_method.
43
+ # [:verbose] Sets whether creators are verbose with boolean or array of creator symbols. A boolean sets verbosity for all creators whereas
44
+ # the array specifies which creators. Default is false.
45
+ # [:force] Sets whether creators force optional validations with boolean or array of creator symbols. Works the same as :verbose. Default is false.
46
+ def config_file
47
+ @config_file ||= File.exists?("config/alias.yml") ? 'config/alias.yml' : "#{ENV['HOME']}/.alias.yml"
48
+ end
49
+
50
+ # Contains primary Alias::Manager object which is used throughout Alias.
51
+ def manager
52
+ @manager ||= Manager.new
53
+ end
54
+
55
+ #:stopdoc:
56
+ def add_to_config_file(new_aliases, file)
57
+ file ||= File.directory?('config') ? 'config/alias.yml' : "#{ENV['HOME']}/.alias.yml"
58
+ existing_aliases = read_config_file(file)
59
+ existing_aliases[:aliases] = Util.recursive_hash_merge existing_aliases[:aliases], new_aliases
60
+ save_to_file file, existing_aliases.to_yaml
61
+ puts "Saved created aliases to #{file}."
62
+ end
63
+
64
+ def save_to_file(file, string)
65
+ File.open(File.expand_path(file), 'w') {|f| f.write string }
66
+ end
67
+
68
+ def read_config_file(file)
69
+ file_config = File.exists?(File.expand_path(file)) ? YAML::load_file(File.expand_path(file)) : {}
70
+ file_config = Util.symbolize_keys file_config
71
+ file_config[:aliases] = file_config[:aliases] ? Util.symbolize_keys(file_config.delete(:aliases)) : {}
72
+ file_config
73
+ end
74
+
75
+ def load_config_file(file)
76
+ return {} if file == false
77
+ @config_file = file if file
78
+ read_config_file(config_file)
79
+ end
80
+
81
+ def config
82
+ @config ||= {}
83
+ end
84
+ #:startdoc:
85
+ end
@@ -0,0 +1,20 @@
1
+ module Alias
2
+ # This module contains the main methods to be accessed from a ruby shell i.e. irb. Simply extend Alias::Console in your ruby shell.
3
+ module Console
4
+ # See Alias::Manager.create_aliases for usage.
5
+ def create_aliases(*args)
6
+ Alias.manager.console_create_aliases(*args)
7
+ end
8
+
9
+ # Saves aliases to a file. If no file is given, defaults to config/alias.yml if the config directory exists (for Rails).
10
+ # Otherwise defaults to ~/.alias.yml.
11
+ def save_aliases(file=nil)
12
+ Alias.manager.save_aliases(file)
13
+ end
14
+
15
+ # Searches aliases with a search term as defined by Alias::Manager.search. If no arguments given, all aliases are listed.
16
+ def search_aliases(*args)
17
+ args.empty? ? Alias.manager.all_aliases : Alias.manager.search(*args)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,112 @@
1
+ module Alias
2
+ # Namespace for subclasses of Alias::Creator.
3
+ class Creators; end
4
+ # This is the base creator class. To be a valid subclass, the creator must define Alias::Creator.map and Alias::Creator.generate.
5
+ # Although not required, creators should enforce validation of their aliases with Alias::Creator.valid. Also, the creator should
6
+ # be named in the format Alias::Creators::*Creator where the asterisk stands for any unique string. Since that string is converted
7
+ # to an underscored version when referenced in the console, it's recommended to make it camel case. For example,
8
+ # Alias::Creators::InstanceMethodCreator is referenced by it's underscored version :instance_method.
9
+ #
10
+ # To better understand how a creator works, here's the steps a creator goes through when creating aliases:
11
+ # * map() : Maps the hash from a config file or console input into an array of alias hashes.
12
+ # * valid() : Defines a validation that each alias hash must pass.
13
+ # * generate() : Given the array of alias hashes, generates the string of ruby code to be evaled for alias creation.
14
+ class Creator
15
+ class AbstractMethodError < StandardError; end
16
+ class FailedAliasCreationError < StandardError; end
17
+ class<<self
18
+ # Creates a validation expectation for the creator by giving it an Alias::Validator object aka validator.
19
+ # This method must be given an :if or :unless option. If the :if option returns false or :unless option
20
+ # returns true for an alias, the alias is skipped.
21
+ # ==== Options:
22
+ # [:if] Takes a proc or a symbol referencing a registered validator. This proc must evaluate to true
23
+ # for the validation to pass. See Alias::Validator.validate for what arguments this proc receives by default. See
24
+ # Alias::Validator.default_validators for validators that can be referenced by symbol.
25
+ # [:unless] Same as :if option but the result is negated.
26
+ # [:message] A proc to print a message if the creator's verbose flag is set. Receives same arguments as :if and :unless procs.
27
+ # If a previous validator is referenced in :unless or :if, then their :message is inherited.
28
+ # [:with] An array of alias attributes/keys which specify the current alias attributes to pass to the validator procs.
29
+ # Overrides default argument a validator proc receives.
30
+ # [:optional] When set to true, this option can be overridden in conjunction with a creator's force flag. Default is false.
31
+ def valid(key, options={})
32
+ begin
33
+ validators[key] = Validator.new(options.merge(:key=>key, :creator=>self))
34
+ rescue Validator::MissingConditionError
35
+ raise ArgumentError, "A :unless or :if option is required."
36
+ rescue Validator::InvalidValidatorError
37
+ $stderr.puts "Validator not set for #{key}"
38
+ @validators.delete(key)
39
+ end
40
+ end
41
+
42
+ # Stores validators per alias attribute/key.
43
+ def validators #:nodoc:
44
+ @validators ||= {}
45
+ end
46
+
47
+ def maps_config(config) #:nodoc:
48
+ @map ? @map.call(config) : raise(AbstractMethodError, "No map() defined for #{self}")
49
+ end
50
+
51
+ # Takes a block which converts the creator's config to an array of aliases.
52
+ def map(&block)
53
+ @map = block
54
+ end
55
+
56
+ def generates_aliases(aliases) #:nodoc:
57
+ @generate ? @generate.call(aliases) : raise(AbstractMethodError, "No generate() defined for #{self}")
58
+ end
59
+
60
+ # Takes a block which converts aliases to a string of ruby code to run through Kernel#eval.
61
+ def generate(&block)
62
+ @generate = block
63
+ end
64
+
65
+ def class_or_module(klass) #:nodoc:
66
+ Util.any_const_get(klass).is_a?(Class) ? 'class' : 'module'
67
+ end
68
+
69
+ def inherited(subclass) #:nodoc:
70
+ @creators ||= []
71
+ @creators << subclass
72
+ end
73
+
74
+ # Array of all Creator subclasses.
75
+ def creators; @creators; end
76
+ end
77
+
78
+ # Same purpose as Alias::Manager.verbose and Alias::Manager.force but unlike them these only take a boolean.
79
+ attr_accessor :verbose, :force
80
+ # Array of alias hashes that have been created.
81
+ attr_accessor :aliases
82
+
83
+ def initialize(options={}) #:nodoc:
84
+ @verbose = false
85
+ @force = false
86
+ @aliases = []
87
+ end
88
+
89
+ # Main method used to create aliases. Handles mapping, validation and creation of aliases.
90
+ def create(aliases_hash, pretend=false)
91
+ aliases_array = self.class.maps_config(aliases_hash)
92
+ delete_invalid_aliases(aliases_array)
93
+ self.aliases = aliases + aliases_array unless pretend
94
+ begin
95
+ #td: create method for efficiently removing constants/methods in any namespace
96
+ eval_string = Util.silence_warnings { self.class.generates_aliases(aliases_array) }
97
+ pretend ? puts("\n", eval_string) : Kernel.eval(eval_string)
98
+ rescue
99
+ raise FailedAliasCreationError, $!
100
+ end
101
+ end
102
+
103
+ # Deletes invalid alias hashes that fail defined validators for a creator.
104
+ def delete_invalid_aliases(arr)
105
+ arr.delete_if {|alias_hash|
106
+ !self.class.validators.all? {|attribute, validator|
107
+ validator.validate(self, alias_hash, attribute)
108
+ }
109
+ }
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,22 @@
1
+ # Creates instance methods which can call any string of ruby code which ends in a method. This class provides the same
2
+ # functionality that Alias::Creators::ClassToInstanceMethodCreator provides and more but at the cost of less validation.
3
+ # Expects a hash of classes/modules of the instance method mapped to a hash of ruby code strings and the instance method names.
4
+ # For example, the hash {"MyDate"=>{'Date.today.to_s.gsub'=>'t'}} creates a MyDate.t method which directly calls Date.today.to_s.gsub.
5
+ class Alias::Creators::AnyToInstanceMethodCreator < Alias::Creator
6
+ map do |config|
7
+ config.inject([]) {|t,(klass,hash)|
8
+ t += hash.map {|k,v|
9
+ {:class=>klass, :any_method=>k, :alias=>v}
10
+ }
11
+ }
12
+ end
13
+
14
+ valid :class, :if=>:class
15
+ valid :alias, :unless=>:instance_method, :with=>[:class, :alias], :optional=>true
16
+
17
+ generate do |aliases|
18
+ aliases.map {|e|
19
+ %[#{class_or_module(e[:class])} ::#{e[:class]}; def #{e[:alias]}(*args, &block); #{e[:any_method]}(*args, &block); end; end]
20
+ }.join("\n")
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ # Creates aliases of class methods. Expects a hash of classes/modules mapped to a hash of class methods and their aliases
2
+ # i.e. {'Date'=>{'today'=>'t'}}.
3
+ class Alias::Creators::ClassMethodCreator < 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 :class_method, :if=>:class_method, :with=>[:class, :name]
12
+ valid :alias, :unless=>:class_method, :with=>[:class, :alias], :optional=>true
13
+
14
+ generate do |aliases|
15
+ aliases.map {|e|
16
+ "#{class_or_module(e[:class])} ::#{e[:class]}; class<<self; alias_method :#{e[:alias]}, :#{e[:name]}; end; end"
17
+ }.join("\n")
18
+ end
19
+ end