amp-front 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/.document +5 -0
  2. data/.gitignore +24 -0
  3. data/Ampfile +3 -0
  4. data/Gemfile +10 -0
  5. data/Gemfile.lock +36 -0
  6. data/LICENSE +20 -0
  7. data/README.md +50 -0
  8. data/Rakefile +64 -0
  9. data/VERSION +1 -0
  10. data/design_docs/commands.md +91 -0
  11. data/design_docs/dependencies.md +35 -0
  12. data/design_docs/plugins.md +47 -0
  13. data/features/amp.feature +8 -0
  14. data/features/amp_help.feature +36 -0
  15. data/features/amp_plugin_list.feature +10 -0
  16. data/features/step_definitions/amp-front_steps.rb +23 -0
  17. data/features/support/env.rb +4 -0
  18. data/lib/amp-front.rb +30 -0
  19. data/lib/amp-front/dispatch/commands/base.rb +158 -0
  20. data/lib/amp-front/dispatch/commands/builtin/help.rb +23 -0
  21. data/lib/amp-front/dispatch/commands/builtin/plugin.rb +24 -0
  22. data/lib/amp-front/dispatch/commands/validations.rb +171 -0
  23. data/lib/amp-front/dispatch/runner.rb +86 -0
  24. data/lib/amp-front/help/entries/__default__.erb +31 -0
  25. data/lib/amp-front/help/entries/ampfiles.md +42 -0
  26. data/lib/amp-front/help/entries/commands.erb +6 -0
  27. data/lib/amp-front/help/entries/new-commands.md +81 -0
  28. data/lib/amp-front/help/help.rb +312 -0
  29. data/lib/amp-front/plugins/base.rb +87 -0
  30. data/lib/amp-front/support/module_extensions.rb +92 -0
  31. data/lib/amp-front/third_party/maruku.rb +136 -0
  32. data/lib/amp-front/third_party/maruku/attributes.rb +227 -0
  33. data/lib/amp-front/third_party/maruku/defaults.rb +71 -0
  34. data/lib/amp-front/third_party/maruku/errors_management.rb +92 -0
  35. data/lib/amp-front/third_party/maruku/helpers.rb +260 -0
  36. data/lib/amp-front/third_party/maruku/input/charsource.rb +326 -0
  37. data/lib/amp-front/third_party/maruku/input/extensions.rb +69 -0
  38. data/lib/amp-front/third_party/maruku/input/html_helper.rb +189 -0
  39. data/lib/amp-front/third_party/maruku/input/linesource.rb +111 -0
  40. data/lib/amp-front/third_party/maruku/input/parse_block.rb +615 -0
  41. data/lib/amp-front/third_party/maruku/input/parse_doc.rb +234 -0
  42. data/lib/amp-front/third_party/maruku/input/parse_span_better.rb +746 -0
  43. data/lib/amp-front/third_party/maruku/input/rubypants.rb +225 -0
  44. data/lib/amp-front/third_party/maruku/input/type_detection.rb +147 -0
  45. data/lib/amp-front/third_party/maruku/input_textile2/t2_parser.rb +163 -0
  46. data/lib/amp-front/third_party/maruku/maruku.rb +33 -0
  47. data/lib/amp-front/third_party/maruku/output/to_ansi.rb +223 -0
  48. data/lib/amp-front/third_party/maruku/output/to_html.rb +991 -0
  49. data/lib/amp-front/third_party/maruku/output/to_markdown.rb +164 -0
  50. data/lib/amp-front/third_party/maruku/output/to_s.rb +56 -0
  51. data/lib/amp-front/third_party/maruku/string_utils.rb +191 -0
  52. data/lib/amp-front/third_party/maruku/structures.rb +167 -0
  53. data/lib/amp-front/third_party/maruku/structures_inspect.rb +87 -0
  54. data/lib/amp-front/third_party/maruku/structures_iterators.rb +61 -0
  55. data/lib/amp-front/third_party/maruku/textile2.rb +1 -0
  56. data/lib/amp-front/third_party/maruku/toc.rb +199 -0
  57. data/lib/amp-front/third_party/maruku/usage/example1.rb +33 -0
  58. data/lib/amp-front/third_party/maruku/version.rb +40 -0
  59. data/lib/amp-front/third_party/trollop.rb +766 -0
  60. data/spec/amp-front_spec.rb +25 -0
  61. data/spec/command_specs/base_spec.rb +123 -0
  62. data/spec/command_specs/command_spec.rb +97 -0
  63. data/spec/command_specs/help_spec.rb +33 -0
  64. data/spec/command_specs/spec_helper.rb +37 -0
  65. data/spec/command_specs/validations_spec.rb +267 -0
  66. data/spec/dispatch_specs/runner_spec.rb +116 -0
  67. data/spec/dispatch_specs/spec_helper.rb +15 -0
  68. data/spec/help_specs/help_entry_spec.rb +78 -0
  69. data/spec/help_specs/help_registry_spec.rb +77 -0
  70. data/spec/help_specs/spec_helper.rb +15 -0
  71. data/spec/plugin_specs/base_spec.rb +36 -0
  72. data/spec/plugin_specs/spec_helper.rb +15 -0
  73. data/spec/spec.opts +1 -0
  74. data/spec/spec_helper.rb +33 -0
  75. data/spec/support_specs/module_extensions_spec.rb +104 -0
  76. data/spec/support_specs/spec_helper.rb +15 -0
  77. data/test/third_party_tests/test_trollop.rb +1181 -0
  78. metadata +192 -0
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,24 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+ .hg*
21
+ .bundle
22
+
23
+ ## PROJECT::SPECIFIC
24
+ *.gemspec
data/Ampfile ADDED
@@ -0,0 +1,3 @@
1
+ # Default: load all rubygems plugins.
2
+
3
+ Amp::Plugins::Base.load_rubygems_plugins
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # This is an install-only file - it's not used by the application
2
+ # require 'bundler/setup' adds over 400ms to startup time
3
+ source "http://rubygems.org"
4
+
5
+ group :development do
6
+ gem 'jeweler'
7
+ gem 'rspec', '< 2.0.0'
8
+ gem 'yard'
9
+ gem 'cucumber'
10
+ end
@@ -0,0 +1,36 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ builder (2.1.2)
5
+ cucumber (0.9.2)
6
+ builder (~> 2.1.2)
7
+ diff-lcs (~> 1.1.2)
8
+ gherkin (~> 2.2.5)
9
+ json (~> 1.4.6)
10
+ term-ansicolor (~> 1.0.5)
11
+ diff-lcs (1.1.2)
12
+ gemcutter (0.6.1)
13
+ gherkin (2.2.9)
14
+ json (~> 1.4.6)
15
+ term-ansicolor (~> 1.0.5)
16
+ git (1.2.5)
17
+ jeweler (1.4.0)
18
+ gemcutter (>= 0.1.0)
19
+ git (>= 1.2.5)
20
+ rubyforge (>= 2.0.0)
21
+ json (1.4.6)
22
+ json_pure (1.4.6)
23
+ rspec (1.3.1)
24
+ rubyforge (2.0.4)
25
+ json_pure (>= 1.1.7)
26
+ term-ansicolor (1.0.5)
27
+ yard (0.6.1)
28
+
29
+ PLATFORMS
30
+ ruby
31
+
32
+ DEPENDENCIES
33
+ cucumber
34
+ jeweler
35
+ rspec (< 2.0.0)
36
+ yard
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Michael Edgar
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,50 @@
1
+ # Rules
2
+
3
+ We've learned a lot about software development. Time to get serious.
4
+
5
+ ## Commits
6
+
7
+ * In the past our commits weren't really well-factored, or really had structure. This makes reading our history hard.
8
+ * Use hg collapse to collapse several small commits into one logical commit.
9
+ * Branch locally to work on separate features.
10
+
11
+ To do that, clone a local repo to another local repo. Change the branch on that other local repo. Make your commits. Then, in that clone, collapse your commits into something logical. Then, merge your changes into the `default` branch. This will show up as a fast-forward commit. Finally, push those to your main checkout.
12
+
13
+ * One commit, one feature. If your feature doesn't fit in one nicely-sized commit, then prefix your commit message with "PARTIAL:".
14
+ * Until we have our own server, we can't really have code reviews. Until then: every commit that goes through, respond to it with *any* questions you have about the code. If you see nothing wrong, respond "LGTM". That stands for "Looks good to me." Continue this conversation until the other person says "LGTM". Otherwise, consider that commit incomplete.
15
+
16
+ ## Tests
17
+
18
+ * Nothing goes in without a test of some kind. If you need to submit code that is untested, include a justification in your commit.
19
+ * Tests are either small, medium, or large.
20
+ * * Small tests are unit tests. They may need mocks to avoid interaction with other modules.
21
+ * * Medium tests are integration tests. This is testing modules' interactions with each other.
22
+ * * Large tests are functional tests. They test the whole system at once.
23
+
24
+ ## Design
25
+
26
+ * Don't use anonymous arrays/hashes. Use objects. We're in rubyland.
27
+ * Defining classes is cheap. In Java, it'll take you thirty lines of code to make a small struct-like object. In Ruby, it takes us 3 or 4. Go for it! It's better to model the data you're moving around using classes than stuffing it into arrays and whatnot.
28
+ * * Exception: Returning two values can be okay. Justify it and make it crystal clear in the docs.
29
+
30
+ ## Documentation
31
+
32
+ * Use YARD.
33
+ * Don't worry too hard about 1-liners.
34
+ * Document why, not what. If
35
+
36
+ ## Code Style
37
+
38
+ * Strive for 80 columns.
39
+ * Never more than 100 columns.
40
+ * Don't make between 80-100 a habit.
41
+ * Descriptive method names.
42
+ * Parentheses in method declarations unless there are no arguments.
43
+ * Spaces around operators.
44
+ * No mathematic operator overloading. `[]`, `[]=` are fair game though.
45
+
46
+ ## License
47
+
48
+ MIT. See LICENSE. No GPL code is allowed in this codebase.
49
+
50
+ By contributing code to the Amp Project, you agree to license your work under the MIT license.
@@ -0,0 +1,64 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "amp-front"
8
+ gem.summary = %Q{Generic front-end for Amp.}
9
+ gem.description = %Q{The generic front-end used by Amp. May be completely published as a generic library in the future, at which point a name change will be necessary.}
10
+ gem.email = "michael.j.edgar@dartmouth.edu"
11
+ gem.homepage = "http://github.com/michaeledgar/amp-front"
12
+ gem.authors = ["Michael Edgar"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ gem.add_development_dependency "yard", ">= 0"
15
+ gem.add_development_dependency "cucumber", ">= 0"
16
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
+ end
22
+
23
+ require 'spec/rake/spectask'
24
+ Spec::Rake::SpecTask.new(:spec) do |spec|
25
+ spec.libs << 'lib' << 'spec'
26
+ spec.spec_files = FileList['spec/**/*_spec.rb']
27
+ end
28
+
29
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
30
+ spec.libs << 'lib' << 'spec'
31
+ spec.pattern = 'spec/**/*_spec.rb'
32
+ spec.rcov = true
33
+ end
34
+
35
+ require 'rake/testtask'
36
+ Rake::TestTask.new do |t|
37
+ t.libs << "test"
38
+ t.test_files = FileList['test/**/test*.rb']
39
+ t.verbose = true
40
+ end
41
+
42
+ task :spec => :check_dependencies
43
+
44
+ begin
45
+ require 'cucumber/rake/task'
46
+ Cucumber::Rake::Task.new(:features)
47
+
48
+ task :features => :check_dependencies
49
+ rescue LoadError
50
+ task :features do
51
+ abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
52
+ end
53
+ end
54
+
55
+ task :default => [:spec, :test, :features]
56
+
57
+ begin
58
+ require 'yard'
59
+ YARD::Rake::YardocTask.new
60
+ rescue LoadError
61
+ task :yardoc do
62
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
63
+ end
64
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,91 @@
1
+ # Command Dispatch Design Doc
2
+
3
+ This design document intends to describe how commands will be dispatched
4
+ by Amp.
5
+
6
+ The user's invocations are in the form:
7
+
8
+ amp --logging=WARN add --force some_file
9
+ amp config set key value --fiddler="fiddly"
10
+ amp git twiddle --dee="x" --dum="y"
11
+
12
+ ## Command classes
13
+
14
+ Each command is its own class. It must inherit (either directly or indirectly)
15
+ from Amp::Command::Base.
16
+
17
+ Commands are discovered by looking up the class with the name of the requested
18
+ command. Thus, the three amp invocations above would run the following (hypothetical)
19
+ code to create the command object:
20
+
21
+ cmd = Amp::Command::Add.new
22
+ cmd = Amp::Command::Config::Set.new
23
+ cmd = Amp::Command::Git::Twiddle.new
24
+
25
+ ## Command Groups
26
+
27
+ There are a number of commands that will be built into Amp Core: help, add, commit, push,
28
+ and so on. These will always be searched first.
29
+
30
+ Then, we go to command groups. Each plugin comprises a command group, and the Amp::Plugin
31
+ subclass that defines a plugin declares a way to access its commands as a group.
32
+
33
+ Each plugin's command groups are searched using the names provided on the command line, such
34
+ as "config set key value --fiddler='fiddly'". Every plugin (hg, git, server, etc.) will be
35
+ searched for a "config" command, then a "config set" command, then a "commit set key" command,
36
+ and a "config set key value" command. The results are collected, and then only the most
37
+ specific matches are kept. If the user has defined a "default command group", and one of the
38
+ resulting matches is in that default command group, it is moved to the top of the choice list.
39
+ The rest are sorted by how often they have been invoked by the user. This list is presented
40
+ to the user, and the user picks which command they intended.
41
+
42
+ Partial matches *do not match*. Thus, if the user entered:
43
+
44
+ amp config --fiddler='fiddly'
45
+
46
+ Then the following commands would match:
47
+
48
+ Amp::Command::Config
49
+ Amp::Command::GitPlugin::Config
50
+ Amp::Command::HgPlugin::Config
51
+
52
+ But the following commands would not:
53
+
54
+ Amp::Command::Config::Set
55
+ Amp::Command::Config::Get
56
+ Amp::Command::GitPlugin::Config::Set
57
+ Amp::Command::GitPlugin::Config::Get::ByKey
58
+
59
+ ## Command definition
60
+
61
+ A command definition is roughly as follows:
62
+
63
+ command "search" do
64
+ # This command has a repository. Pull in helpers for this common
65
+ # case, and add the --repository (-R) command-line option.
66
+ has_repo
67
+
68
+ # Define options using Trollop syntax
69
+ opt :verbose, "Verbose output", :type => :boolean
70
+ opt :query, "The query to search for", :type => :string
71
+
72
+ # Use macros to quickly add features
73
+ has_paging_options
74
+
75
+ # Add validations
76
+ validates_length_of :query, :in => 4..32
77
+ validates_inclusion_of :query
78
+ validates_has_repository # validates :repository option
79
+
80
+ # specify on_call
81
+ on_call do
82
+ repo = self.repository
83
+ p repo.search(options[:query])
84
+ end
85
+ end
86
+
87
+ When the command runs, its on\_call block is instance\_eval'd by the command
88
+ object. That way, any helper methods defined by the command class can be used
89
+ in the on\_call block. One particularly useful helper for on\_call is "repository"
90
+ which will get the current repository, regardless of type. This is provided by
91
+ amp-core.
@@ -0,0 +1,35 @@
1
+ # Amp Dependancies Design Doc
2
+
3
+ Amp has so far avoided Rubygems dependancies, due to performance concerns.
4
+ Required libraries are included wholesale; so far all such libraires have
5
+ been modified in some way.
6
+
7
+ ## Dependancies
8
+
9
+ ### Maruku
10
+
11
+ Maruku is a pure ruby markdown interpreter.
12
+ Amp uses a highly hacked version of Maruku
13
+ (it adds an output form with ANSI color codes)
14
+ that was trimmed down to try to make it lightweight.
15
+
16
+ ### Trollop
17
+
18
+ Trollop is a command-line parsing library.
19
+ It didn't originally expose its --help behavior,
20
+ which is really nice because it lists options automatically.
21
+ The Amp version exposes the parser object so we can run parser.educate!.
22
+
23
+ ## The Case Against Rubygems
24
+
25
+ Loading Rubygems on each invocation of amp adds at least 150-500ms+ to each invocation.
26
+ With the current plugin architecture, we require users to either use rubygems,
27
+ or individually download each repo and load plugins manually using their Ampfile.
28
+ An installer script down the line could automate the latter option.
29
+
30
+ ### Bundler
31
+
32
+ Bundler (require 'bundler/setup') adds another 400ms+ to startup time.
33
+ A Gemfile is used to streamline gem installation, but it is not used by
34
+ Amp itself.
35
+
@@ -0,0 +1,47 @@
1
+ # Amp Frontend Plugin Design Doc
2
+
3
+ This document intends to specify how plugins will be managed by the generic
4
+ Amp front-end.
5
+
6
+ ## Amp::Plugin Subclasses
7
+
8
+ By tracking the subclasses (and subclasses of subclasses) of Amp::Plugin, we
9
+ can look up all plugins that are loaded. Thus, to register a plugin, one simply
10
+ needs to load a Ruby file that contains the definition of an Amp::Plugin subclass.
11
+
12
+ The list of all plugins can be retrieved with the following Ruby code:
13
+
14
+ Amp::Plugin.all_plugins
15
+
16
+ Which is simply an accessor to an ivar on the Amp::Plugin metaclass. Following
17
+ the Ruby convention of preferring convention over configuration, the default
18
+ Ampfile contains the following, single statement:
19
+
20
+ Amp::Plugin.load_rubygems_plugins
21
+
22
+ which, when run, all gems are searched for the file 'amp/plugin.rb'. All such files
23
+ are loaded in an arbitrary order.
24
+
25
+ If a plugin is automatically loaded that you *do not* wish to be loaded, you
26
+ will have to require each one individually. You do not need to manually load
27
+ amp-core. amp-core will be loaded first.
28
+
29
+ ## Plugin Callbacks
30
+
31
+ Each plugin class in `Amp::Plugin.all_plugins` is instantiated with the current
32
+ global configuration as the sole parameter:
33
+
34
+ class Amp::Plugin::Git < Amp::Plugin::Base
35
+ def initialize(settings)
36
+ puts "oh noez" if settings[:failboatz]
37
+ end
38
+ end
39
+
40
+ Or the simpler:
41
+
42
+ amp_plugin 'git' do
43
+ init do |settings|
44
+ puts "oh noez" if settings[:failboatz]
45
+ end
46
+ end
47
+
@@ -0,0 +1,8 @@
1
+ Feature: Amp command
2
+ In order to see what amp does
3
+ A user will run amp by itself
4
+ to find out what it does
5
+
6
+ Scenario: Running amp with no parameters
7
+ When I run dispatch
8
+ Then I should see "Thanks for using Amp!"
@@ -0,0 +1,36 @@
1
+ Feature: Help Command
2
+ In order to learn to use Amp
3
+ A user can use the help command
4
+ to be delivered useful information
5
+
6
+ Scenario: Running help with no parameters
7
+ Given the argument help
8
+ When I run dispatch
9
+ Then I should see "Thanks for using Amp!"
10
+
11
+ Scenario: Running help commands
12
+ Given the argument help
13
+ And the argument commands
14
+ When I run dispatch
15
+ Then I should see "Prints the help for the program."
16
+ And I should see "^help"
17
+
18
+ Scenario: Running help ampfiles
19
+ Given the argument help
20
+ And the argument ampfiles
21
+ When I run dispatch
22
+ Then I should see "What'?s an Ampfile?"
23
+
24
+ Scenario: Running help new-commands
25
+ Given the argument help
26
+ And the argument new-commands
27
+ When I run dispatch
28
+ Then I should see "commands are the driving force behind"
29
+
30
+ Scenario: Running help help
31
+ Given the argument help
32
+ And the argument help
33
+ When I run dispatch
34
+ Then I should see " --help, -h"
35
+ And I should see "Show this message"
36
+ And I should see "Prints the help for the program."