mergit 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +5 -0
- data/README.md +52 -12
- data/Rakefile +5 -0
- data/bin/mergit +8 -1
- data/lib/mergit/errors.rb +3 -0
- data/lib/mergit/processor.rb +65 -4
- data/lib/mergit/version.rb +3 -1
- data/lib/mergit.rb +26 -0
- data/mergit.gemspec +5 -3
- data/spec/mergit/processor_spec.rb +38 -0
- metadata +41 -7
data/.yardopts
ADDED
data/README.md
CHANGED
@@ -6,20 +6,28 @@
|
|
6
6
|
|
7
7
|
Mergit is a way to merge a bunch of `require`d files into one file.
|
8
8
|
|
9
|
-
This
|
10
|
-
|
9
|
+
This allows you develop, design, and test your ruby script using normal ruby best practices (rspec, etc.) and then
|
10
|
+
distribute them as a single-file ruby script.
|
11
|
+
|
12
|
+
Some use cases include:
|
13
|
+
|
14
|
+
* Administration scripts
|
15
|
+
* Simple tools
|
16
|
+
* Programs that need to work on any ruby without installing gems
|
11
17
|
|
12
18
|
## Limitations
|
13
19
|
|
14
20
|
Mergit uses simple text processing, therefore it can be tripped up. Some known problems include:
|
15
21
|
|
16
|
-
* `require` statements nested in code instead of at outermost scope of a file.
|
22
|
+
* `require` statements nested in code instead of at outermost scope of a file will expand in-place. This probably isn't what you want.
|
23
|
+
* The order `require`d files are pulled in may be different than ruby.
|
24
|
+
* The replacement feature is very brute force. Be careful using it.
|
17
25
|
|
18
26
|
## Installation
|
19
27
|
|
20
28
|
Add this line to your application's Gemfile:
|
21
29
|
|
22
|
-
gem 'mergit'
|
30
|
+
gem 'mergit', '~> 1.0'
|
23
31
|
|
24
32
|
And then execute:
|
25
33
|
|
@@ -29,6 +37,8 @@ Or install it yourself as:
|
|
29
37
|
|
30
38
|
$ gem install mergit
|
31
39
|
|
40
|
+
Note: Mergit uses [Semantic Versioning](http://semver.org/).
|
41
|
+
|
32
42
|
## Usage
|
33
43
|
|
34
44
|
### Command Line Tool
|
@@ -40,26 +50,56 @@ you want `require`d from.
|
|
40
50
|
|
41
51
|
You can specify the `--lib` flag multiple times.
|
42
52
|
|
53
|
+
There is also a `--replace` flag that lets you specify a string or regular expression (a string surrounded by `/`) that should be replaced.
|
54
|
+
|
55
|
+
Example:
|
56
|
+
|
57
|
+
bin/mergit --replace mouse=cat filename
|
58
|
+
|
59
|
+
This will replace all occurances of "mouse" with "cat".
|
60
|
+
|
61
|
+
You can specify the `--replace` flag multiple times.
|
62
|
+
|
43
63
|
Use the `--output` flag to send the resulting output to someplace other than stdout.
|
44
64
|
|
65
|
+
#### MERGIT directives
|
66
|
+
|
67
|
+
You can also cause any line to be skipped by adding a Mergit directive in a comment at the end of the line.
|
68
|
+
|
69
|
+
Example:
|
70
|
+
|
71
|
+
raise "This won't be in the merged output." # MERGIT: skip
|
72
|
+
|
45
73
|
### Library API
|
46
74
|
|
47
75
|
Simple usage:
|
48
76
|
|
49
|
-
|
50
|
-
|
51
|
-
mergit = Mergit.new(:search_path => search_path)
|
77
|
+
search_path = [ '/path/to/lib', '/path/to/other/lib' ]
|
78
|
+
mergit = Mergit.new(:search_path => search_path)
|
52
79
|
|
53
|
-
string_of_merged_file = mergit.process_file('/path/to/file')
|
54
|
-
# or
|
55
|
-
string_of_merged_string = mergit.process(some_string)
|
80
|
+
string_of_merged_file = mergit.process_file('/path/to/file')
|
81
|
+
# or
|
82
|
+
string_of_merged_string = mergit.process(some_string)
|
56
83
|
|
57
|
-
|
84
|
+
For more detailed information, see the [documentation](http://rubydoc.info/gems/mergit/frames).
|
58
85
|
|
59
86
|
## Contributing
|
60
87
|
|
61
|
-
1
|
88
|
+
### Level 1 -- Apprentice
|
89
|
+
|
90
|
+
File an [issue](https://github.com/docwhat/mergit/issues).
|
91
|
+
|
92
|
+
Make sure it includes the steps needed to reproduce it as well as what you expected to happen.
|
93
|
+
|
94
|
+
### Level 2 -- Journeyman
|
95
|
+
|
96
|
+
1. [Fork it](https://help.github.com/articles/fork-a-repo)
|
62
97
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
63
98
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
64
99
|
4. Push to the branch (`git push origin my-new-feature`)
|
65
100
|
5. Create new Pull Request
|
101
|
+
|
102
|
+
### Level 3 -- Master
|
103
|
+
|
104
|
+
Repeat Level 2 until I give you write access on github. :-)
|
105
|
+
|
data/Rakefile
CHANGED
data/bin/mergit
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# -*- coding: utf-8 -*-
|
3
3
|
|
4
|
-
$: << File.expand_path('../../lib/', __FILE__) if File.directory?(File.expand_path('../../lib/', __FILE__))
|
4
|
+
$: << File.expand_path('../../lib/', __FILE__) if File.directory?(File.expand_path('../../lib/', __FILE__)) # MERGIT: skip
|
5
5
|
require 'mergit'
|
6
6
|
require 'optparse'
|
7
7
|
|
@@ -42,6 +42,13 @@ class MergitProgram
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
opts.on('-r', '--replace MATCH=REPLACEMENT', String,
|
46
|
+
"Replaces all occurrences of MATCH with REPLACEMENT. If MATCH is between / characters, then it'll be used as a case-sensitive regexp. Can be specified multiple times.") do |m|
|
47
|
+
match, replace = m.split(/=/)
|
48
|
+
match = Regexp.new(match.slice(1..-2)) if match.start_with?('/') && match.end_with?('/')
|
49
|
+
mergit.replacements[match] = replace
|
50
|
+
end
|
51
|
+
|
45
52
|
opts.on('-d', '--debug', "Turn on debugging.") do
|
46
53
|
@debug = true
|
47
54
|
end
|
data/lib/mergit/errors.rb
CHANGED
data/lib/mergit/processor.rb
CHANGED
@@ -1,12 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
require 'mergit/errors'
|
2
3
|
require 'pathname'
|
3
4
|
require 'stringio'
|
4
5
|
|
5
6
|
class Mergit
|
7
|
+
# The class that actually does the merge processing.
|
6
8
|
class Processor
|
9
|
+
|
10
|
+
# @return {Array<Pathname>} A frozen array of {http://rubydoc.info/stdlib/pathname/frames Pathname}s
|
11
|
+
attr_reader :search_path
|
12
|
+
|
13
|
+
# @return {Hash} A frozen hash with the rules for replacements.
|
14
|
+
attr_reader :replacements
|
15
|
+
|
16
|
+
# @param [Array<Pathname, String>] search_path The list of directories to search.
|
17
|
+
# @param [Hash] replacements A list of keywords to replace.
|
18
|
+
# @param [Hash] options Either `:filename` or `:string` should be set.
|
7
19
|
def initialize search_path, replacements, options
|
8
|
-
@search_path = search_path.map{|p| Pathname.new p}
|
9
|
-
@replacements = replacements
|
20
|
+
@search_path = search_path.map{|p| Pathname.new p}.freeze
|
21
|
+
@replacements = replacements.freeze
|
10
22
|
@visited_files = []
|
11
23
|
|
12
24
|
@output = StringIO.new
|
@@ -21,6 +33,10 @@ class Mergit
|
|
21
33
|
end
|
22
34
|
end
|
23
35
|
|
36
|
+
# Finds a library using the {#search_path}
|
37
|
+
#
|
38
|
+
# @param [String] lib_name The name of the library to look for.
|
39
|
+
# @return [Nil, Pathname] Returns `nil` if it isn't found or a {http://rubydoc.info/stdlib/pathname/frames Pathname} if it is found.
|
24
40
|
def find_requirement lib_name
|
25
41
|
@search_path.each do |directory|
|
26
42
|
possible_path = directory + "#{lib_name}.rb"
|
@@ -29,15 +45,34 @@ class Mergit
|
|
29
45
|
nil
|
30
46
|
end
|
31
47
|
|
48
|
+
|
49
|
+
# Finds a library using the {#search_path}
|
50
|
+
#
|
51
|
+
# This is identical to {#find_requirement} except it raises {Mergit::RequirementNotFound} if
|
52
|
+
# it fails to find the library.
|
53
|
+
#
|
54
|
+
# @raise [Mergit::RequirementNotFound] if it can't find the library.
|
55
|
+
# @param (see #find_requirement)
|
56
|
+
# @return [Pathname] Returns the {http://rubydoc.info/stdlib/pathname/frames Pathname} of the library.
|
57
|
+
# @see #find_requirement
|
32
58
|
def find_requirement! lib_name
|
33
59
|
find_requirement(lib_name).tap do |retval|
|
34
60
|
raise Mergit::RequirementNotFound.new("Unabled to find require'd file: #{lib_name}") if retval.nil?
|
35
61
|
end
|
36
62
|
end
|
37
63
|
|
64
|
+
## Scans a single line of the file.
|
65
|
+
#
|
66
|
+
# It looks for things that need to be changed, and {#emit}s the resulting
|
67
|
+
# (changed) line.
|
68
|
+
#
|
69
|
+
# @param [String] line The line to parse
|
70
|
+
# @return [Nil]
|
38
71
|
def scan_line line
|
39
72
|
line.chomp!
|
40
|
-
if line =~
|
73
|
+
if line =~ /#\s*MERGIT:\s*skip\s*$/
|
74
|
+
nil # do nothing
|
75
|
+
elsif line =~ /^\s*require\s+'([^']+)'\s*$/ or line =~ /^\s*require\s+"([^"]+)"\s*$/
|
41
76
|
requirement = find_requirement($1)
|
42
77
|
if requirement.nil?
|
43
78
|
emit line
|
@@ -45,10 +80,19 @@ class Mergit
|
|
45
80
|
scan_file requirement
|
46
81
|
end
|
47
82
|
else
|
83
|
+
replacements.each_key do |string_to_replace|
|
84
|
+
line.gsub!(string_to_replace, replacements[string_to_replace])
|
85
|
+
end
|
48
86
|
emit line
|
49
87
|
end
|
50
88
|
end
|
51
89
|
|
90
|
+
## Scans an entire file
|
91
|
+
#
|
92
|
+
# It passes each line of the file to {#scan_line} for parsing.
|
93
|
+
#
|
94
|
+
# @param [Pathname] filename The file to scan.
|
95
|
+
# @return [Nil]
|
52
96
|
def scan_file filename
|
53
97
|
relative_filename = if filename.relative?
|
54
98
|
filename
|
@@ -65,22 +109,39 @@ class Mergit
|
|
65
109
|
emit "### MERGIT: End of '#{relative_filename}'"
|
66
110
|
end
|
67
111
|
|
112
|
+
## Scans a string
|
113
|
+
#
|
114
|
+
# It splits a string up into individual line via {#string_split} and
|
115
|
+
# passes them to {#scan_line}.
|
116
|
+
#
|
117
|
+
# @param [String] string The string to parse.
|
118
|
+
# @return [Nil]
|
68
119
|
def scan string
|
69
120
|
string_split(string).each { |line| scan_line line }
|
70
121
|
end
|
71
122
|
|
123
|
+
## Split a string into lines.
|
124
|
+
#
|
125
|
+
# @param [String] string The string to split into lines.
|
126
|
+
# @return [Array<String>] The split up string.
|
72
127
|
def string_split string
|
73
128
|
string.split(/\n|\r\n/)
|
74
129
|
end
|
75
130
|
|
131
|
+
## The resulting processed output.
|
132
|
+
#
|
133
|
+
# @return [String]
|
76
134
|
def output
|
77
135
|
@output.close unless @output.closed?
|
78
136
|
@final_output ||= @output.string
|
79
137
|
end
|
80
138
|
|
139
|
+
## Sends a string to the {#output}
|
140
|
+
#
|
141
|
+
# @param [String] string The string to send to {#output}.
|
142
|
+
# @return [Nil]
|
81
143
|
def emit string
|
82
144
|
@output.puts string
|
83
145
|
end
|
84
|
-
|
85
146
|
end
|
86
147
|
end
|
data/lib/mergit/version.rb
CHANGED
data/lib/mergit.rb
CHANGED
@@ -1,8 +1,13 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
require 'mergit/version'
|
2
3
|
require 'mergit/processor'
|
3
4
|
require 'mergit/errors'
|
4
5
|
|
6
|
+
# A class for merging in `require`ments.
|
5
7
|
class Mergit
|
8
|
+
# List of attributes accepted by {Mergit}, and the default values.
|
9
|
+
#
|
10
|
+
# @return [Hash]
|
6
11
|
ATTRIBUTES = {
|
7
12
|
:search_path => [Dir.pwd],
|
8
13
|
:replacements => {},
|
@@ -12,6 +17,9 @@ class Mergit
|
|
12
17
|
attr_accessor attr
|
13
18
|
end
|
14
19
|
|
20
|
+
# Create a new mergit instance.
|
21
|
+
#
|
22
|
+
# @param [Hash] options See {ATTRIBUTES} for the list of options you can pass in.
|
15
23
|
def initialize options=nil
|
16
24
|
final_options = options ? ATTRIBUTES.merge(options) : ATTRIBUTES
|
17
25
|
|
@@ -20,6 +28,10 @@ class Mergit
|
|
20
28
|
end
|
21
29
|
end
|
22
30
|
|
31
|
+
# Merge a file
|
32
|
+
#
|
33
|
+
# @param [Pathname, String] filename The name of the file to merge.
|
34
|
+
# @return [String] The merged file.
|
23
35
|
def process_file filename
|
24
36
|
if File.file? filename
|
25
37
|
create_file_processor(filename).output
|
@@ -28,6 +40,10 @@ class Mergit
|
|
28
40
|
end
|
29
41
|
end
|
30
42
|
|
43
|
+
# Merge a string
|
44
|
+
#
|
45
|
+
# @param [String] string The text that should be merged.
|
46
|
+
# @return [String] The merged output.
|
31
47
|
def process string
|
32
48
|
create_string_processor(string).output
|
33
49
|
end
|
@@ -38,10 +54,20 @@ class Mergit
|
|
38
54
|
|
39
55
|
private
|
40
56
|
|
57
|
+
# Helper to create a string processor
|
58
|
+
#
|
59
|
+
# @param [String] string The string to merge.
|
60
|
+
# @return [Processor]
|
61
|
+
# @!visibility private
|
41
62
|
def create_string_processor string
|
42
63
|
Processor.new(search_path, replacements, :string => string)
|
43
64
|
end
|
44
65
|
|
66
|
+
# Helper to create a file processor
|
67
|
+
#
|
68
|
+
# @param [Pathname, String] filename The file to process
|
69
|
+
# @return [Processor]
|
70
|
+
# @!visibility private
|
45
71
|
def create_file_processor filename
|
46
72
|
Processor.new(search_path, replacements, :filename => filename)
|
47
73
|
end
|
data/mergit.gemspec
CHANGED
@@ -8,9 +8,9 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Mergit::VERSION
|
9
9
|
spec.authors = ["Christian Höltje"]
|
10
10
|
spec.email = ["docwhat@gerf.org"]
|
11
|
-
spec.
|
12
|
-
spec.
|
13
|
-
spec.homepage = ""
|
11
|
+
spec.summary = %q{Merge 'require'd files into one file.}
|
12
|
+
spec.description = %q{Ever wanted to merge all your 'require'd files into one file for easy distribution? Mergit is your friend!}
|
13
|
+
spec.homepage = "https://github.com/docwhat/mergit"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files`.split($/)
|
@@ -20,6 +20,8 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.3"
|
22
22
|
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "yard", "~> 0.8.5"
|
24
|
+
spec.add_development_dependency "redcarpet", "~> 2.2"
|
23
25
|
spec.add_development_dependency "rspec", "~> 2.13"
|
24
26
|
spec.add_development_dependency "coveralls", "~> 0.6"
|
25
27
|
spec.add_development_dependency "guard-rspec", "~> 2.5"
|
@@ -122,6 +122,44 @@ describe Mergit::Processor do
|
|
122
122
|
subject.should_receive(:scan_file).with(Pathname.new('../../../lib/mergit/version.rb').expand_path(__FILE__)).once
|
123
123
|
end
|
124
124
|
end
|
125
|
+
|
126
|
+
context "given a line with MERGIT: skip" do
|
127
|
+
let(:ruby_string) { "this should never be seen # MERGIT: skip " }
|
128
|
+
after { subject.scan_line ruby_string }
|
129
|
+
|
130
|
+
it "should not call emit" do
|
131
|
+
subject.should_not_receive(:emit)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
context "with a replacements" do
|
136
|
+
let(:replacements) do
|
137
|
+
{
|
138
|
+
'VERSION' => '1.2.3',
|
139
|
+
/PROG\s*NAME/ => 'Awesome Program',
|
140
|
+
}
|
141
|
+
end
|
142
|
+
|
143
|
+
context "matching on a string" do
|
144
|
+
let(:ruby_string) { "puts 'The version is VERSION'" }
|
145
|
+
let(:expected_string) { ruby_string.sub('VERSION', '1.2.3') }
|
146
|
+
after { subject.scan_line ruby_string }
|
147
|
+
|
148
|
+
it "should replace VERSION" do
|
149
|
+
subject.should_receive(:emit).with(expected_string)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context "matching on a regexp" do
|
154
|
+
let(:ruby_string) { "puts 'The program is PROGNAME.'" }
|
155
|
+
let(:expected_string) { ruby_string.sub('PROGNAME', 'Awesome Program') }
|
156
|
+
after { subject.scan_line ruby_string }
|
157
|
+
|
158
|
+
it "should replace PROGNAME" do
|
159
|
+
subject.should_receive(:emit).with(expected_string)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
125
163
|
end
|
126
164
|
|
127
165
|
describe "string_split" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mergit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -43,6 +43,38 @@ dependencies:
|
|
43
43
|
- - ~>
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '10.0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: yard
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.8.5
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.8.5
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: redcarpet
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '2.2'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '2.2'
|
46
78
|
- !ruby/object:Gem::Dependency
|
47
79
|
name: rspec
|
48
80
|
requirement: !ruby/object:Gem::Requirement
|
@@ -187,7 +219,8 @@ dependencies:
|
|
187
219
|
- - ~>
|
188
220
|
- !ruby/object:Gem::Version
|
189
221
|
version: '1.5'
|
190
|
-
description:
|
222
|
+
description: Ever wanted to merge all your 'require'd files into one file for easy
|
223
|
+
distribution? Mergit is your friend!
|
191
224
|
email:
|
192
225
|
- docwhat@gerf.org
|
193
226
|
executables:
|
@@ -198,6 +231,7 @@ files:
|
|
198
231
|
- .gitignore
|
199
232
|
- .rspec
|
200
233
|
- .travis.yml
|
234
|
+
- .yardopts
|
201
235
|
- Gemfile
|
202
236
|
- Guardfile
|
203
237
|
- LICENSE.txt
|
@@ -215,7 +249,7 @@ files:
|
|
215
249
|
- spec/mergit/processor_spec.rb
|
216
250
|
- spec/mergit_spec.rb
|
217
251
|
- spec/spec_helper.rb
|
218
|
-
homepage:
|
252
|
+
homepage: https://github.com/docwhat/mergit
|
219
253
|
licenses:
|
220
254
|
- MIT
|
221
255
|
post_install_message:
|
@@ -230,7 +264,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
230
264
|
version: '0'
|
231
265
|
segments:
|
232
266
|
- 0
|
233
|
-
hash:
|
267
|
+
hash: 3687644075462403820
|
234
268
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
235
269
|
none: false
|
236
270
|
requirements:
|
@@ -239,14 +273,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
239
273
|
version: '0'
|
240
274
|
segments:
|
241
275
|
- 0
|
242
|
-
hash:
|
276
|
+
hash: 3687644075462403820
|
243
277
|
requirements: []
|
244
278
|
rubyforge_project:
|
245
279
|
rubygems_version: 1.8.23
|
246
280
|
signing_key:
|
247
281
|
specification_version: 3
|
248
|
-
summary:
|
249
|
-
Mergit is your friend!
|
282
|
+
summary: Merge 'require'd files into one file.
|
250
283
|
test_files:
|
251
284
|
- spec/examples/loop/a.rb
|
252
285
|
- spec/examples/loop/b.rb
|
@@ -254,3 +287,4 @@ test_files:
|
|
254
287
|
- spec/mergit/processor_spec.rb
|
255
288
|
- spec/mergit_spec.rb
|
256
289
|
- spec/spec_helper.rb
|
290
|
+
has_rdoc:
|