bob-compiler 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 61eb15e3c30c2bc86d29a782bdb6252ad1bde448
4
+ data.tar.gz: 2bac9022339fd3d22528a49f5211243304b7de1f
5
+ SHA512:
6
+ metadata.gz: d5eaaa22cfbd87d1b0c2c4a606cbeb18d0b08af6e9f984c3092d68aa6f45f0dc7431ca2d1c86eabeb370cff220f78ac45fab4902e1a6c6889d9fd4ea59c58543
7
+ data.tar.gz: ba8f418b9f47e9e8096866b145508a22914cef9a8fe8b9900ce71e63597a42c8c8c84a492c92bfa568944ea28d21a1efdfe60784afc4c62785161fa4702c80ff
data/.gitignore ADDED
@@ -0,0 +1,35 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+ *.tmp
12
+
13
+ ## Specific to RubyMotion:
14
+ .dat*
15
+ .repl_history
16
+ build/
17
+
18
+ ## Documentation cache and generated files:
19
+ /.yardoc/
20
+ /_yardoc/
21
+ /doc/
22
+ /rdoc/
23
+
24
+ ## Environment normalisation:
25
+ /.bundle/
26
+ /lib/bundler/man/
27
+
28
+ # for a library or gem, you might want to ignore these files since the code is
29
+ # intended to run in multiple environments; otherwise, check them in:
30
+ # Gemfile.lock
31
+ # .ruby-version
32
+ # .ruby-gemset
33
+
34
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
35
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in bob-compiler.gemspec
4
+ gemspec
5
+
6
+ gem 'activesupport', "~> 3.2.0"
data/Gemfile.lock ADDED
@@ -0,0 +1,38 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ bob-compiler (0.0.1)
5
+ activesupport (>= 3.2.0)
6
+ nokogiri (~> 1.6.4)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (3.2.21)
12
+ i18n (~> 0.6, >= 0.6.4)
13
+ multi_json (~> 1.0)
14
+ coderay (1.1.0)
15
+ i18n (0.6.11)
16
+ method_source (0.8.2)
17
+ mini_portile (0.6.1)
18
+ minitest (5.4.2)
19
+ multi_json (1.10.1)
20
+ nokogiri (1.6.5)
21
+ mini_portile (~> 0.6.0)
22
+ pry (0.10.1)
23
+ coderay (~> 1.1.0)
24
+ method_source (~> 0.8.1)
25
+ slop (~> 3.4)
26
+ rake (10.3.2)
27
+ slop (3.6.0)
28
+
29
+ PLATFORMS
30
+ ruby
31
+
32
+ DEPENDENCIES
33
+ activesupport (~> 3.2.0)
34
+ bob-compiler!
35
+ bundler (~> 1.7)
36
+ minitest (~> 5.4.0)
37
+ pry (~> 0.10.1)
38
+ rake (~> 10.0)
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Brewhouse
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,8 @@
1
+ Bob Compiler
2
+ ============
3
+
4
+ The [Bob][bob] builder requires a [JavaScript template](bob-js) to function. In order to simplify the workflow, a designer could simply create and annotate templates with [Bob-flavored HTML](bob-html) and let the Bob compiler generate the corresponding JavaScript template.
5
+
6
+ [bob]: https://github.com/BrewhouseTeam/bob
7
+ [bob-js]: https://some/where/
8
+ [bob-html]: https://some/where/
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ task default: ["test"]
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs << "test"
8
+ t.pattern = "test/*_test.rb"
9
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bob/compiler/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "bob-compiler"
8
+ spec.version = Bob::Compiler::VERSION
9
+ spec.authors = ["Godfrey Chan", "Gabe Scholz"]
10
+ spec.email = ["godfreykfc@gmail.com", "gabe@brewhouse.io"]
11
+ spec.summary = "A compiler for Bob templates"
12
+ spec.homepage = "https://github.com/BrewhouseTeam/bob-compiler"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency "nokogiri", "~> 1.6.4"
21
+ spec.add_dependency "activesupport", ">= 3.2.0"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.7"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "minitest", "~> 5.4.0"
26
+ spec.add_development_dependency "pry", "~> 0.10.1"
27
+ end
@@ -0,0 +1,8 @@
1
+ require "active_support"
2
+ require "bob/compiler/version"
3
+ require "bob/compiler/template"
4
+
5
+ module Bob
6
+ module Compiler
7
+ end
8
+ end
@@ -0,0 +1,132 @@
1
+ require 'active_support/core_ext/object/blank'
2
+ require 'bob/compiler/message'
3
+ require 'bob/compiler/buffer'
4
+ require 'json'
5
+
6
+ module Bob
7
+ module Compiler
8
+ class Base
9
+ def initialize
10
+ @buffer = Buffer.new
11
+ @warnings = []
12
+ @errors = []
13
+ @compiled = false
14
+ end
15
+
16
+ def warnings
17
+ ensure_compiled { @warnings }
18
+ end
19
+
20
+ def errors
21
+ ensure_compiled { @errors }
22
+ end
23
+
24
+ def warnings?
25
+ ensure_compiled { @warnings.present? }
26
+ end
27
+
28
+ def well_formed?
29
+ ensure_compiled { valid? && @warnings.blank? }
30
+ end
31
+
32
+ def errors?
33
+ ensure_compiled { @errors.present? }
34
+ end
35
+
36
+ def valid?
37
+ ensure_compiled { @errors.blank? }
38
+ end
39
+
40
+ def result
41
+ ensure_compiled { @buffer.string }
42
+ end
43
+
44
+ alias_method :to_s, :result
45
+
46
+ protected
47
+ def buffer
48
+ ensure_compiled { @buffer }
49
+ end
50
+
51
+ private
52
+ def ensure_compiled
53
+ compile! unless compiled?
54
+ yield
55
+ end
56
+
57
+ def compiled?
58
+ @compiled
59
+ end
60
+
61
+ def compile!
62
+ @compiled = true
63
+ compile
64
+ end
65
+
66
+ def compile
67
+ end
68
+
69
+ def warn(message, node = nil)
70
+ @warnings << Message.new(message, node)
71
+ end
72
+
73
+ def error(message, node = nil)
74
+ @errors << Message.new(message, node)
75
+ end
76
+
77
+ def identifier_from_attribute(node, attribute, required = true)
78
+ sanitize = nil
79
+
80
+ if node.has_attribute?(attribute)
81
+ identifier = node[attribute]
82
+ sanitized = identifier && sanitize_identifier(identifier)
83
+
84
+ if required && identifier.blank?
85
+ error "Required attribute `#{attribute}` cannot be blank", node
86
+ elsif required && sanitized.blank?
87
+ error "Attribute `#{attribute}` does not contain a valid identifier", node
88
+ elsif sanitized != identifier
89
+ warn "Attribute `#{attribute}` does not contain a valid identifier, assuming you meant #{sanitized.inspect}", node
90
+ end
91
+
92
+ elsif required
93
+ error "Missing required attribute `#{attribute}`", node
94
+ end
95
+
96
+ sanitized
97
+ end
98
+
99
+ def sanitize_identifier(identifier)
100
+ sanitized = identifier.downcase
101
+
102
+ # Replace all disallowed characters with a hyphen
103
+ sanitized.gsub! /[^a-z0-9\-\/]+/, '-'
104
+
105
+ # Each part must begin with a letter
106
+ sanitized.gsub! /(?<=\A|\/)[^a-z]+/, ''
107
+
108
+ # Remove any trailing hyphens
109
+ sanitized.gsub! /-+(?=\z|\/)/, ''
110
+
111
+ # Collapse multiple hyphens into one
112
+ sanitized.gsub! /-+/, '-'
113
+
114
+ # Collapse multiple slashes into one
115
+ sanitized.gsub! /\/+/, '/'
116
+
117
+ # Remove leading or trailing slashes
118
+ sanitized.gsub! /\A\/|\/\z/, ''
119
+
120
+ sanitized
121
+ end
122
+
123
+ def quote(arg)
124
+ JSON.generate(arg, quirks_mode: true)
125
+ end
126
+
127
+ def pretty_quote(arg)
128
+ JSON.pretty_generate(arg, quirks_mode: true)
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,21 @@
1
+ require "bob/compiler/walker"
2
+ require "bob/compiler/editable"
3
+
4
+ module Bob
5
+ module Compiler
6
+ class Block < Walker
7
+ include Editable
8
+
9
+ attr_reader :name
10
+
11
+ private
12
+ def compile
13
+ super if @name = identifier_from_attribute(@node, "bob-block")
14
+ end
15
+
16
+ def descriptor
17
+ {type: name}.merge(super)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,79 @@
1
+ require 'active_support/core_ext/string'
2
+
3
+ module Bob
4
+ module Compiler
5
+ class Buffer
6
+ class SameLineProxy
7
+ def initialize(buffer)
8
+ @buffer = buffer
9
+ end
10
+
11
+ def <<(string)
12
+ @buffer.<< string, true
13
+ self
14
+ end
15
+ end
16
+
17
+ attr_reader :string
18
+ alias_method :to_s, :string
19
+
20
+ def initialize(indent_level = 0)
21
+ @string = ''
22
+ @indent_level = indent_level
23
+ @same_line_proxy = SameLineProxy.new(self)
24
+ end
25
+
26
+ def <<(string, same_line = false)
27
+ newline! unless first_line? || same_line
28
+
29
+ if indented?
30
+ string = indent(string, @indent_level)
31
+ end
32
+
33
+ if same_line
34
+ string = string.lstrip
35
+ end
36
+
37
+ @string << string
38
+
39
+ @same_line_proxy
40
+ end
41
+
42
+ def merge(buffer)
43
+ buffer.string.each_line.with_index do |line, i|
44
+ self.<<(line.chomp, i == 0)
45
+ end
46
+
47
+ @same_line_proxy
48
+ end
49
+
50
+ def newline!
51
+ @string << "\n"
52
+ end
53
+
54
+ alias_method :blankline!, :newline!
55
+
56
+ def indented(increment = 2)
57
+ indent_level_was = @indent_level
58
+ @indent_level += increment
59
+ yield
60
+ ensure
61
+ @indent_level = indent_level_was
62
+ end
63
+
64
+ private
65
+ def first_line?
66
+ @string.empty?
67
+ end
68
+
69
+ def indented?
70
+ @indent_level > 0
71
+ end
72
+
73
+ def indent(str, multiplier = 2)
74
+ spaces = " " * multiplier
75
+ str.each_line.map { |line| line.blank? ? line : "#{spaces}#{line}" }.join
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,86 @@
1
+ require "json"
2
+ require "bob/compiler/buffer"
3
+
4
+ module Bob
5
+ module Compiler
6
+ module Editable
7
+ def initialize(*)
8
+ super
9
+ @next_editable_id = Hash.new(0)
10
+ end
11
+
12
+ private
13
+ def after_element(node, root)
14
+ push_editable(node) if is_editable?(node)
15
+ super
16
+ end
17
+
18
+ def is_editable?(node)
19
+ node.has_attribute?("bob-editable") || node.has_attribute?("bob-editable-type")
20
+ end
21
+
22
+ def push_editable(node)
23
+ type = editable_type_for(node)
24
+
25
+ if type
26
+ name = editable_name_for(node, type)
27
+
28
+ options = editable_options_for(node)
29
+
30
+ @buffer << "descriptor.editables.push({"
31
+ @buffer << " type: #{quote type},"
32
+ @buffer << " name: #{quote name},"
33
+ @buffer << " element: dom.getElement(),"
34
+ @buffer << " options: " << quote_options(options, node)
35
+ @buffer << "});"
36
+ end
37
+ end
38
+
39
+ def editable_type_for(node)
40
+ identifier_from_attribute(node, "bob-editable-type", false) || identifier_from_attribute(node, "bob-editable")
41
+ end
42
+
43
+ def editable_name_for(node, type)
44
+ identifier_from_attribute(node, "bob-editable-name", false) || generate_editable_name(type)
45
+ end
46
+
47
+ def editable_options_for(node)
48
+ node.attribute_nodes.each_with_object({}) do |attr, options|
49
+ if attr.name.start_with?("bob-option-")
50
+ options[ attr.name[11..-1] ] = parse_option_value( attr.value )
51
+ end
52
+ end
53
+ end
54
+
55
+ def generate_editable_name(type)
56
+ "#{type}#{generate_editable_id(type)}"
57
+ end
58
+
59
+ def generate_editable_id(type)
60
+ id = @next_editable_id[type]
61
+ @next_editable_id[type] += 1
62
+ id
63
+ end
64
+
65
+ def parse_option_value(value)
66
+ JSON.parse(value, quirks_mode: true) rescue value
67
+ end
68
+
69
+ def quote_options(options, node)
70
+ buffer = Buffer.new(2)
71
+
72
+ buffer << "{"
73
+
74
+ buffer.indented do
75
+ options.each do |(k, v)|
76
+ buffer << quote(k) << ": " << (String === v ? quote_and_substitute(v, node) : quote(v)) << ","
77
+ end
78
+ end
79
+
80
+ buffer << "}"
81
+
82
+ buffer.to_s
83
+ end
84
+ end
85
+ end
86
+ end