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 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: