petroglyph 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.
- data/.gitignore +5 -0
- data/.rspec +1 -0
- data/.simplecov +2 -0
- data/Gemfile +4 -0
- data/README.md +95 -0
- data/Rakefile +1 -0
- data/lib/petroglyph/engine.rb +18 -0
- data/lib/petroglyph/scope.rb +94 -0
- data/lib/petroglyph/version.rb +3 -0
- data/lib/petroglyph.rb +17 -0
- data/lib/sinatra/petroglyph.rb +9 -0
- data/lib/tilt/petroglyph.rb +27 -0
- data/petroglyph.gemspec +25 -0
- data/spec/engine_spec.rb +19 -0
- data/spec/fixtures/views/helper.pg +1 -0
- data/spec/fixtures/views/index.pg +3 -0
- data/spec/fixtures/views/partials/sub_partial.pg +1 -0
- data/spec/fixtures/views/post.pg +1 -0
- data/spec/fixtures/views/syntax.pg +3 -0
- data/spec/fixtures/views/the_partial.pg +1 -0
- data/spec/fixtures/views/with_partial.pg +3 -0
- data/spec/fixtures/views/with_sub_partial.pg +3 -0
- data/spec/scope_spec.rb +250 -0
- data/spec/sinatra_integration_spec.rb +37 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/tilt_spec.rb +44 -0
- metadata +115 -0
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color --format documentation
|
data/.simplecov
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# Petroglyph
|
2
|
+
|
3
|
+
A simple, terse, and unsurprising ruby dsl to create json views.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
Add a node with a simple value:
|
8
|
+
|
9
|
+
node :beverage => current_user.favorite_drink
|
10
|
+
=> '{"beverage":"mead"}'
|
11
|
+
|
12
|
+
Add a node with nested content:
|
13
|
+
|
14
|
+
node :home do
|
15
|
+
merge {:location => {:city => 'Paris', :country => 'France'}}
|
16
|
+
end
|
17
|
+
=> '{"home":{"location":{"city":"Paris","country":"France"}}}'
|
18
|
+
|
19
|
+
Add sibling nodes within a node:
|
20
|
+
|
21
|
+
node :pet do
|
22
|
+
merge {:species => "turtle", :color => 'green'}
|
23
|
+
node :name => "Anthony"
|
24
|
+
end
|
25
|
+
=> '{"pet":{"species":"turtle","color":"green","name":"Anthony"}}'
|
26
|
+
|
27
|
+
It's all just ruby, unsurprisingly:
|
28
|
+
|
29
|
+
node :pet do
|
30
|
+
if user.child?
|
31
|
+
merge {:species => "turtle"}
|
32
|
+
node :name => "Anthony"
|
33
|
+
else
|
34
|
+
node :species => 'human'
|
35
|
+
node :name => 'Billy'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
=> '{"pet":{"species":"turtle","name":"Anthony"}}'
|
39
|
+
|
40
|
+
Conveniently define which attributes to include. Create a new node with a different name for attributes you wish to alias.
|
41
|
+
|
42
|
+
alice = Person.create!(:name => 'Alice', :profession => 'surgeon', :created_at => 28.years.ago, :gender => 'female')
|
43
|
+
|
44
|
+
node :person => alice do
|
45
|
+
attributes :name, :gender
|
46
|
+
node :job => alice.profession
|
47
|
+
end
|
48
|
+
=> '{"person":{"name":"Alice","gender":"female","job":"surgeon"}}'
|
49
|
+
|
50
|
+
Iterate through collections:
|
51
|
+
|
52
|
+
wulong = Tea.new(:type => 'wulong')
|
53
|
+
lucha = Tea.new(:type => 'green')
|
54
|
+
|
55
|
+
collection :teas => [wulong, lucha] do
|
56
|
+
attributes :type
|
57
|
+
end
|
58
|
+
=> '{"teas":[{"type":"wulong"},{"type":"wulong"}]}'
|
59
|
+
|
60
|
+
|
61
|
+
You can also explicitly reference each item in the collection if you need to:
|
62
|
+
|
63
|
+
collection :teas => teas do |tea|
|
64
|
+
node :tea => tea do
|
65
|
+
attributes :type
|
66
|
+
end
|
67
|
+
node :provider => lookup_provider_for(tea)
|
68
|
+
end
|
69
|
+
=> '{"teas":[{"tea":{"type":"wulong"},{"provider":"Imperial Teas"}},{"tea":{"type":"wulong"},{"provider":"House of Tea"}}]}'
|
70
|
+
|
71
|
+
Partials have been implemented. This defaults to looking for a file in the same file as the template, or in a subdirectory called `partials`.
|
72
|
+
|
73
|
+
This can be overridden by re-implementing the `Petroglyph.partial(name)` method.
|
74
|
+
|
75
|
+
collection :teas => teas do |tea|
|
76
|
+
# partial(template_name, local_variables)
|
77
|
+
partial :tea, :tea => tea
|
78
|
+
end
|
79
|
+
|
80
|
+
## Caveat
|
81
|
+
|
82
|
+
We've removed support for using instance variables (from the controller) in the template, as this was hacky and not optimal.
|
83
|
+
|
84
|
+
## Related Projects
|
85
|
+
|
86
|
+
Other json templating libraries exist, some of which also generate XML.
|
87
|
+
|
88
|
+
* [Rabl](https://github.com/nesquena/rabl)
|
89
|
+
* [Tequila](https://github.com/inem/tequila)
|
90
|
+
* [Argonaut](https://github.com/jbr/argonaut)
|
91
|
+
* [JSON Builder](https://github.com/dewski/json_builder)
|
92
|
+
* [JBuilder](https://github.com/rails/jbuilder)
|
93
|
+
* [Jsonify](https://github.com/bsiggelkow/jsonify)
|
94
|
+
* [Representative](https://github.com/mdub/representative)
|
95
|
+
* [Tokamak](https://github.com/abril/tokamak)
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Petroglyph
|
2
|
+
class Engine
|
3
|
+
|
4
|
+
def initialize(data = nil)
|
5
|
+
@data = data
|
6
|
+
end
|
7
|
+
|
8
|
+
def render(context = Object.new, locals = {}, file = nil, &block)
|
9
|
+
scope = Scope.new(context, locals, file)
|
10
|
+
if @data
|
11
|
+
scope.instance_eval(@data)
|
12
|
+
else
|
13
|
+
scope.instance_eval(&block)
|
14
|
+
end
|
15
|
+
scope.value.to_json
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Petroglyph
|
2
|
+
class Scope
|
3
|
+
attr_accessor :value, :object, :file
|
4
|
+
|
5
|
+
def initialize(context = nil, locals = {}, template_filename = nil, parent_scope = nil)
|
6
|
+
@file = template_filename
|
7
|
+
@context = context
|
8
|
+
@locals = locals
|
9
|
+
@parent_scope = parent_scope
|
10
|
+
@value = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def sub_scope(object = nil)
|
14
|
+
scope = Scope.new(@context, @locals, @file, self)
|
15
|
+
scope.object = object
|
16
|
+
scope
|
17
|
+
end
|
18
|
+
|
19
|
+
def node(input, &block)
|
20
|
+
@value ||= {}
|
21
|
+
scope = nil
|
22
|
+
name = nil
|
23
|
+
value = nil
|
24
|
+
if input.is_a?(Hash)
|
25
|
+
raise ArgumentError, "node can't deal with more than one key at a time" if input.keys.size > 1
|
26
|
+
name = input.keys.first
|
27
|
+
value = input.values.first
|
28
|
+
else
|
29
|
+
name = input
|
30
|
+
end
|
31
|
+
|
32
|
+
if block_given?
|
33
|
+
scope = sub_scope(value)
|
34
|
+
scope.instance_eval(&block)
|
35
|
+
@value[name] = scope.value if scope.value
|
36
|
+
else
|
37
|
+
@value[name] = value
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def collection(input, &block)
|
42
|
+
@value ||= {}
|
43
|
+
name = input.keys.first
|
44
|
+
items = input.values.first
|
45
|
+
results = []
|
46
|
+
items.each do |item|
|
47
|
+
scope = sub_scope(item)
|
48
|
+
scope.instance_exec(item, &block)
|
49
|
+
results << scope.value
|
50
|
+
end
|
51
|
+
@value[name] = results
|
52
|
+
end
|
53
|
+
|
54
|
+
def merge(hash, &block)
|
55
|
+
@value ||= {}
|
56
|
+
if block_given?
|
57
|
+
scope = sub_scope(hash)
|
58
|
+
scope.instance_eval(&block)
|
59
|
+
hash = scope.value
|
60
|
+
end
|
61
|
+
@value.merge!(hash)
|
62
|
+
end
|
63
|
+
|
64
|
+
def attributes(*args)
|
65
|
+
fragment = {}
|
66
|
+
args.each do |method|
|
67
|
+
if @object.respond_to?(method)
|
68
|
+
fragment[method] = @object.send(method)
|
69
|
+
else
|
70
|
+
fragment[method] = @object[method]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
merge fragment
|
74
|
+
end
|
75
|
+
|
76
|
+
def partial(name, locals = {})
|
77
|
+
data = Petroglyph.partial(name, file)
|
78
|
+
scope = Scope.new(@context, locals)
|
79
|
+
scope.instance_eval(data)
|
80
|
+
merge scope.value
|
81
|
+
end
|
82
|
+
|
83
|
+
def method_missing(method, *args, &block)
|
84
|
+
if @locals.has_key?(method)
|
85
|
+
@locals[method]
|
86
|
+
elsif @context.respond_to?(method)
|
87
|
+
@context.send(method)
|
88
|
+
else
|
89
|
+
super
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
data/lib/petroglyph.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
require 'petroglyph/scope'
|
4
|
+
require 'petroglyph/engine'
|
5
|
+
|
6
|
+
module Petroglyph
|
7
|
+
class << self
|
8
|
+
def partial(filename, template_filename)
|
9
|
+
basedir = File.dirname(template_filename)
|
10
|
+
[basedir, "#{basedir}/partials"].each do |dir|
|
11
|
+
path = File.join(dir, "#{filename}.pg")
|
12
|
+
return File.read(path) if File.exist?(path)
|
13
|
+
end
|
14
|
+
raise Exception.new("Could not find partial #{filename}")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Makes Petroglyph available through Tilt.
|
2
|
+
# Also contains a utility function that will
|
3
|
+
# be added to Sinatra if Sinatra is defined.
|
4
|
+
|
5
|
+
require 'tilt'
|
6
|
+
require 'petroglyph'
|
7
|
+
|
8
|
+
module Tilt
|
9
|
+
class PetroglyphTemplate < Template
|
10
|
+
self.default_mime_type = "application/json"
|
11
|
+
def initialize_engine
|
12
|
+
return if defined? ::Petroglyph
|
13
|
+
require_template_library 'petroglyph'
|
14
|
+
end
|
15
|
+
|
16
|
+
def prepare; end
|
17
|
+
|
18
|
+
def precompiled_template(locals)
|
19
|
+
data.to_str
|
20
|
+
end
|
21
|
+
|
22
|
+
def evaluate(scope = Object.new, locals = {}, &block)
|
23
|
+
Petroglyph::Engine.new(data).render(scope, locals, file, &block)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
register PetroglyphTemplate, 'pg'
|
27
|
+
end
|
data/petroglyph.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "petroglyph/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "petroglyph"
|
7
|
+
s.version = Petroglyph::VERSION
|
8
|
+
s.authors = ["Katrina Owen"]
|
9
|
+
s.email = ["katrina.owen@gmail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{A simple, terse, and unsurprising ruby dsl to create json views.}
|
12
|
+
s.description = %q{A simple, terse, and unsurprising ruby dsl to create json views.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "petroglyph"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {spec}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_runtime_dependency "tilt"
|
22
|
+
s.add_development_dependency "rspec"
|
23
|
+
s.add_development_dependency "sinatra"
|
24
|
+
s.add_development_dependency "rack-test"
|
25
|
+
end
|
data/spec/engine_spec.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Petroglyph::Engine do
|
4
|
+
it "takes a template" do
|
5
|
+
engine = Petroglyph::Engine.new('node :whatever => "nevermind"')
|
6
|
+
engine.render.should eq({:whatever => "nevermind"}.to_json)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "takes a block" do
|
10
|
+
Petroglyph::Engine.new.render do
|
11
|
+
node :whatever => "nevermind"
|
12
|
+
end.should eq({:whatever => "nevermind"}.to_json)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "passes the locals" do
|
16
|
+
engine = Petroglyph::Engine.new('node :whatever => thing')
|
17
|
+
engine.render(nil, {:thing => 'stuff'}).should eq({:whatever => 'stuff'}.to_json)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
node :price => price
|
@@ -0,0 +1 @@
|
|
1
|
+
node :thing => thing
|
@@ -0,0 +1 @@
|
|
1
|
+
node :post => post
|
@@ -0,0 +1 @@
|
|
1
|
+
node :thing => thing
|
data/spec/scope_spec.rb
ADDED
@@ -0,0 +1,250 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Petroglyph::Scope do
|
4
|
+
|
5
|
+
it "takes a simple string value" do
|
6
|
+
scope = Petroglyph::Scope.new
|
7
|
+
scope.node :whatever => "nevermind"
|
8
|
+
scope.value.should eq(:whatever => "nevermind")
|
9
|
+
end
|
10
|
+
|
11
|
+
it "merges in a hash" do
|
12
|
+
tea = {:tea => {:temperature => 'hot', :type => 'wulong'}}
|
13
|
+
|
14
|
+
scope = Petroglyph::Scope.new
|
15
|
+
scope.merge tea
|
16
|
+
scope.value.should eq({:tea => {:temperature => "hot", :type => 'wulong'}})
|
17
|
+
end
|
18
|
+
|
19
|
+
it "merges within a block" do
|
20
|
+
scope = Petroglyph::Scope.new
|
21
|
+
scope.node :whatever do
|
22
|
+
merge(:stuff => {:no => :way})
|
23
|
+
end
|
24
|
+
|
25
|
+
scope.value.should eq({:whatever => {:stuff => {:no => :way}}})
|
26
|
+
end
|
27
|
+
|
28
|
+
it "lets you process what you merge in a block" do
|
29
|
+
scope = Petroglyph::Scope.new
|
30
|
+
scope.node :whatever do
|
31
|
+
merge(10) do
|
32
|
+
attributes :to_s
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
scope.value.should eq({:whatever => {:to_s => '10'}})
|
37
|
+
end
|
38
|
+
|
39
|
+
it "handles sibling nodes" do
|
40
|
+
scope = Petroglyph::Scope.new
|
41
|
+
scope.node :whatever => "nevermind"
|
42
|
+
scope.node :stuff => "awesome"
|
43
|
+
|
44
|
+
scope.value.should eq({:whatever => "nevermind", :stuff => "awesome"})
|
45
|
+
end
|
46
|
+
|
47
|
+
it "handles sibling nodes as blocks" do
|
48
|
+
scope = Petroglyph::Scope.new
|
49
|
+
scope.node :whatever => "nevermind"
|
50
|
+
scope.node :stuff do
|
51
|
+
merge(:too => :cool)
|
52
|
+
end
|
53
|
+
|
54
|
+
scope.value.should eq({:whatever => "nevermind", :stuff => {:too => :cool}})
|
55
|
+
end
|
56
|
+
|
57
|
+
it "nests nodes" do
|
58
|
+
scope = Petroglyph::Scope.new
|
59
|
+
scope.node :whatever do
|
60
|
+
node :stuff => "awesome"
|
61
|
+
end
|
62
|
+
|
63
|
+
scope.value.should eq({:whatever => {:stuff => 'awesome'}})
|
64
|
+
end
|
65
|
+
|
66
|
+
it "nests stuff arbitrarily deeply with complex values" do
|
67
|
+
scope = Petroglyph::Scope.new
|
68
|
+
scope.node :drink do
|
69
|
+
node :tea do
|
70
|
+
node :temperature do
|
71
|
+
merge(:really => :hot)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
scope.value.should eq({:drink => {:tea => {:temperature => {:really => :hot}}}})
|
77
|
+
end
|
78
|
+
|
79
|
+
it "uses regular ruby" do
|
80
|
+
scope = Petroglyph::Scope.new
|
81
|
+
scope.node :drink do
|
82
|
+
if false
|
83
|
+
"cold"
|
84
|
+
else
|
85
|
+
node(:tea) do
|
86
|
+
merge(:temperature => "hot".upcase)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
scope.value.should eq({:drink => {:tea => {:temperature => "HOT"}}})
|
92
|
+
end
|
93
|
+
|
94
|
+
context "with locals" do
|
95
|
+
it "resolves values" do
|
96
|
+
scope = Petroglyph::Scope.new(nil, {:thing => 'stuff'})
|
97
|
+
|
98
|
+
scope.instance_eval do
|
99
|
+
node :thing => thing
|
100
|
+
end
|
101
|
+
|
102
|
+
scope.value.should eq({:thing => 'stuff'})
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "within a context" do
|
107
|
+
it "has access to methods" do
|
108
|
+
context = Object.new
|
109
|
+
def context.thing
|
110
|
+
'stuff'
|
111
|
+
end
|
112
|
+
|
113
|
+
scope = Petroglyph::Scope.new(context)
|
114
|
+
scope.instance_eval do
|
115
|
+
node :thing => thing
|
116
|
+
end
|
117
|
+
scope.value.should eq({:thing => 'stuff'})
|
118
|
+
end
|
119
|
+
|
120
|
+
it "lets local variables take precedence over methods" do
|
121
|
+
context = Object.new
|
122
|
+
def context.thing
|
123
|
+
'junk'
|
124
|
+
end
|
125
|
+
|
126
|
+
scope = Petroglyph::Scope.new(context, {:thing => 'stuff'})
|
127
|
+
scope.instance_eval do
|
128
|
+
node :thing => thing
|
129
|
+
end
|
130
|
+
scope.value.should eq({:thing => 'stuff'})
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe "attributes" do
|
135
|
+
it "evaluates on an object" do
|
136
|
+
hal = OpenStruct.new(:name => 'HAL 9000', :temperament => 'psychotic', :garbage => 'junk')
|
137
|
+
|
138
|
+
scope = Petroglyph::Scope.new
|
139
|
+
scope.node :hal => hal do
|
140
|
+
attributes :name, :temperament
|
141
|
+
end
|
142
|
+
|
143
|
+
scope.value.should eq({:hal => {:name => 'HAL 9000', :temperament => 'psychotic'}})
|
144
|
+
end
|
145
|
+
|
146
|
+
it "evaluates on a hash" do
|
147
|
+
hal = {:name => 'HAL 9000', :temperament => 'psychotic', :garbage => 'junk'}
|
148
|
+
|
149
|
+
scope = Petroglyph::Scope.new
|
150
|
+
scope.node :hal => hal do
|
151
|
+
attributes :name, :temperament
|
152
|
+
end
|
153
|
+
|
154
|
+
scope.value.should eq({:hal => {:name => 'HAL 9000', :temperament => 'psychotic'}})
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context "with a collection" do
|
159
|
+
let(:tea) { OpenStruct.new(:type => 'tea', :temperature => 'hot') }
|
160
|
+
let(:coffee) { OpenStruct.new(:type => 'coffee', :temperature => 'lukewarm') }
|
161
|
+
let(:drinks) { [tea, coffee] }
|
162
|
+
|
163
|
+
it "evaluates attributes" do
|
164
|
+
scope = Petroglyph::Scope.new
|
165
|
+
scope.collection :drinks => drinks do
|
166
|
+
attributes :type, :temperature
|
167
|
+
end
|
168
|
+
|
169
|
+
scope.value.should eq({:drinks => [{:type => 'tea', :temperature => 'hot'}, {:type => 'coffee', :temperature => 'lukewarm'}]})
|
170
|
+
end
|
171
|
+
|
172
|
+
it "evaluates attributes on explicitly named items" do
|
173
|
+
scope = Petroglyph::Scope.new
|
174
|
+
scope.collection :drinks => drinks do |drink|
|
175
|
+
node :drink do
|
176
|
+
node :type => drink.type
|
177
|
+
end
|
178
|
+
node :prep => "Make water #{drink.temperature}."
|
179
|
+
end
|
180
|
+
|
181
|
+
scope.value.should eq({:drinks => [{:drink => {:type => 'tea'}, :prep => "Make water hot."}, {:drink => {:type => 'coffee'}, :prep => "Make water lukewarm."}]})
|
182
|
+
end
|
183
|
+
|
184
|
+
it "evaluates object attributes within a sub node" do
|
185
|
+
scope = Petroglyph::Scope.new
|
186
|
+
scope.collection :drinks => drinks do |drink|
|
187
|
+
node :drink => drink do
|
188
|
+
attributes :type
|
189
|
+
end
|
190
|
+
node :prep => "Make water #{drink.temperature}."
|
191
|
+
end
|
192
|
+
|
193
|
+
scope.value.should eq({:drinks => [{:drink => {:type => 'tea'}, :prep => "Make water hot."}, {:drink => {:type => 'coffee'}, :prep => "Make water lukewarm."}]})
|
194
|
+
end
|
195
|
+
|
196
|
+
it "evaluates an empty collection to an empty array" do
|
197
|
+
scope = Petroglyph::Scope.new
|
198
|
+
scope.collection :drinks => [] do |drink|
|
199
|
+
node :drink => drink
|
200
|
+
end
|
201
|
+
|
202
|
+
scope.value.should eq({:drinks => []})
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
context "with partials" do
|
207
|
+
it "renders a partial" do
|
208
|
+
Petroglyph.stub(:partial) { 'node :drink => "tea"' }
|
209
|
+
|
210
|
+
scope = Petroglyph::Scope.new
|
211
|
+
scope.node :partial do
|
212
|
+
partial :the_partial
|
213
|
+
end
|
214
|
+
|
215
|
+
scope.value.should eq({:partial => {:drink => 'tea'}})
|
216
|
+
end
|
217
|
+
|
218
|
+
it "renders a partial with local variables" do
|
219
|
+
Petroglyph.stub(:partial) { 'node :drink => drink' }
|
220
|
+
|
221
|
+
scope = Petroglyph::Scope.new
|
222
|
+
scope.node :partial do
|
223
|
+
partial :the_partial, :drink => 'tea'
|
224
|
+
end
|
225
|
+
|
226
|
+
scope.value.should eq({:partial => {:drink => 'tea'}})
|
227
|
+
end
|
228
|
+
|
229
|
+
it "finds the partial" do
|
230
|
+
scope = Petroglyph::Scope.new
|
231
|
+
scope.file = "spec/fixtures/views/some_template.pg"
|
232
|
+
scope.node :partial do
|
233
|
+
partial :the_partial, :thing => 'stuff'
|
234
|
+
end
|
235
|
+
|
236
|
+
scope.value.should eq({:partial => {:thing => 'stuff'}})
|
237
|
+
end
|
238
|
+
|
239
|
+
it "finds the partial in a subdirectory" do
|
240
|
+
scope = Petroglyph::Scope.new
|
241
|
+
scope.file = "spec/fixtures/views/some_template.pg"
|
242
|
+
scope.node :partial do
|
243
|
+
partial :sub_partial, :thing => 'stuff'
|
244
|
+
end
|
245
|
+
|
246
|
+
scope.value.should eq({:partial => {:thing => 'stuff'}})
|
247
|
+
end
|
248
|
+
|
249
|
+
end
|
250
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'sinatra'
|
3
|
+
require 'sinatra/petroglyph'
|
4
|
+
require 'rack/test'
|
5
|
+
|
6
|
+
class PetroglyphApp < Sinatra::Base
|
7
|
+
set :root, File.dirname(__FILE__)+"/fixtures"
|
8
|
+
set :show_exceptions, false
|
9
|
+
|
10
|
+
get "/" do
|
11
|
+
tea = OpenStruct.new(:type => 'tea', :temperature => 'hot')
|
12
|
+
coffee = OpenStruct.new(:type => 'coffee', :temperature => 'lukewarm')
|
13
|
+
pg :index, :locals => {:drinks => [tea, coffee]}
|
14
|
+
end
|
15
|
+
|
16
|
+
post '/' do
|
17
|
+
pg :post, :locals => {:post => 'a post'}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "Sinatra integration" do
|
22
|
+
include Rack::Test::Methods
|
23
|
+
|
24
|
+
def app
|
25
|
+
PetroglyphApp
|
26
|
+
end
|
27
|
+
|
28
|
+
it "works" do
|
29
|
+
get "/"
|
30
|
+
last_response.body.should eq '{"drinks":[{"type":"tea","temperature":"hot"},{"type":"coffee","temperature":"lukewarm"}]}'
|
31
|
+
end
|
32
|
+
|
33
|
+
xit "doesn't freak out on the HTTP method" do
|
34
|
+
post '/'
|
35
|
+
last_response.body.should eq '{}'
|
36
|
+
end
|
37
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/spec/tilt_spec.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'tilt/petroglyph'
|
3
|
+
|
4
|
+
describe "Tilt integration" do
|
5
|
+
it "registers .pg as a petroglyph template" do
|
6
|
+
Tilt.mappings['pg'].should include(Tilt::PetroglyphTemplate)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "renders from a file" do
|
10
|
+
template = Tilt::PetroglyphTemplate.new('spec/fixtures/views/syntax.pg')
|
11
|
+
template.render.should eq('{"syntax":{"it":"works"}}')
|
12
|
+
end
|
13
|
+
|
14
|
+
it "renders from a block" do
|
15
|
+
template = Tilt::PetroglyphTemplate.new { |t| 'node :hello => "world"' }
|
16
|
+
template.render.should eq("{\"hello\":\"world\"}")
|
17
|
+
end
|
18
|
+
|
19
|
+
it "can be rendered more than once" do
|
20
|
+
template = Tilt::PetroglyphTemplate.new { |t| 'node :hello => "world"' }
|
21
|
+
|
22
|
+
3.times do
|
23
|
+
template.render.should eq("{\"hello\":\"world\"}")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it "takes local variables" do
|
28
|
+
template = Tilt::PetroglyphTemplate.new { |t| 'node :hello => place' }
|
29
|
+
template.render(Object.new, :place => 'world').should eq("{\"hello\":\"world\"}")
|
30
|
+
end
|
31
|
+
|
32
|
+
context "with partials" do
|
33
|
+
it "loads the partial" do
|
34
|
+
template = Tilt::PetroglyphTemplate.new('spec/fixtures/views/with_partial.pg')
|
35
|
+
template.render.should eq('{"root":{"thing":"stuff"}}')
|
36
|
+
end
|
37
|
+
|
38
|
+
it "loads the partial from a subdirectory" do
|
39
|
+
template = Tilt::PetroglyphTemplate.new('spec/fixtures/views/with_sub_partial.pg')
|
40
|
+
template.render.should eq('{"root":{"thing":"stuff"}}')
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
metadata
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: petroglyph
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Katrina Owen
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-12-20 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: tilt
|
16
|
+
requirement: &2161013440 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2161013440
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &2161013020 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2161013020
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: sinatra
|
38
|
+
requirement: &2161012600 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *2161012600
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rack-test
|
49
|
+
requirement: &2161012180 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *2161012180
|
58
|
+
description: A simple, terse, and unsurprising ruby dsl to create json views.
|
59
|
+
email:
|
60
|
+
- katrina.owen@gmail.com
|
61
|
+
executables: []
|
62
|
+
extensions: []
|
63
|
+
extra_rdoc_files: []
|
64
|
+
files:
|
65
|
+
- .gitignore
|
66
|
+
- .rspec
|
67
|
+
- .simplecov
|
68
|
+
- Gemfile
|
69
|
+
- README.md
|
70
|
+
- Rakefile
|
71
|
+
- lib/petroglyph.rb
|
72
|
+
- lib/petroglyph/engine.rb
|
73
|
+
- lib/petroglyph/scope.rb
|
74
|
+
- lib/petroglyph/version.rb
|
75
|
+
- lib/sinatra/petroglyph.rb
|
76
|
+
- lib/tilt/petroglyph.rb
|
77
|
+
- petroglyph.gemspec
|
78
|
+
- spec/engine_spec.rb
|
79
|
+
- spec/fixtures/views/helper.pg
|
80
|
+
- spec/fixtures/views/index.pg
|
81
|
+
- spec/fixtures/views/partials/sub_partial.pg
|
82
|
+
- spec/fixtures/views/post.pg
|
83
|
+
- spec/fixtures/views/syntax.pg
|
84
|
+
- spec/fixtures/views/the_partial.pg
|
85
|
+
- spec/fixtures/views/with_partial.pg
|
86
|
+
- spec/fixtures/views/with_sub_partial.pg
|
87
|
+
- spec/scope_spec.rb
|
88
|
+
- spec/sinatra_integration_spec.rb
|
89
|
+
- spec/spec_helper.rb
|
90
|
+
- spec/tilt_spec.rb
|
91
|
+
homepage: ''
|
92
|
+
licenses: []
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ! '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ! '>='
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
requirements: []
|
110
|
+
rubyforge_project: petroglyph
|
111
|
+
rubygems_version: 1.8.10
|
112
|
+
signing_key:
|
113
|
+
specification_version: 3
|
114
|
+
summary: A simple, terse, and unsurprising ruby dsl to create json views.
|
115
|
+
test_files: []
|