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 ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ **/.md
3
+ --markup markdown
4
+ --protected
5
+ --private
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 is useful to distribute single-file ruby executables, such as administration scripts, simple tools, etc. Yet allows you to break
10
- files out for easy design, programming and testing.
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
- search_path = [ '/path/to/lib', '/path/to/other/lib' ]
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. Fork it
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
@@ -1,6 +1,11 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
+ require 'yard'
3
4
 
4
5
  RSpec::Core::RakeTask.new(:spec)
5
6
 
6
7
  task :default => :spec
8
+
9
+ YARD::Rake::YardocTask.new do |t|
10
+ t.files = ['lib/**/*.rb', '**/*.md']
11
+ end
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
@@ -1,5 +1,8 @@
1
1
  class Mergit
2
+ # The superclass for all errors raised by {Mergit}
2
3
  class MergitError < StandardError; end
4
+
5
+ # This is raised whenever a required library isn't found.
3
6
  class RequirementNotFound < MergitError; end
4
7
  end
5
8
 
@@ -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 =~ /^\s*require\s+'([^']+)'\s*$/ or line =~ /^\s*require\s+"([^"]+)"\s*$/
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
@@ -1,3 +1,5 @@
1
+ # coding: utf-8
1
2
  class Mergit
2
- VERSION = "0.1.0"
3
+ # The version of the Mergit Library.
4
+ VERSION = "1.0.0"
3
5
  end
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.description = %q{Merge 'require'd files into one file.}
12
- spec.summary = %q{Ever wanted to merge all your 'require'd files into one file for easy distribution? Mergit is your friend!}
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: 0.1.0
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: Merge 'require'd files into one file.
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: -2751772494269357839
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: -2751772494269357839
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: Ever wanted to merge all your 'require'd files into one file for easy distribution?
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: