client_pages 0.0.2.alpha → 0.0.3.beta
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.rubocop.yml +74 -0
- data/README.md +19 -46
- data/Rakefile +8 -1
- data/client_pages.gemspec +1 -0
- data/lib/client_pages/configuration.rb +29 -0
- data/lib/client_pages/markdown_content/content_builder.rb +64 -0
- data/lib/client_pages/markdown_content/renderer.rb +84 -0
- data/lib/client_pages/matcher.rb +39 -0
- data/lib/client_pages/template.rb +7 -0
- data/lib/client_pages/version.rb +1 -1
- data/lib/client_pages.rb +14 -196
- data/spec/fixtures/{link_list.md → content/link_list.md} +0 -0
- data/spec/fixtures/{test_content.md → content/test_content.md} +0 -0
- data/spec/fixtures/{text_list.md → content/text_list.md} +0 -0
- data/spec/fixtures/foo/bar.md +3 -0
- data/spec/integration/client_pages_spec.rb +274 -0
- data/spec/spec_helper.rb +24 -1
- data/spec/unit/configuration_spec.rb +52 -0
- data/spec/unit/content_builder_spec.rb +8 -0
- data/spec/unit/matcher_spec.rb +11 -0
- data/spec/unit/renderer_spec.rb +8 -0
- data/spec/unit/template_spec.rb +16 -0
- metadata +42 -10
- data/spec/unit/client_pages_spec.rb +0 -205
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 651585c4dbeab42b84039c22aec54878b9f54426
|
4
|
+
data.tar.gz: df24efef446ba402721bf025295c0d978103dfd4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f859d580c6ba704bec2fd9dcbb05baa4208bae91c355c6c8618f55d9a46ce32bceeaa0b4bab8622ca4b2279dcd7a6d577908a33bfc664e39e4b391b122071d6
|
7
|
+
data.tar.gz: 9812ecd03067ce9907d6fe3d0c1fd58d6625877fb1f2d44a67af5ec5535e373187abc1aa4313cb82db0184968cf472ffa59c32d0d4b14a459988942aecad49f2
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
AllCops:
|
2
|
+
Includes:
|
3
|
+
- 'Gemfile'
|
4
|
+
- 'Rakefile'
|
5
|
+
|
6
|
+
ParameterLists:
|
7
|
+
Max: 3
|
8
|
+
CountKeywordArgs: true
|
9
|
+
|
10
|
+
BlockNesting:
|
11
|
+
Max: 3
|
12
|
+
|
13
|
+
HashSyntax:
|
14
|
+
Enabled: true
|
15
|
+
|
16
|
+
StringLiterals:
|
17
|
+
Enabled: true
|
18
|
+
|
19
|
+
Void:
|
20
|
+
Enabled: true
|
21
|
+
|
22
|
+
LineLength:
|
23
|
+
Enabled: true
|
24
|
+
Max: 115
|
25
|
+
|
26
|
+
CyclomaticComplexity:
|
27
|
+
Max: 7
|
28
|
+
|
29
|
+
SpaceInsideHashLiteralBraces:
|
30
|
+
Enabled: true
|
31
|
+
|
32
|
+
AlignParameters:
|
33
|
+
Enabled: true
|
34
|
+
|
35
|
+
Blocks:
|
36
|
+
Enabled: false
|
37
|
+
|
38
|
+
SpaceAroundEqualsInParameterDefault:
|
39
|
+
Enabled: false
|
40
|
+
|
41
|
+
NumericLiterals:
|
42
|
+
Enabled: true
|
43
|
+
|
44
|
+
SpaceInsideBrackets:
|
45
|
+
Enabled: true
|
46
|
+
|
47
|
+
WordArray:
|
48
|
+
Enabled: true
|
49
|
+
|
50
|
+
Lambda:
|
51
|
+
Enabled: true
|
52
|
+
|
53
|
+
RegexpLiteral:
|
54
|
+
Enabled: true
|
55
|
+
|
56
|
+
ConstantName:
|
57
|
+
Enabled: false
|
58
|
+
|
59
|
+
TrivialAccessors:
|
60
|
+
Enabled: true
|
61
|
+
ExactNameMatch: true
|
62
|
+
|
63
|
+
Alias:
|
64
|
+
Enabled: true
|
65
|
+
|
66
|
+
Loop:
|
67
|
+
Enabled: true
|
68
|
+
|
69
|
+
AndOr:
|
70
|
+
Enabled: false
|
71
|
+
|
72
|
+
Documentation:
|
73
|
+
Enabled: false
|
74
|
+
|
data/README.md
CHANGED
@@ -1,47 +1,19 @@
|
|
1
1
|
# ClientPages
|
2
2
|
|
3
|
-
##### Todo:
|
4
|
-
|
5
|
-
- extract single files and responsibilites
|
6
|
-
- set resonable defaults, like enable table, and auto link.
|
7
|
-
- fallback option? => error handling if no such file?
|
8
|
-
- yaml content?
|
9
|
-
- builder#wrap needs conditional and multi wrap + nested wrap
|
10
|
-
- builder methoden vereinheitlichen
|
11
|
-
- modify :tag, with: {}, if: {}|->|?
|
12
|
-
- wrap :tag, with: {}, if: {}|->|?
|
13
|
-
- replace :tag, with: {}, if: {}|->|?
|
14
|
-
- c.method &block
|
15
|
-
- builder defaults? (all :p will get class x)
|
16
|
-
- maybe adjust ```render_content``` with additional parameter, like simple_form does
|
17
|
-
- defaults: :table_settings => will be stored somewhere as a block
|
18
|
-
- test new configs! (.remote, .remote_content_path, .content_path, .caching, .fallback, .i18n)
|
19
|
-
- railtie
|
20
|
-
- i18n support content/de/something, content/en/something
|
21
|
-
- extract classes
|
22
|
-
- oauth dropbox?
|
23
|
-
- logger!?
|
24
|
-
- back up command/rake task, content:pull
|
25
|
-
nimmt alle local content files und überschreibt diese mit denen der remote version (wenn verfügbar)
|
26
|
-
- travis
|
27
|
-
- codeclimate
|
28
|
-
|
29
|
-
|
30
|
-
if defined?(Rails)
|
31
|
-
html_safe all the things
|
32
|
-
|
33
3
|
---
|
4
|
+
Ever heard: **"can we change the text on the homepage by ourself?"**
|
34
5
|
|
35
|
-
|
36
|
-
|
37
|
-
---
|
6
|
+
___
|
38
7
|
|
39
|
-
with client_pages you can separate your content from html markup and css styles
|
8
|
+
with client_pages you can separate your content from html markup and css styles,
|
9
|
+
by extracting it to simple markdown files.
|
40
10
|
|
41
11
|
use it for small content parts of your app which need to be updated regulary by someone
|
42
12
|
or outsource all your content together, it's up to you.
|
43
13
|
|
44
|
-
|
14
|
+
"the principle is really easy. at the place where you call render_content(:foo),
|
15
|
+
the renderer will grap the markdown file content/foo.md and render it as html.
|
16
|
+
by passing a block to render_content the generated markup can be modified."
|
45
17
|
|
46
18
|
the main benefit of doing this: anybody can change the content, without the need for an admin backend and stuff like this.
|
47
19
|
just make it possible for them to edit the files.
|
@@ -70,6 +42,8 @@ we can accomplish all that by just giving the ```render_content(:file)``` method
|
|
70
42
|
and tell the content builder to do that for you.
|
71
43
|
**insert haml here**
|
72
44
|
|
45
|
+
if configured ```render_content``` work with raw markdown, instead of a file name.
|
46
|
+
|
73
47
|
this way, you can add a lot of behaivour by leaving your markdown content just as content with raw structure,
|
74
48
|
and the main markup and styling stays in your views.
|
75
49
|
|
@@ -139,8 +113,6 @@ sinatra?
|
|
139
113
|
c.remote_content_path = 'https://dl.dropboxusercontent.com/u/363312/rackedy_rack'
|
140
114
|
c.content_path = 'content'
|
141
115
|
c.caching = true
|
142
|
-
c.fallback = false
|
143
|
-
c.i18n = false
|
144
116
|
c.render_options = {
|
145
117
|
hard_wrap: true,
|
146
118
|
with_toc_data: false,
|
@@ -164,17 +136,15 @@ sinatra?
|
|
164
136
|
hm, or where is the problem? ```render_content("#{my_locale}/posts/index")```
|
165
137
|
|
166
138
|
### caching
|
167
|
-
the current implementation it the easiest way to do
|
168
|
-
it's really simple and lives as long as the process runs.
|
169
|
-
it can not be shared between processes
|
139
|
+
the current implementation it the easiest way to do something like caching.
|
140
|
+
it's really simple and lives as long as the process runs, as it's nothing more than a hash that doesn't get persistet.
|
141
|
+
so it can not be shared between processes.
|
170
142
|
and because one of the [hard things in Computer Science](http://martinfowler.com/bliki/TwoHardThings.html) is _cache invalidation_
|
171
|
-
you can flush the cache also at runtime
|
172
|
-
|
173
|
-
you can accomplish this with some additional code.
|
143
|
+
you can flush the cache also at runtime with
|
174
144
|
|
175
|
-
```
|
145
|
+
```cache.clear```
|
176
146
|
|
177
|
-
|
147
|
+
So if your're curious about caching, you should take care of that by yourself and cache the complete render_content block for example.
|
178
148
|
|
179
149
|
### commands
|
180
150
|
- downloader!!!
|
@@ -184,9 +154,12 @@ sinatra?
|
|
184
154
|
|
185
155
|
## Contributing
|
186
156
|
|
157
|
+
Contributions are always welcome! Doesn't matter if you fix just a typo, add some documentation/tests or refactor hole parts of the code.
|
158
|
+
As this gem is in early development, there are a lot of low hanging fruits.
|
159
|
+
|
187
160
|
+ Fork it
|
188
161
|
+ Change it
|
189
162
|
+ Test it
|
190
163
|
+ Commit it
|
191
164
|
+ Push it
|
192
|
-
+ Pull Request
|
165
|
+
+ Pull Request it
|
data/Rakefile
CHANGED
@@ -2,4 +2,11 @@ require 'bundler/gem_tasks'
|
|
2
2
|
require 'rspec/core/rake_task'
|
3
3
|
|
4
4
|
RSpec::Core::RakeTask.new(:spec)
|
5
|
-
task default: :
|
5
|
+
task default: :metrics
|
6
|
+
|
7
|
+
desc 'run some metric tools'
|
8
|
+
task metrics: :spec do
|
9
|
+
system('rubocop')
|
10
|
+
puts
|
11
|
+
system('flog lib')
|
12
|
+
end
|
data/client_pages.gemspec
CHANGED
@@ -30,5 +30,6 @@ Gem::Specification.new do |s|
|
|
30
30
|
s.add_development_dependency 'webmock', '~> 1.17.3'
|
31
31
|
s.add_development_dependency 'pry-plus', '~> 1.0.0'
|
32
32
|
s.add_development_dependency 'rubocop', '~> 0.18.1'
|
33
|
+
s.add_development_dependency 'flog', '~> 4.2.0'
|
33
34
|
s.add_development_dependency 'simplecov', '~> 0.8.2'
|
34
35
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module ClientPages
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :content_path, :remote, :remote_content_path, :caching, :fallback, :i18n, :raw_markdown
|
4
|
+
attr_reader :render_options, :markdown_extensions
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@content_path = 'content'
|
8
|
+
@render_options = {}
|
9
|
+
@markdown_extensions = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def render_options=(options)
|
13
|
+
@render_options.merge!(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def markdown_extensions=(options)
|
17
|
+
@markdown_extensions.merge!(options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_hash
|
21
|
+
# TODO: instance vars get
|
22
|
+
{
|
23
|
+
content_path: content_path,
|
24
|
+
render_options: render_options,
|
25
|
+
markdown_extensions: markdown_extensions
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module ClientPages
|
2
|
+
module MarkdownContent
|
3
|
+
class ContentBuilder
|
4
|
+
attr_accessor :html
|
5
|
+
|
6
|
+
def initialize(html)
|
7
|
+
@html = html
|
8
|
+
end
|
9
|
+
|
10
|
+
def modify(tag, options={})
|
11
|
+
html_lines do |line|
|
12
|
+
conditions(tag, line, options[:if]) do
|
13
|
+
match(tag, line, :open) { |open_tag| open_tag.gsub(/>/, "#{attributes(options)}>") }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def wrap(tag, wrapper, options={})
|
19
|
+
html_lines do |line|
|
20
|
+
conditions(tag, line, options[:if]) do
|
21
|
+
line = match(tag, line, :open) { |rest| "<#{wrapper.to_s + attributes(options)}>" + rest }
|
22
|
+
line = match(tag, line, :close) { |rest| rest + "</#{wrapper}>" }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def replace(tag, new_tag, options={})
|
28
|
+
html_lines do |line|
|
29
|
+
conditions(tag, line, options[:if]) do
|
30
|
+
line = match(tag, line, :open) { |open_tag| "<#{new_tag.to_s + attributes(options)}>" }
|
31
|
+
line = match(tag, line, :close) { |closing_tag| "</#{new_tag}>" }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def match(tag, line, state)
|
39
|
+
Matcher.new(tag, line).find(state) { |match| yield match }
|
40
|
+
end
|
41
|
+
|
42
|
+
def conditions(tag, line, condition)
|
43
|
+
return yield if condition.nil?
|
44
|
+
if condition.respond_to?(:call) && condition.call(Matcher.new(tag, line))
|
45
|
+
yield
|
46
|
+
elsif condition.is_a?(Hash) && Matcher.new(tag, line).match_all?(condition)
|
47
|
+
yield
|
48
|
+
elsif condition == true
|
49
|
+
yield
|
50
|
+
else
|
51
|
+
line
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def html_lines(&block)
|
56
|
+
self.html = html.lines.map { |line| yield line }.join
|
57
|
+
end
|
58
|
+
|
59
|
+
def attributes(options)
|
60
|
+
options.fetch(:with, {}).map { |k, v| " #{k}=\"#{v}\"" }.join
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module ClientPages
|
2
|
+
module MarkdownContent
|
3
|
+
class Renderer
|
4
|
+
class << self
|
5
|
+
def render_content(file, options = {})
|
6
|
+
html = new(file, options).html
|
7
|
+
block_given? ? yield(ContentBuilder.new(html)) : html.strip
|
8
|
+
rescue OpenURI::HTTPError
|
9
|
+
"404: could not find '#{file}'"
|
10
|
+
rescue Errno::ENOENT
|
11
|
+
"No such file or directory - #{file}"
|
12
|
+
rescue SocketError
|
13
|
+
'Error: content_path not available'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :file, :options
|
18
|
+
|
19
|
+
def initialize(file, options = {})
|
20
|
+
@file, @options = file, options
|
21
|
+
end
|
22
|
+
|
23
|
+
def html
|
24
|
+
html = html_from_markdown
|
25
|
+
html = template.call(ContentBuilder.new(html)) if options.key?(:template)
|
26
|
+
html
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def html_renderer
|
32
|
+
Redcarpet::Render::HTML.new(config.render_options)
|
33
|
+
end
|
34
|
+
|
35
|
+
def extensions
|
36
|
+
config.markdown_extensions.merge(options.key?(:extensions) ? options[:extensions] : {})
|
37
|
+
end
|
38
|
+
|
39
|
+
def html_from_markdown
|
40
|
+
Redcarpet::Markdown.new(html_renderer, extensions).render(markdown)
|
41
|
+
end
|
42
|
+
|
43
|
+
def template
|
44
|
+
ClientPages.template[options[:template]]
|
45
|
+
end
|
46
|
+
|
47
|
+
def markdown
|
48
|
+
raw? ? file : read_file
|
49
|
+
end
|
50
|
+
|
51
|
+
def url
|
52
|
+
"#{content_path}/#{file}.md"
|
53
|
+
end
|
54
|
+
|
55
|
+
def read_file
|
56
|
+
if caching?
|
57
|
+
cache.fetch(url) { cache[url] = open(url).read }
|
58
|
+
else
|
59
|
+
open(url).read
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def content_path
|
64
|
+
config.remote ? config.remote_content_path : config.content_path
|
65
|
+
end
|
66
|
+
|
67
|
+
def raw?
|
68
|
+
config.raw_markdown || options[:raw]
|
69
|
+
end
|
70
|
+
|
71
|
+
def caching?
|
72
|
+
config.caching
|
73
|
+
end
|
74
|
+
|
75
|
+
def config
|
76
|
+
ClientPages.config
|
77
|
+
end
|
78
|
+
|
79
|
+
def cache
|
80
|
+
ClientPages.cache
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module ClientPages
|
2
|
+
class Matcher
|
3
|
+
attr_accessor :tag, :line
|
4
|
+
|
5
|
+
def initialize(tag, line)
|
6
|
+
@tag, @line = tag, line
|
7
|
+
end
|
8
|
+
|
9
|
+
def match?(attribute, match)
|
10
|
+
!!line.match(/<#{tag}[^>]*#{attribute}="#{match}"/i)
|
11
|
+
end
|
12
|
+
|
13
|
+
def match_all?(attributes)
|
14
|
+
attributes.keys.all? { |key| match?(key, attributes[key]) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def match_any?(attribute, match)
|
18
|
+
!!line.match(/#{attribute}="#{match}"/i)
|
19
|
+
end
|
20
|
+
|
21
|
+
def includes?(pattern)
|
22
|
+
!!line.match(pattern)
|
23
|
+
end
|
24
|
+
|
25
|
+
def find(type)
|
26
|
+
if block_given? && includes?(tag_pattern[type])
|
27
|
+
line.gsub(tag_pattern[type]) { |match| yield match }
|
28
|
+
else
|
29
|
+
line
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def tag_pattern
|
36
|
+
{ open: /<#{tag}[^>]*>/, close: /<\/#{tag}[^>]*>/ }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/client_pages/version.rb
CHANGED
data/lib/client_pages.rb
CHANGED
@@ -4,6 +4,15 @@ require 'open-uri'
|
|
4
4
|
require 'redcarpet'
|
5
5
|
|
6
6
|
module ClientPages
|
7
|
+
autoload :Matcher, 'client_pages/matcher'
|
8
|
+
autoload :Template, 'client_pages/template'
|
9
|
+
autoload :Configuration, 'client_pages/configuration'
|
10
|
+
|
11
|
+
module MarkdownContent
|
12
|
+
autoload :ContentBuilder, 'client_pages/markdown_content/content_builder'
|
13
|
+
autoload :Renderer, 'client_pages/markdown_content/renderer'
|
14
|
+
end
|
15
|
+
|
7
16
|
class << self
|
8
17
|
attr_accessor :configuration
|
9
18
|
|
@@ -12,207 +21,16 @@ module ClientPages
|
|
12
21
|
block_given? ? yield(configuration) : configuration
|
13
22
|
end
|
14
23
|
|
15
|
-
def
|
16
|
-
@
|
17
|
-
end
|
18
|
-
|
19
|
-
def clear_cache(key=nil)
|
20
|
-
if key
|
21
|
-
cache.delete(key)
|
22
|
-
"cache cleared: #{key}"
|
23
|
-
else
|
24
|
-
cache.clear
|
25
|
-
'cache cleared'
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
module MarkdownContent
|
31
|
-
class MarkdownContentRenderer
|
32
|
-
include ClientPages::MarkdownContent
|
33
|
-
end
|
34
|
-
|
35
|
-
def self.render_content(file, options={})
|
36
|
-
MarkdownContentRenderer.new.render_content(file, options)
|
37
|
-
end
|
38
|
-
|
39
|
-
class Matcher
|
40
|
-
attr_accessor :tag, :line
|
41
|
-
|
42
|
-
def initialize(tag, line)
|
43
|
-
@tag, @line = tag, line
|
44
|
-
end
|
45
|
-
|
46
|
-
def exact_match?(attribute, match)
|
47
|
-
!!line.match(/<#{tag}[^>]*#{attribute}="#{match}"/i)
|
48
|
-
end
|
49
|
-
|
50
|
-
def attr?(attribute, match)
|
51
|
-
!!line.match(/#{attribute}="#{match}"/i)
|
52
|
-
end
|
53
|
-
|
54
|
-
def match?(pattern)
|
55
|
-
!!line.match(pattern)
|
56
|
-
end
|
57
|
-
|
58
|
-
def find(type)
|
59
|
-
pattern = { opening: /<#{tag}[^>]*>/, closing: /<\/#{tag}[^>]*>/ }[type]
|
60
|
-
if block_given? && !!line.match(pattern)
|
61
|
-
line.gsub(pattern) { |match| yield match }
|
62
|
-
else
|
63
|
-
line
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
class ContentBuilder
|
69
|
-
attr_accessor :html
|
70
|
-
|
71
|
-
def initialize(html)
|
72
|
-
@html = html
|
73
|
-
end
|
74
|
-
|
75
|
-
def modify(tag, options={}, conditions=true)
|
76
|
-
if conditions.respond_to? :call
|
77
|
-
html_lines do |line|
|
78
|
-
if conditions.call(Matcher.new(tag, line))
|
79
|
-
set_options_for_tags_per_line(options, tag, line)
|
80
|
-
else
|
81
|
-
line
|
82
|
-
end
|
83
|
-
end
|
84
|
-
elsif conditions.is_a?(Hash)
|
85
|
-
html_lines do |line|
|
86
|
-
if conditions.keys.all? { |key| Matcher.new(tag, line).exact_match?(key, conditions[key]) }
|
87
|
-
set_options_for_tags_per_line(options, tag, line)
|
88
|
-
else
|
89
|
-
line
|
90
|
-
end
|
91
|
-
end
|
92
|
-
elsif !!conditions
|
93
|
-
set_options_for_tags(options, tag)
|
94
|
-
end
|
95
|
-
self.html
|
96
|
-
end
|
97
|
-
|
98
|
-
def wrap(tag, options={})
|
99
|
-
html_lines do |line|
|
100
|
-
line = Matcher.new(tag, line).find(:opening) do |match|
|
101
|
-
"<#{options[:wrapper]} class=\"#{options[:class]}\">#{match}"
|
102
|
-
end
|
103
|
-
line = Matcher.new(tag, line).find(:closing) do |match|
|
104
|
-
"#{match}</#{options[:wrapper]}>"
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def replace(tag, new_tag, options={})
|
110
|
-
html_lines do |line|
|
111
|
-
line = Matcher.new(tag, line).find(:opening) do |match|
|
112
|
-
"<#{new_tag} class=\"#{options[:class]}\">"
|
113
|
-
end
|
114
|
-
line = Matcher.new(tag, line).find(:closing) do |match|
|
115
|
-
"</#{new_tag}>"
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end # ContentBuilder
|
119
|
-
|
120
|
-
private
|
121
|
-
|
122
|
-
def html_lines(&block)
|
123
|
-
self.html = html.lines.map { |line| yield line }.join
|
124
|
-
end
|
125
|
-
|
126
|
-
def set_options_for_tags(options, tag)
|
127
|
-
html_lines { |line| set_options_for_tags_per_line(options, tag, line) }
|
128
|
-
end
|
129
|
-
|
130
|
-
def set_options_for_tags_per_line(options, tag, line)
|
131
|
-
options.each { |k, v| line = line.gsub(/<#{tag}/, "<#{tag} #{k}=\"#{v}\"") }
|
132
|
-
line
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
def render_content(file, options = markdown_extensions)
|
137
|
-
html = Redcarpet::Markdown.new(html_renderer, options).render(markdown(file))
|
138
|
-
if block_given?
|
139
|
-
yield(ContentBuilder.new(html))
|
140
|
-
else
|
141
|
-
html.strip
|
142
|
-
end
|
143
|
-
rescue OpenURI::HTTPError
|
144
|
-
"404: could not find '#{file}'"
|
145
|
-
rescue Errno::ENOENT => e
|
146
|
-
e.message
|
147
|
-
rescue SocketError
|
148
|
-
'Error: content_path not available'
|
24
|
+
def template
|
25
|
+
@template ||= {}
|
149
26
|
end
|
150
27
|
|
151
|
-
|
152
|
-
|
153
|
-
def html_renderer
|
154
|
-
Redcarpet::Render::HTML.new(config.render_options)
|
155
|
-
end
|
156
|
-
|
157
|
-
def markdown_extensions
|
158
|
-
config.markdown_extensions
|
159
|
-
end
|
160
|
-
|
161
|
-
def markdown(file)
|
162
|
-
url = "#{content_path}/#{file}.md"
|
163
|
-
if caching?
|
164
|
-
cache.fetch(url) { cache[url] = open(url).read }
|
165
|
-
else
|
166
|
-
open(url).read
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
def config
|
171
|
-
ClientPages.config
|
28
|
+
def templates(&block)
|
29
|
+
yield Template.new
|
172
30
|
end
|
173
31
|
|
174
32
|
def cache
|
175
|
-
|
176
|
-
end
|
177
|
-
|
178
|
-
def caching?
|
179
|
-
config.caching
|
180
|
-
end
|
181
|
-
|
182
|
-
def remote?
|
183
|
-
config.remote
|
184
|
-
end
|
185
|
-
|
186
|
-
def content_path
|
187
|
-
remote? ? config.remote_content_path : config.content_path
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
class Configuration
|
192
|
-
attr_accessor :content_path, :remote, :remote_content_path, :caching, :fallback, :i18n
|
193
|
-
attr_reader :render_options, :markdown_extensions
|
194
|
-
|
195
|
-
def initialize
|
196
|
-
@content_path = 'content'
|
197
|
-
@render_options = {}
|
198
|
-
@markdown_extensions = {}
|
199
|
-
end
|
200
|
-
|
201
|
-
def render_options=(options)
|
202
|
-
@render_options.merge!(options)
|
203
|
-
end
|
204
|
-
|
205
|
-
def markdown_extensions=(options)
|
206
|
-
@markdown_extensions.merge!(options)
|
207
|
-
end
|
208
|
-
|
209
|
-
def to_hash
|
210
|
-
# TODO: instance vars get
|
211
|
-
{
|
212
|
-
content_path: content_path,
|
213
|
-
render_options: render_options,
|
214
|
-
markdown_extensions: markdown_extensions
|
215
|
-
}
|
33
|
+
@cache ||= {}
|
216
34
|
end
|
217
35
|
end
|
218
36
|
end
|
File without changes
|
File without changes
|
File without changes
|