duct 0.0.0 → 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f20e93081ed84a57df274ce99797d4aba409c43e
4
+ data.tar.gz: f075c2159472292ee1cb0f6cae1927b06cb2a8a8
5
+ SHA512:
6
+ metadata.gz: 0b8619568bf00163da213ee8bd4ea5480d582101b9dcc7916fb9a4cca4032126682f27488b23694f2897d274b8dd8a0710adea06ee9ae4c7616ec0c8740c4fc8
7
+ data.tar.gz: 76cde8fe434e75b2101bf13d92cc3a96d095141aeb08a6cd78017c6f09d56d5f7c6627660ef8f8810638a62df88d8f5d079f79c6b6c3f198e1ca80a8130aaa67
data/README.md CHANGED
@@ -1,24 +1,84 @@
1
1
  # Duct
2
2
 
3
- TODO: Write a gem description
3
+ ![Duct Tape, By Evan-Amos, via Wikimedia Commons](http://upload.wikimedia.org/wikipedia/commons/thumb/8/89/Duct-tape.jpg/256px-Duct-tape.jpg)
4
4
 
5
- ## Installation
5
+ Duct is a small wrapper around [bundler](http://bundler.io/). It allows you to embed a Gemfile in a script.
6
6
 
7
- Add this line to your application's Gemfile:
7
+ ## Usecase
8
8
 
9
- gem 'duct'
9
+ Sometimes you write small scripts to do different tasks. Sometimes you store them for future use. Sometimes those
10
+ scripts use some gems to do their task (database access, web requests, whatever). So, after some time, you reuse them
11
+ and you don't know which version of which gems did you use and you can run into trouble (like with full fledged apps in
12
+ the pre-bundler days). That's when you do the obvious workaround: create a directory to store the script together with a
13
+ `Gemfile` and a `Gemfile.lock`.
10
14
 
11
- And then execute:
15
+ That's a good solution but feels a bit overkill. Duct allows you to embed the `Gemfile` (and the `Gemfile.lock`) in the same file, so it's a single file to store together with others, as a [gist](https://gist.github.com/), etc.
12
16
 
13
- $ bundle
17
+ ## Installation
14
18
 
15
- Or install it yourself as:
19
+ Duct is a command line utility, so just install it via rubygems:
16
20
 
17
21
  $ gem install duct
18
22
 
19
23
  ## Usage
20
24
 
21
- TODO: Write usage instructions here
25
+ First, write your `Gemfile` in the data section of your script, after the `__END__` label, and after a `@@ Gemfile`
26
+ label:
27
+
28
+ ```ruby
29
+ require 'sinatra'
30
+
31
+ get '/' do
32
+ 'Hello, world!'
33
+ end
34
+
35
+ __END__
36
+ @@ Gemfile
37
+ source 'https://rubygems.org'
38
+
39
+ gem 'sinatra'
40
+ ```
41
+
42
+ (this is very much like the [Sinatra inline templates](http://www.sinatrarb.com/intro.html#Inline%20Templates), and can
43
+ in fact be combined with them).
44
+
45
+ Then, run the script using the `duct` command:
46
+
47
+ ```
48
+ $ duct my_script.rb
49
+ ```
50
+
51
+ The default action is running the script, checking previously whether dependencies are met, and installing them (via
52
+ `bundle install`) if needed. Notice that your script data section will be updated with a section for the `Gemfile.lock`
53
+ if needed (beware of conflicts with your editor while you develop the script).
54
+
55
+ ### Passing parameters
56
+
57
+ If your script expects parameters, just pass them after the filename:
58
+
59
+ ```
60
+ $ duct my_script.rb param1 param2
61
+ ```
62
+
63
+ ### Updating the bundle
64
+
65
+ You can run any of the `bundle` subcommands (mainly `update`, with or without a gem name, but also `outdated`, `check`,
66
+ `list`, `show` and [all the rest](http://bundler.io/v1.5/man/bundle.1.html#PRIMARY-COMMANDS)) passing them *before* the
67
+ filename:
68
+
69
+ ```
70
+ $ duct update sinatra my_script.rb
71
+ ```
72
+
73
+ Remember that updating the bundle will update the `Gemfile.lock` section in your script, so remember to save those
74
+ changes.
75
+
76
+ ### Using the script data
77
+
78
+ Duct will ignore the part of the data section **before** the first `@@ XYZ` label (and the sections with other labels
79
+ than `@@ Gemfile` and `@@ Gemfile.lock`, as it was mentioned for the Sinatra inline templates case), so you can still
80
+ use that ruby feature, but you will need to make sure your script handles and ignores the `Gemfile` and `Gemfile.lock`
81
+ sections, of course.
22
82
 
23
83
  ## Contributing
24
84
 
@@ -27,3 +87,7 @@ TODO: Write usage instructions here
27
87
  3. Commit your changes (`git commit -am 'Add some feature'`)
28
88
  4. Push to the branch (`git push origin my-new-feature`)
29
89
  5. Create new Pull Request
90
+
91
+ ## License
92
+
93
+ Released under the MIT License, Copyright (c) 2014 Sergio Gil.
data/Rakefile CHANGED
@@ -1 +1,7 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
data/bin/duct ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path('../../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+
6
+ require 'duct/cli'
7
+
8
+ Duct::Cli.new(ARGV.dup).run
data/duct.gemspec CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Duct::VERSION
9
9
  spec.authors = ["Sergio Gil"]
10
10
  spec.email = ["sgilperez@gmail.com"]
11
- spec.description = %q{duct tape}
12
- spec.summary = %q{duct tape}
11
+ spec.description = %q{Embeds a Gemfile in a script}
12
+ spec.summary = %q{Embeds a Gemfile in a script}
13
13
  spec.homepage = "https://github.com/porras/duct"
14
14
  spec.license = "MIT"
15
15
 
@@ -18,6 +18,9 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
+ spec.add_dependency "bundler"
22
+
21
23
  spec.add_development_dependency "bundler", "~> 1.3"
22
24
  spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec"
23
26
  end
@@ -0,0 +1,32 @@
1
+ require 'sinatra'
2
+
3
+ get '/' do
4
+ @name = params[:name] || 'world'
5
+ erb :index
6
+ end
7
+
8
+ __END__
9
+ @@ Gemfile
10
+ source 'https://rubygems.org'
11
+
12
+ gem 'sinatra'
13
+ @@ Gemfile.lock
14
+ GEM
15
+ remote: https://rubygems.org/
16
+ specs:
17
+ rack (1.5.2)
18
+ rack-protection (1.5.1)
19
+ rack
20
+ sinatra (1.4.4)
21
+ rack (~> 1.4)
22
+ rack-protection (~> 1.4)
23
+ tilt (~> 1.3, >= 1.3.4)
24
+ tilt (1.4.1)
25
+
26
+ PLATFORMS
27
+ ruby
28
+
29
+ DEPENDENCIES
30
+ sinatra
31
+ @@ index
32
+ Hello, <%= @name %>!
data/lib/duct.rb CHANGED
@@ -1,5 +1,3 @@
1
- require "duct/version"
2
-
3
- module Duct
4
- # Your code goes here...
5
- end
1
+ require 'duct/version'
2
+ require 'duct/runner'
3
+ require 'duct/cli'
data/lib/duct/cli.rb ADDED
@@ -0,0 +1,58 @@
1
+ require 'duct/runner'
2
+
3
+ module Duct
4
+ class Cli
5
+ BUNDLER_COMMANDS = %w{
6
+ update
7
+ check
8
+ list
9
+ show
10
+ outdated
11
+ console
12
+ open
13
+ viz
14
+ }
15
+
16
+ USAGE = %Q{Commands:
17
+ duct myscript.rb [param1 param2] # Installs bundle included in myscript.rb if needed and
18
+ # runs myscript with optional parameters.
19
+ duct update sinatra myscript.rb # Runs specified bundler command on the bundled included
20
+ # in myscript, passing the additional parameters. Available
21
+ # commands: #{BUNDLER_COMMANDS.join(', ')}.
22
+ # Run bundle help for more info.}
23
+
24
+ def initialize(args)
25
+ @args = args
26
+ end
27
+
28
+ def filename
29
+ command ? @args.last : @args.first
30
+ end
31
+
32
+ def command
33
+ @args[0..-2].join(' ') if BUNDLER_COMMANDS.include?(@args.first)
34
+ end
35
+
36
+ def params
37
+ command ? [] : @args[1..-1]
38
+ end
39
+
40
+ def error?
41
+ !(command || filename)
42
+ end
43
+
44
+ def run
45
+ if error?
46
+ puts(USAGE)
47
+ exit(-1)
48
+ end
49
+ runner.run
50
+ end
51
+
52
+ private
53
+
54
+ def runner
55
+ @runner ||= Duct::Runner.new(self)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,59 @@
1
+ require 'tmpdir'
2
+ require 'stringio'
3
+
4
+ module Duct
5
+ class Runner
6
+ def initialize(config)
7
+ @config = config
8
+ @script, @data = File.read(@config.filename).split(/^__END__$/, 2)
9
+ @preamble = @data.split(/^@@\s*(.*\S)\s*$/, 2).first
10
+ end
11
+
12
+ def run
13
+ copy_embedded_files
14
+ system "BUNDLE_GEMFILE=#{tempdir}/Gemfile bundle #{@config.command}"
15
+ update_embedded_files
16
+ exec "BUNDLE_GEMFILE=#{tempdir}/Gemfile bundle exec ruby #{@config.filename} #{@config.params.join(' ')}; rm -rf #{tempdir}" unless @config.command
17
+ end
18
+
19
+ private
20
+
21
+ def copy_embedded_files
22
+ embedded_files.each do |file, contents|
23
+ IO.write("#{tempdir}/#{file}", contents)
24
+ end
25
+ end
26
+
27
+ def update_embedded_files
28
+ contents = @script.dup
29
+
30
+ contents << "__END__"
31
+ contents << @preamble
32
+
33
+ Dir.glob("#{tempdir}/*") do |filename|
34
+ contents << "@@ #{File.basename(filename)}\n"
35
+ contents << File.read(filename)
36
+ end
37
+
38
+ IO.write(@config.filename, contents)
39
+ end
40
+
41
+ def embedded_files
42
+ @embedded_files ||= {}.tap do |files|
43
+ file = nil
44
+ @data.each_line do |line|
45
+ if line =~ /^@@\s*(.*\S)\s*$/
46
+ file = ''
47
+ files[$1.to_sym] = file
48
+ elsif file
49
+ file << line
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ def tempdir
56
+ @tempdir ||= Dir.mktmpdir
57
+ end
58
+ end
59
+ end
data/lib/duct/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Duct
2
- VERSION = "0.0.0"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -0,0 +1,3 @@
1
+ RSpec.configure do |config|
2
+ config.color_enabled = true
3
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+ require 'duct/cli'
3
+
4
+ describe Duct::Cli do
5
+ subject { Duct::Cli.new(argv) }
6
+
7
+ context 'just filename' do
8
+ let(:argv) { ['myscript.rb'] }
9
+
10
+ its(:filename) { should == 'myscript.rb' }
11
+ its(:command) { should be_nil }
12
+ its(:params) { should be_empty }
13
+ its(:error?) { should be_false }
14
+ end
15
+
16
+ context 'filename and params' do
17
+ let(:argv) { ['myscript.rb', 'param1', 'param2'] }
18
+
19
+ its(:filename) { should == 'myscript.rb' }
20
+ its(:command) { should be_nil }
21
+ its(:params) { should == ['param1', 'param2'] }
22
+ its(:error?) { should be_false }
23
+ end
24
+
25
+ context 'command and filename' do
26
+ let(:argv) { ['update', 'myscript.rb'] }
27
+
28
+ its(:filename) { should == 'myscript.rb' }
29
+ its(:command) { should == 'update' }
30
+ its(:params) { should be_empty }
31
+ its(:error?) { should be_false }
32
+
33
+ context 'with params for the command' do
34
+ let(:argv) { ['update', 'sinatra', 'myscript.rb'] }
35
+
36
+ its(:filename) { should == 'myscript.rb' }
37
+ its(:command) { should == 'update sinatra' }
38
+ its(:params) { should be_empty }
39
+ its(:error?) { should be_false }
40
+ end
41
+ end
42
+
43
+ context 'errors' do
44
+ context 'nothing' do
45
+ let(:argv) { [] }
46
+
47
+ its(:error?) { should be_true }
48
+ end
49
+ end
50
+ end
metadata CHANGED
@@ -1,20 +1,32 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: duct
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
5
- prerelease:
4
+ version: 0.1.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Sergio Gil
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2014-01-05 00:00:00.000000000 Z
11
+ date: 2014-01-13 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: bundler
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
18
30
  requirements:
19
31
  - - ~>
20
32
  - !ruby/object:Gem::Version
@@ -22,7 +34,6 @@ dependencies:
22
34
  type: :development
23
35
  prerelease: false
24
36
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
37
  requirements:
27
38
  - - ~>
28
39
  - !ruby/object:Gem::Version
@@ -30,23 +41,36 @@ dependencies:
30
41
  - !ruby/object:Gem::Dependency
31
42
  name: rake
32
43
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
44
  requirements:
35
- - - ! '>='
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
36
60
  - !ruby/object:Gem::Version
37
61
  version: '0'
38
62
  type: :development
39
63
  prerelease: false
40
64
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
65
  requirements:
43
- - - ! '>='
66
+ - - '>='
44
67
  - !ruby/object:Gem::Version
45
68
  version: '0'
46
- description: duct tape
69
+ description: Embeds a Gemfile in a script
47
70
  email:
48
71
  - sgilperez@gmail.com
49
- executables: []
72
+ executables:
73
+ - duct
50
74
  extensions: []
51
75
  extra_rdoc_files: []
52
76
  files:
@@ -55,33 +79,39 @@ files:
55
79
  - LICENSE.txt
56
80
  - README.md
57
81
  - Rakefile
82
+ - bin/duct
58
83
  - duct.gemspec
84
+ - examples/sinatra.rb
59
85
  - lib/duct.rb
86
+ - lib/duct/cli.rb
87
+ - lib/duct/runner.rb
60
88
  - lib/duct/version.rb
89
+ - spec/spec_helper.rb
90
+ - spec/unit/cli_spec.rb
61
91
  homepage: https://github.com/porras/duct
62
92
  licenses:
63
93
  - MIT
94
+ metadata: {}
64
95
  post_install_message:
65
96
  rdoc_options: []
66
97
  require_paths:
67
98
  - lib
68
99
  required_ruby_version: !ruby/object:Gem::Requirement
69
- none: false
70
100
  requirements:
71
- - - ! '>='
101
+ - - '>='
72
102
  - !ruby/object:Gem::Version
73
103
  version: '0'
74
104
  required_rubygems_version: !ruby/object:Gem::Requirement
75
- none: false
76
105
  requirements:
77
- - - ! '>='
106
+ - - '>='
78
107
  - !ruby/object:Gem::Version
79
108
  version: '0'
80
109
  requirements: []
81
110
  rubyforge_project:
82
- rubygems_version: 1.8.23
111
+ rubygems_version: 2.0.3
83
112
  signing_key:
84
- specification_version: 3
85
- summary: duct tape
86
- test_files: []
87
- has_rdoc:
113
+ specification_version: 4
114
+ summary: Embeds a Gemfile in a script
115
+ test_files:
116
+ - spec/spec_helper.rb
117
+ - spec/unit/cli_spec.rb