mergit 0.1.0 → 1.0.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.
- 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:
|