representative_view 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +11 -0
- data/README.markdown +57 -0
- data/Rakefile +11 -0
- data/lib/representative_view.rb +1 -0
- data/lib/representative_view/template_handler.rb +60 -0
- data/lib/representative_view/version.rb +3 -0
- data/representative_view.gemspec +26 -0
- data/spec/fixtures/books.rb +31 -0
- data/spec/representative_view/template_handler_spec.rb +103 -0
- data/spec/spec_helper.rb +52 -0
- metadata +142 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
Representative View
|
2
|
+
===================
|
3
|
+
|
4
|
+
"Representative View" integrates [Representative](http://github.com/mdub/representative) as a Rails template format, making it possible to generate XML and JSON representations of data <u>using the same template</u>.
|
5
|
+
|
6
|
+
Installing it
|
7
|
+
-------------
|
8
|
+
|
9
|
+
Simply add the '`representative_view`' gem to your `Gemfile`
|
10
|
+
|
11
|
+
gem "representative_view"
|
12
|
+
|
13
|
+
and run "`bundle install`".
|
14
|
+
|
15
|
+
Using it
|
16
|
+
--------
|
17
|
+
|
18
|
+
In your controller, declare that you can provide both XML and JSON, e.g.
|
19
|
+
|
20
|
+
class BooksController < ApplicationController
|
21
|
+
|
22
|
+
def index
|
23
|
+
respond_to do |format|
|
24
|
+
format.xml
|
25
|
+
format.json
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
Next, create a template with the suffix "`.rep`" (to select the Representative View template handler) and use the Representative DSL to generate elements and lists, e.g.
|
32
|
+
|
33
|
+
# app/views/books/index.rep
|
34
|
+
|
35
|
+
r.list_of :books, @books do
|
36
|
+
r.element :title
|
37
|
+
r.list_of :authors
|
38
|
+
end
|
39
|
+
|
40
|
+
Note that it's "`index.rep`", not "`index.xml.rep`" or "`index.json.rep`"; by omitting the format specifier, the template will be used to render both formats.
|
41
|
+
|
42
|
+
### Partials
|
43
|
+
|
44
|
+
Representative View happily supports the use of partials, as long as they're also in Representative format. Because Representative keeps track of the current "subject" as it renders, there's no need to explicitly pass an object into the partial:
|
45
|
+
|
46
|
+
# app/views/books/index.rep
|
47
|
+
|
48
|
+
r.list_of :books, @books do
|
49
|
+
render :partial => 'book'
|
50
|
+
end
|
51
|
+
|
52
|
+
# app/views/books/_book.rep
|
53
|
+
|
54
|
+
r.element :title
|
55
|
+
r.element :published do
|
56
|
+
r.element :by
|
57
|
+
end
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'representative_view/template_handler'
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'action_view'
|
2
|
+
|
3
|
+
module RepresentativeView
|
4
|
+
|
5
|
+
class TemplateHandler < ActionView::Template::Handler
|
6
|
+
|
7
|
+
include ActionView::Template::Handlers::Compilable
|
8
|
+
|
9
|
+
self.default_format = Mime::XML
|
10
|
+
|
11
|
+
def compile(template)
|
12
|
+
require 'representative/json'
|
13
|
+
require 'representative/nokogiri'
|
14
|
+
<<-RUBY
|
15
|
+
representative_view do |r|
|
16
|
+
#{template.source}
|
17
|
+
end
|
18
|
+
RUBY
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
module ViewHelpers
|
24
|
+
|
25
|
+
def mime_type
|
26
|
+
format_extension = formats.first
|
27
|
+
Mime::Type.lookup_by_extension(format_extension.to_s) || begin
|
28
|
+
raise "unrecognised format #{format_extension.inspect}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def appropriate_representative_class
|
33
|
+
case mime_type.to_s
|
34
|
+
when /xml/
|
35
|
+
::Representative::Nokogiri
|
36
|
+
when /json/
|
37
|
+
::Representative::JSON
|
38
|
+
else
|
39
|
+
raise "cannot determine appropriate Representative class for #{mime_type.to_s.inspect}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def representative_view
|
44
|
+
included = defined?(@_representative)
|
45
|
+
@_representative ||= appropriate_representative_class.new
|
46
|
+
yield @_representative
|
47
|
+
@_representative.to_s unless included
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
ActionView::Template.register_template_handler(:rep, RepresentativeView::TemplateHandler)
|
55
|
+
|
56
|
+
class ActionView::Base
|
57
|
+
|
58
|
+
include RepresentativeView::ViewHelpers
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "representative_view/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
|
7
|
+
gem.name = "representative_view"
|
8
|
+
gem.summary = "Builds XML and JSON from a single view template"
|
9
|
+
gem.homepage = "http://github.com/mdub/representative_view"
|
10
|
+
gem.authors = ["Mike Williams"]
|
11
|
+
gem.email = "mdub@dogbiscuit.org"
|
12
|
+
|
13
|
+
gem.version = RepresentativeView::VERSION.dup
|
14
|
+
gem.platform = Gem::Platform::RUBY
|
15
|
+
|
16
|
+
gem.add_runtime_dependency("representative", "~> 0.3.1")
|
17
|
+
gem.add_runtime_dependency("actionpack", "~> 3.0.1")
|
18
|
+
gem.add_runtime_dependency("nokogiri", ">= 1.4.2")
|
19
|
+
gem.add_runtime_dependency("json", ">= 1.4.5")
|
20
|
+
|
21
|
+
gem.require_paths = ["lib"]
|
22
|
+
|
23
|
+
gem.files = `git ls-files`.split("\n")
|
24
|
+
gem.test_files = `git ls-files -- spec/*`.split("\n")
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Books
|
2
|
+
|
3
|
+
def all
|
4
|
+
[
|
5
|
+
OpenStruct.new(
|
6
|
+
:title => "Sailing for old dogs",
|
7
|
+
:authors => ["Jim Watson"],
|
8
|
+
:published => OpenStruct.new(
|
9
|
+
:by => "Credulous Print",
|
10
|
+
:year => 1994
|
11
|
+
)
|
12
|
+
),
|
13
|
+
OpenStruct.new(
|
14
|
+
:title => "On the horizon",
|
15
|
+
:authors => ["Zoe Primpton", "Stan Ford"],
|
16
|
+
:published => OpenStruct.new(
|
17
|
+
:by => "McGraw-Hill",
|
18
|
+
:year => 2005
|
19
|
+
)
|
20
|
+
),
|
21
|
+
OpenStruct.new(
|
22
|
+
:title => "The Little Blue Book of VHS Programming",
|
23
|
+
:authors => ["Henry Nelson"],
|
24
|
+
:rating => "****"
|
25
|
+
)
|
26
|
+
]
|
27
|
+
end
|
28
|
+
|
29
|
+
extend self
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "a Representative template" do
|
4
|
+
|
5
|
+
before do
|
6
|
+
write_template 'books.rep', <<-RUBY
|
7
|
+
r.list_of :books, @books do
|
8
|
+
r.element :title
|
9
|
+
end
|
10
|
+
RUBY
|
11
|
+
end
|
12
|
+
|
13
|
+
it "can generate XML" do
|
14
|
+
render("books", :xml, :books => Books.all).should == undent(<<-XML)
|
15
|
+
<?xml version="1.0"?>
|
16
|
+
<books type="array">
|
17
|
+
<book>
|
18
|
+
<title>Sailing for old dogs</title>
|
19
|
+
</book>
|
20
|
+
<book>
|
21
|
+
<title>On the horizon</title>
|
22
|
+
</book>
|
23
|
+
<book>
|
24
|
+
<title>The Little Blue Book of VHS Programming</title>
|
25
|
+
</book>
|
26
|
+
</books>
|
27
|
+
XML
|
28
|
+
end
|
29
|
+
|
30
|
+
it "can generate XML dialects" do
|
31
|
+
Mime::Type.register "application/vnd.books+xml", :book_xml
|
32
|
+
render("books", :book_xml, :books => Books.all).should == undent(<<-XML)
|
33
|
+
<?xml version="1.0"?>
|
34
|
+
<books type="array">
|
35
|
+
<book>
|
36
|
+
<title>Sailing for old dogs</title>
|
37
|
+
</book>
|
38
|
+
<book>
|
39
|
+
<title>On the horizon</title>
|
40
|
+
</book>
|
41
|
+
<book>
|
42
|
+
<title>The Little Blue Book of VHS Programming</title>
|
43
|
+
</book>
|
44
|
+
</books>
|
45
|
+
XML
|
46
|
+
end
|
47
|
+
|
48
|
+
it "can generate JSON" do
|
49
|
+
render("books", :json, :books => Books.all).should == undent(<<-JSON)
|
50
|
+
[
|
51
|
+
{
|
52
|
+
"title": "Sailing for old dogs"
|
53
|
+
},
|
54
|
+
{
|
55
|
+
"title": "On the horizon"
|
56
|
+
},
|
57
|
+
{
|
58
|
+
"title": "The Little Blue Book of VHS Programming"
|
59
|
+
}
|
60
|
+
]
|
61
|
+
JSON
|
62
|
+
end
|
63
|
+
|
64
|
+
it "can include partials" do
|
65
|
+
|
66
|
+
write_template 'books_with_partial.rep', <<-RUBY
|
67
|
+
r.list_of :books, @books do
|
68
|
+
render :partial => 'book'
|
69
|
+
end
|
70
|
+
RUBY
|
71
|
+
|
72
|
+
write_template '_book.rep', <<-RUBY
|
73
|
+
r.element :title
|
74
|
+
r.element :published do
|
75
|
+
r.element :by
|
76
|
+
end
|
77
|
+
RUBY
|
78
|
+
|
79
|
+
render("books_with_partial", :json, :books => Books.all).should == undent(<<-JSON)
|
80
|
+
[
|
81
|
+
{
|
82
|
+
"title": "Sailing for old dogs",
|
83
|
+
"published": {
|
84
|
+
"by": "Credulous Print"
|
85
|
+
}
|
86
|
+
},
|
87
|
+
{
|
88
|
+
"title": "On the horizon",
|
89
|
+
"published": {
|
90
|
+
"by": "McGraw-Hill"
|
91
|
+
}
|
92
|
+
},
|
93
|
+
{
|
94
|
+
"title": "The Little Blue Book of VHS Programming",
|
95
|
+
"published": null
|
96
|
+
}
|
97
|
+
]
|
98
|
+
JSON
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'action_view'
|
2
|
+
require 'minstrel'
|
3
|
+
require 'representative_view'
|
4
|
+
require 'rspec'
|
5
|
+
|
6
|
+
require 'pathname'
|
7
|
+
require 'fixtures/books'
|
8
|
+
|
9
|
+
$tmp_dir = Pathname(__FILE__).parent.parent + "tmp"
|
10
|
+
$template_dir = $tmp_dir + "templates"
|
11
|
+
|
12
|
+
module Fixtures
|
13
|
+
|
14
|
+
def write_template(name, content)
|
15
|
+
template_path = $template_dir + name
|
16
|
+
template_path.parent.mkpath
|
17
|
+
template_path.open("w") do |io|
|
18
|
+
io << content
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def render(file, format = :xml, assigns = {})
|
23
|
+
@base = ActionView::Base.new($template_dir.to_str, assigns)
|
24
|
+
@base.lookup_context.freeze_formats([format])
|
25
|
+
@base.render(:file => file)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
Rspec.configure do |config|
|
31
|
+
|
32
|
+
config.mock_with :rr
|
33
|
+
config.include(Fixtures)
|
34
|
+
|
35
|
+
config.before do
|
36
|
+
$template_dir.mkpath
|
37
|
+
end
|
38
|
+
|
39
|
+
config.after do
|
40
|
+
$tmp_dir.rmtree if $tmp_dir.exist?
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
def undent(raw)
|
46
|
+
if raw =~ /\A( +)/
|
47
|
+
indent = $1
|
48
|
+
raw.gsub(/^#{indent}/, '').gsub(/ +$/, '')
|
49
|
+
else
|
50
|
+
raw
|
51
|
+
end
|
52
|
+
end
|
metadata
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: representative_view
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Mike Williams
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-02-10 00:00:00 +11:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ~>
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
hash: 17
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
- 3
|
31
|
+
- 1
|
32
|
+
version: 0.3.1
|
33
|
+
prerelease: false
|
34
|
+
type: :runtime
|
35
|
+
requirement: *id001
|
36
|
+
name: representative
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 5
|
44
|
+
segments:
|
45
|
+
- 3
|
46
|
+
- 0
|
47
|
+
- 1
|
48
|
+
version: 3.0.1
|
49
|
+
prerelease: false
|
50
|
+
type: :runtime
|
51
|
+
requirement: *id002
|
52
|
+
name: actionpack
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
60
|
+
segments:
|
61
|
+
- 1
|
62
|
+
- 4
|
63
|
+
- 2
|
64
|
+
version: 1.4.2
|
65
|
+
prerelease: false
|
66
|
+
type: :runtime
|
67
|
+
requirement: *id003
|
68
|
+
name: nokogiri
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
hash: 13
|
76
|
+
segments:
|
77
|
+
- 1
|
78
|
+
- 4
|
79
|
+
- 5
|
80
|
+
version: 1.4.5
|
81
|
+
prerelease: false
|
82
|
+
type: :runtime
|
83
|
+
requirement: *id004
|
84
|
+
name: json
|
85
|
+
description:
|
86
|
+
email: mdub@dogbiscuit.org
|
87
|
+
executables: []
|
88
|
+
|
89
|
+
extensions: []
|
90
|
+
|
91
|
+
extra_rdoc_files: []
|
92
|
+
|
93
|
+
files:
|
94
|
+
- .gitignore
|
95
|
+
- Gemfile
|
96
|
+
- README.markdown
|
97
|
+
- Rakefile
|
98
|
+
- lib/representative_view.rb
|
99
|
+
- lib/representative_view/template_handler.rb
|
100
|
+
- lib/representative_view/version.rb
|
101
|
+
- representative_view.gemspec
|
102
|
+
- spec/fixtures/books.rb
|
103
|
+
- spec/representative_view/template_handler_spec.rb
|
104
|
+
- spec/spec_helper.rb
|
105
|
+
has_rdoc: true
|
106
|
+
homepage: http://github.com/mdub/representative_view
|
107
|
+
licenses: []
|
108
|
+
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options: []
|
111
|
+
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
hash: 3
|
120
|
+
segments:
|
121
|
+
- 0
|
122
|
+
version: "0"
|
123
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
124
|
+
none: false
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
hash: 3
|
129
|
+
segments:
|
130
|
+
- 0
|
131
|
+
version: "0"
|
132
|
+
requirements: []
|
133
|
+
|
134
|
+
rubyforge_project:
|
135
|
+
rubygems_version: 1.5.0
|
136
|
+
signing_key:
|
137
|
+
specification_version: 3
|
138
|
+
summary: Builds XML and JSON from a single view template
|
139
|
+
test_files:
|
140
|
+
- spec/fixtures/books.rb
|
141
|
+
- spec/representative_view/template_handler_spec.rb
|
142
|
+
- spec/spec_helper.rb
|