kitc 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.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ group :development do
4
+ gem "rspec"
5
+ gem "rdoc"
6
+ gem "bundler"
7
+ gem "jeweler"
8
+ gem "rcov"
9
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013 Matthew McMillion
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,32 @@
1
+ KitC
2
+ ===============================================================================
3
+
4
+ A Command-Line Compiler and Library for The Kit Language
5
+ -------------------------------------------------------------------------------
6
+
7
+ **KitC** makes it easy to quickly compile Kit files written in [The Kit Language](http://incident57.com/codekit/kit.php):
8
+
9
+ $ gem install kitc
10
+ $ kitc index.kit
11
+
12
+ By default, **KitC** compiles the Kit file to the same location as the source. This can be overridden by specifying `--output-dir`:
13
+
14
+ $ kitc --output-dir /web/ index.kit
15
+
16
+ Feel free to queue up as many files as needed. **KitC** will parse them one at a time:
17
+
18
+ $ kitc index.kit bio.kit about.kit
19
+
20
+ **KitC** doesn't automatically clobber existing files. If you want, you can specify to overwrite output files:
21
+
22
+ $ kitc --overwrite index.kit
23
+
24
+ If piping is your thing, **KitC** can also output to Standard Out:
25
+
26
+ $ kitc --stdout index.kit > index.html
27
+
28
+ **KitC** is packaged as a Ruby module with a Ruby front-end executable. If you're Ruby-savvy, you can use **KitC** programmatically in your programs or Rake tasks:
29
+
30
+ require "kitc"
31
+
32
+ output = KitC.parse(path_to_file)
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+
3
+ require 'lib/kitc/version'
4
+ require 'rubygems'
5
+ require 'bundler'
6
+ begin
7
+ Bundler.setup(:default, :development)
8
+ rescue Bundler::BundlerError => e
9
+ $stderr.puts e.message
10
+ $stderr.puts "Run `bundle install` to install missing gems"
11
+ exit e.status_code
12
+ end
13
+ require 'rake'
14
+
15
+ require 'jeweler'
16
+ Jeweler::Tasks.new do |gem|
17
+ gem.name = "kitc"
18
+ gem.version = KitC::Version::STRING
19
+ gem.homepage = "http://github.com/MatthewMcMillion/kitc"
20
+ gem.license = "MIT"
21
+ gem.summary = %Q{Kit Language Compiler}
22
+ gem.description = %Q{Compiles (Processes) HTML files written with the Kit language}
23
+ gem.email = "matthew@bitpivot.com"
24
+ gem.authors = ["Matthew McMillion"]
25
+ gem.executables = 'kitc'
26
+ end
27
+ Jeweler::RubygemsDotOrgTasks.new
28
+
29
+ require 'rspec/core'
30
+ require 'rspec/core/rake_task'
31
+ RSpec::Core::RakeTask.new(:spec) do |spec|
32
+ spec.pattern = FileList['spec/**/*_spec.rb']
33
+ end
34
+
35
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
36
+ spec.pattern = 'spec/**/*_spec.rb'
37
+ spec.rcov = true
38
+ end
39
+
40
+ task :default => :spec
41
+
42
+ require 'rdoc/task'
43
+ Rake::RDocTask.new do |rdoc|
44
+ version = KitC::Version::STRING
45
+
46
+ rdoc.rdoc_dir = 'rdoc'
47
+ rdoc.title = "kitc #{version}"
48
+ rdoc.rdoc_files.include('README*')
49
+ rdoc.rdoc_files.include('lib/**/*.rb')
50
+ rdoc.rdoc_files.exclude('lib/**/trollop.rb')
51
+ end
data/bin/kitc ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Add self to libpath
4
+ $:.unshift File.expand_path("../../lib", __FILE__)
5
+ require "kitc"
6
+
7
+ opts = Trollop::options do
8
+ version "KitC #{KitC::Version::STRING}"
9
+ banner "Usage: kitc --output_dir <output> <input_file_1> <input_file_2>"
10
+ opt :output_dir, "Output directory", :type => String
11
+ opt :stdout, "Send output to standard out"
12
+ opt :overwrite, "Overwrite output files if they exist"
13
+ end
14
+
15
+ # Blow up if no input files were supplied
16
+ Trollop::die "At least one input file is required" if ARGV.empty?
17
+
18
+ ARGV.each do |in_file|
19
+ # Stdout
20
+ if opts[:stdout]
21
+ puts KitC.parse(in_file)
22
+
23
+ # Write file
24
+ elsif
25
+ out_filename = File.basename(in_file, ".*") + ".html"
26
+ out_filepath = ""
27
+ if opts[:output_dir]
28
+ out_filepath = File.expand_path(File.join(opts[:output_dir], out_filename))
29
+ else
30
+ out_filepath = File.expand_path(File.join(File.dirname(in_file), out_filename))
31
+ end
32
+
33
+ # If the file exists and overwrite wasn't specified, blow up
34
+ abort "ERROR: '#{out_filepath}' already exists and --overwrite was not specified." if (File.exists? out_filepath) && !opts[:overwrite]
35
+
36
+ puts "Compiling '#{in_file}'..."
37
+ File.open(out_filepath, "w") { |f| f.write(KitC.parse(in_file))}
38
+ end
39
+ end
data/kitc.gemspec ADDED
@@ -0,0 +1,73 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "kitc"
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Matthew McMillion"]
12
+ s.date = "2013-03-23"
13
+ s.description = "Compiles (Processes) HTML files written with the Kit language"
14
+ s.email = "matthew@bitpivot.com"
15
+ s.executables = ["kitc"]
16
+ s.extra_rdoc_files = [
17
+ "LICENSE.txt",
18
+ "README.md"
19
+ ]
20
+ s.files = [
21
+ ".document",
22
+ ".rspec",
23
+ "Gemfile",
24
+ "LICENSE.txt",
25
+ "README.md",
26
+ "Rakefile",
27
+ "bin/kitc",
28
+ "kitc.gemspec",
29
+ "lib/kitc.rb",
30
+ "lib/kitc/base.rb",
31
+ "lib/kitc/trollop.rb",
32
+ "lib/kitc/version.rb",
33
+ "spec/base_spec.rb",
34
+ "spec/spec_helper.rb",
35
+ "test/assets/_lorem.kit",
36
+ "test/assets/_sosad.html",
37
+ "test/assets/fish.kit",
38
+ "test/assets/sub/sub.kit",
39
+ "test/assets/sub/sub2/test.html",
40
+ "test/assets/test.kit",
41
+ "test/test.html"
42
+ ]
43
+ s.homepage = "http://github.com/MatthewMcMillion/kitc"
44
+ s.licenses = ["MIT"]
45
+ s.require_paths = ["lib"]
46
+ s.rubygems_version = "1.8.25"
47
+ s.summary = "Kit Language Compiler"
48
+
49
+ if s.respond_to? :specification_version then
50
+ s.specification_version = 3
51
+
52
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
53
+ s.add_development_dependency(%q<rspec>, [">= 0"])
54
+ s.add_development_dependency(%q<rdoc>, [">= 0"])
55
+ s.add_development_dependency(%q<bundler>, [">= 0"])
56
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
57
+ s.add_development_dependency(%q<rcov>, [">= 0"])
58
+ else
59
+ s.add_dependency(%q<rspec>, [">= 0"])
60
+ s.add_dependency(%q<rdoc>, [">= 0"])
61
+ s.add_dependency(%q<bundler>, [">= 0"])
62
+ s.add_dependency(%q<jeweler>, [">= 0"])
63
+ s.add_dependency(%q<rcov>, [">= 0"])
64
+ end
65
+ else
66
+ s.add_dependency(%q<rspec>, [">= 0"])
67
+ s.add_dependency(%q<rdoc>, [">= 0"])
68
+ s.add_dependency(%q<bundler>, [">= 0"])
69
+ s.add_dependency(%q<jeweler>, [">= 0"])
70
+ s.add_dependency(%q<rcov>, [">= 0"])
71
+ end
72
+ end
73
+
data/lib/kitc/base.rb ADDED
@@ -0,0 +1,174 @@
1
+ module KitC
2
+
3
+ public
4
+
5
+
6
+
7
+ # Begin parsing a Kit file
8
+ #
9
+ def self.parse(file)
10
+ filepath = from_fuzzy_path(file)
11
+ kit_error("File not found", file) unless filepath
12
+ import(filepath)
13
+ end
14
+
15
+
16
+
17
+ private
18
+
19
+
20
+
21
+ @current_offset # Hold current offset when parsing a Kit file
22
+
23
+
24
+
25
+ # Fuzzily get an import file's path
26
+ #
27
+ def self.from_fuzzy_path(filepath)
28
+ filepath = File.expand_path(filepath)
29
+
30
+ return filepath if FileTest.exists?(filepath)
31
+
32
+ # Assume .kit extension
33
+ assume_kit = "#{filepath}.kit"
34
+ return assume_kit if FileTest.exists?(assume_kit)
35
+
36
+ # Assume Sass partial
37
+ sass_partial = sass_partial(filepath)
38
+ return sass_partial if FileTest.exists?(sass_partial)
39
+
40
+ # Assume Sass partial and .kit extension
41
+ sass_partial_kit = sass_partial(assume_kit)
42
+ return sass_partial_kit if FileTest.exists?(sass_partial_kit)
43
+
44
+ return nil
45
+ end
46
+
47
+
48
+
49
+ # Performs a file import on one or more files
50
+ #
51
+ def self.import(filepaths, imports = Array.new, variables = Hash.new)
52
+ result = ""
53
+
54
+ filepaths.each do |filepath|
55
+ scope_dir(filepath) do
56
+ # If the file is a Kit parse it, otherwise just get contents
57
+ if File.extname(filepath).downcase == ".kit"
58
+ result += parse_kit(filepath, imports, variables)
59
+ else
60
+ result += File.read(filepath)
61
+ end
62
+
63
+ result += "\n" unless filepath == filepaths[-1] # Don't add a newline to last import
64
+ end
65
+ end
66
+
67
+ return result
68
+ end
69
+
70
+
71
+
72
+ # Parse a file for Kit tags
73
+ #
74
+ def self.parse_kit(filepath, imports, variables)
75
+ # Each recursion gets its own verison of imports and variables
76
+ imports = imports.dup
77
+ variables = variables.dup
78
+
79
+ scope_dir(filepath) do
80
+ # Blow up with the parent import if this file has already been imported
81
+ kit_error("Recursion error", imports[-1]) if imports.include? filepath
82
+
83
+ result = File.read(filepath)
84
+ imports << filepath
85
+
86
+ # Find and parse all Kit tags
87
+ result.gsub!(/<!--\s*(?:@|\$)(.*)-->/) do
88
+ tag = $1.strip.chomp
89
+ @current_offset = $~.offset(0)[0]
90
+
91
+ # Import tag
92
+ if match = tag.match(/(?:import|include)\s+(.*)/)
93
+ files = $1.split(",").map do |file|
94
+ # Clean up any quotes or extraneous spaces and check the file
95
+ filepath = from_fuzzy_path(strip_quotes(file).strip)
96
+ kit_error("File not found", file, imports) unless filepath
97
+ filepath
98
+ end
99
+ import(files, imports, variables)
100
+
101
+ # Variable assignment tag
102
+ elsif match = tag.match(/([a-zA-Z0-9\-_]+)\s*(?:=|\:|\s+)\s*(.*)/)
103
+ variables[$1.strip] = $2.strip
104
+ "" # Completely remove the comment from the source
105
+
106
+ # Variable usage tag
107
+ elsif match = tag.match(/^([a-zA-Z0-9\-_]+)$/)
108
+ if variables.has_key? $1
109
+ variables[$1]
110
+ else
111
+ kit_error("Unitialized variable: '#{$1}'", filepath)
112
+ end
113
+
114
+ # Undefined tag type
115
+ else kit_error("Undefined tag type", filepath)
116
+
117
+ end
118
+ end
119
+
120
+ return result
121
+ end
122
+ end
123
+
124
+
125
+
126
+ # Removes Single and Double Quotes
127
+ #
128
+ def self.strip_quotes(str)
129
+ return str.gsub("\"", "").gsub("'", "")
130
+ end
131
+
132
+
133
+
134
+ # Return a Sass partial filename
135
+ #
136
+ def self.sass_partial(filepath)
137
+ filename = File.basename(filepath)
138
+ return File.join(File.dirname(filepath), "_#{filename}")
139
+ end
140
+
141
+
142
+
143
+ # Scope a block to a file's directory
144
+ #
145
+ def self.scope_dir(filepath, &code)
146
+ Dir.chdir(File.dirname(filepath)) do
147
+ code.call
148
+ end
149
+ end
150
+
151
+
152
+
153
+ # Raise a friendly error
154
+ #
155
+ def self.kit_error(message, filepath, imports = nil)
156
+ # File not found, recurse if imports was passed in
157
+ if !File.exists? filepath
158
+ puts "ERROR: #{filepath}: #{message}"
159
+ if imports
160
+ kit_error(message, imports[-1])
161
+ else
162
+ abort
163
+ end
164
+
165
+ # Use the supplied offset to find the line number and column of the error
166
+ else
167
+ lines = File.read(filepath)[0, @current_offset].split("\n")
168
+ line = lines.length
169
+ column = lines[-1].length
170
+ abort "ERROR: #{filepath} (Line #{line}, Column #{column}): #{message}"
171
+ end
172
+ end
173
+
174
+ end