kitc 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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