client_pages 0.0.1.alpha
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 +7 -0
- data/.gitignore +20 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +150 -0
- data/Rakefile +5 -0
- data/client_pages.gemspec +34 -0
- data/lib/client_pages/version.rb +3 -0
- data/lib/client_pages.rb +208 -0
- data/spec/fixtures/link_list.md +3 -0
- data/spec/fixtures/test_content.md +5 -0
- data/spec/fixtures/text_list.md +2 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/unit/client_pages_spec.rb +199 -0
- metadata +179 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: dc3c627266b1e9187280a19b95fd613e73f08acc
|
4
|
+
data.tar.gz: 9c72b430526090a8c73866c6fef996b7e9b2e003
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e01fbb81395367d36f4217fa7221841e1881f6bedca4d0ebb9c3ebec09e3aa0687ea2f366bf5011ef429669fe72206abdcfdcea281f2101455d3a46fcd107a97
|
7
|
+
data.tar.gz: e471af6747143523d2c94fd5de271d981f16f4011dd1faff736b2028e8e6625414a3f43edfabfd88efa754b901cf68b47fb7321ca77238d83c39998b6d47bd47
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Marco Schaden
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
# ClientPages
|
2
|
+
|
3
|
+
##### Todo:
|
4
|
+
|
5
|
+
- fallback option? => error handling if no such file?
|
6
|
+
- yaml content?
|
7
|
+
- builder#wrap needs conditional and multi wrap + nested wrap
|
8
|
+
- builder methoden vereinheitlichen
|
9
|
+
- builder defaults? (all :p will get class x)
|
10
|
+
- > maybe adjust ```render_content``` with additional parameter, like simple_form does
|
11
|
+
- > defaults: :table_settings => will be stored somewhere as a block
|
12
|
+
- test new configs! (.remote, .remote_content_path, .content_path, .caching, .fallback, .i18n)
|
13
|
+
- railtie
|
14
|
+
- i18n support content/de/something, content/en/something
|
15
|
+
- extract classes
|
16
|
+
- oauth dropbox?
|
17
|
+
- logger!?
|
18
|
+
- back up command/rake task, content:pull
|
19
|
+
nimmt alle local content files und überschreibt diese mit denen der remote version (wenn verfügbar)
|
20
|
+
- travis
|
21
|
+
- codeclimate
|
22
|
+
|
23
|
+
---
|
24
|
+
|
25
|
+
# Ideas/Notes:
|
26
|
+
|
27
|
+
---
|
28
|
+
|
29
|
+
with client_pages you can separate your content from html markup and css styles.
|
30
|
+
|
31
|
+
use it for small content parts of your app which need to be updated regulary by someone
|
32
|
+
or outsource all your content together, it's up to you.
|
33
|
+
|
34
|
+
**Ever heard "can we change the text on page x?"**
|
35
|
+
|
36
|
+
the main benefit of doing this: anybody can change the content, without the need for an admin backend and stuff like this.
|
37
|
+
just make it possible for them to edit the files.
|
38
|
+
|
39
|
+
from the beginning client pages was built for supporting remote files.
|
40
|
+
an easy example could be a public shared dropbox folder (or anything that can deliver files),
|
41
|
+
|
42
|
+
a sample configuration could look like this:
|
43
|
+
here we set ```bla = true``` to enable ...
|
44
|
+
|
45
|
+
btw, the core markdown parser is redcarpet and you can customize the behaviour
|
46
|
+
of it like you would normaly do by setting renderer and extensions through the config. (~ naaa, formulierung)
|
47
|
+
|
48
|
+
but the power of client_pages comes from its helper methods for rendering content
|
49
|
+
|
50
|
+
### simple example
|
51
|
+
|
52
|
+
imaginge you have this markdown file:
|
53
|
+
**insert markdown here**
|
54
|
+
|
55
|
+
Let's say you want a class on each list item, and a data attribute on that link.
|
56
|
+
And maybe a 'active' class on any link, when it's the current path?
|
57
|
+
It would also be nice to wrap each paragraph in a container?
|
58
|
+
|
59
|
+
we can accomplish all that by just giving the ```render_content(:file)``` method a block
|
60
|
+
and tell the content builder to do that for you.
|
61
|
+
**insert haml here**
|
62
|
+
|
63
|
+
this way, you can add a lot of behaivour by leaving your markdown content just as content with raw structure,
|
64
|
+
and the main markup and styling stays in your views.
|
65
|
+
|
66
|
+
### Installation and setup
|
67
|
+
|
68
|
+
Add this line to your application's Gemfile:
|
69
|
+
|
70
|
+
gem 'client_pages'
|
71
|
+
|
72
|
+
And then execute:
|
73
|
+
|
74
|
+
$ bundle
|
75
|
+
|
76
|
+
Or install it yourself as:
|
77
|
+
|
78
|
+
$ gem install client_pages
|
79
|
+
|
80
|
+
rails project?
|
81
|
+
|
82
|
+
add a content directory and your done, per default it's _content_
|
83
|
+
maybe you like to configure this to app/content or app/views/content
|
84
|
+
totaly up to you.
|
85
|
+
|
86
|
+
no framework? pure rack? sinatra?
|
87
|
+
|
88
|
+
for convenience you can add ``` include ClientPages::MarkdownContent ``` to your application controller/view_helper/presenter/... or something which is responsible for building html
|
89
|
+
so that you can call ```render_content``` there.
|
90
|
+
|
91
|
+
or if you're riding the real blank slate, just call ```ClientPages::MarkdownContent.render_content``` directly.
|
92
|
+
|
93
|
+
### renderer
|
94
|
+
if you call ```render_content``` without a block it just renders the markdown to html
|
95
|
+
|
96
|
+
```render_content(:index)```
|
97
|
+
|
98
|
+
```render_content('posts/index')```
|
99
|
+
|
100
|
+
### builder
|
101
|
+
- modify
|
102
|
+
- wrap
|
103
|
+
- switch?
|
104
|
+
|
105
|
+
### matcher
|
106
|
+
- implicit attribute match
|
107
|
+
- explicit match
|
108
|
+
- content match? (hole file?)
|
109
|
+
|
110
|
+
### config
|
111
|
+
config.ru file or initializer
|
112
|
+
|
113
|
+
- by default...
|
114
|
+
- content_path
|
115
|
+
- if you go the full remote path without fallback, you might consider to turn on caching.
|
116
|
+
|
117
|
+
### i18n
|
118
|
+
per default client_pages looks in your content_path for /locale/...
|
119
|
+
- is the locale a config_setting?
|
120
|
+
- how to tell client_pages the locale?
|
121
|
+
|
122
|
+
hm, or where is the problem? ```render_content("#{my_locale}/posts/index")```
|
123
|
+
|
124
|
+
### caching
|
125
|
+
the current implementation it the easiest way to do this.
|
126
|
+
it's really simple and lives as long as the process runs.
|
127
|
+
it can not be shared between processes
|
128
|
+
and because one of the [hard things in Computer Science](http://martinfowler.com/bliki/TwoHardThings.html) is _cache invalidation_
|
129
|
+
you can flush the cache also at runtime either complete or by passing a file_path (which is the cache key):
|
130
|
+
|
131
|
+
you can accomplish this with some additional code.
|
132
|
+
|
133
|
+
```code follows```
|
134
|
+
|
135
|
+
or run the corresponding command. (not implemented yet)
|
136
|
+
|
137
|
+
### commands
|
138
|
+
- downloader!!!
|
139
|
+
- cache killer?
|
140
|
+
|
141
|
+
----
|
142
|
+
|
143
|
+
## Contributing
|
144
|
+
|
145
|
+
+ Fork it
|
146
|
+
+ Change it
|
147
|
+
+ Test it
|
148
|
+
+ Commit it
|
149
|
+
+ Push it
|
150
|
+
+ Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'client_pages/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = 'client_pages'
|
8
|
+
s.version = ClientPages::VERSION
|
9
|
+
s.authors = ['Marco Schaden']
|
10
|
+
s.email = ['ms@donschado.de']
|
11
|
+
s.summary = %q{With client_pages you can separate your content from html markup and css styles}
|
12
|
+
s.description = <<-EOF
|
13
|
+
With client_pages you can separate your content from html markup and css styles.
|
14
|
+
use it for small content parts of your app which need to be updated regulary by someone
|
15
|
+
or outsource all your content together, it's up to you.
|
16
|
+
EOF
|
17
|
+
s.homepage = ''
|
18
|
+
s.license = 'MIT'
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split($/)
|
21
|
+
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
22
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
23
|
+
s.require_paths = ['lib']
|
24
|
+
|
25
|
+
s.add_dependency 'redcarpet', '~> 3.1.1'
|
26
|
+
|
27
|
+
s.add_development_dependency 'bundler', '~> 1.5.3'
|
28
|
+
s.add_development_dependency 'rake', '~> 10.1.1'
|
29
|
+
s.add_development_dependency 'rspec', '~> 2.14.1'
|
30
|
+
s.add_development_dependency 'webmock', '~> 1.17.3'
|
31
|
+
s.add_development_dependency 'pry-plus', '~> 1.0.0'
|
32
|
+
s.add_development_dependency 'rubocop', '~> 0.18.1'
|
33
|
+
s.add_development_dependency 'simplecov', '~> 0.8.2'
|
34
|
+
end
|
data/lib/client_pages.rb
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'client_pages/version'
|
3
|
+
require 'open-uri'
|
4
|
+
require 'redcarpet'
|
5
|
+
|
6
|
+
module ClientPages
|
7
|
+
class << self
|
8
|
+
attr_accessor :configuration
|
9
|
+
|
10
|
+
def config
|
11
|
+
self.configuration ||= Configuration.new
|
12
|
+
block_given? ? yield(configuration) : configuration
|
13
|
+
end
|
14
|
+
|
15
|
+
def cache
|
16
|
+
@cache ||= {}
|
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 Matcher
|
32
|
+
attr_accessor :tag, :line
|
33
|
+
|
34
|
+
def initialize(tag, line)
|
35
|
+
@tag, @line = tag, line
|
36
|
+
end
|
37
|
+
|
38
|
+
def exact_match?(attribute, match)
|
39
|
+
!!line.match(/<#{tag}[^>]*#{attribute}="#{match}"/i)
|
40
|
+
end
|
41
|
+
|
42
|
+
def attr?(attribute, match)
|
43
|
+
!!line.match(/#{attribute}="#{match}"/i)
|
44
|
+
end
|
45
|
+
|
46
|
+
def match?(pattern)
|
47
|
+
!!line.match(pattern)
|
48
|
+
end
|
49
|
+
|
50
|
+
def find(type)
|
51
|
+
pattern = { opening: /<#{tag}[^>]*>/, closing: /<\/#{tag}[^>]*>/ }[type]
|
52
|
+
if block_given? && !!line.match(pattern)
|
53
|
+
line.gsub(pattern) { |match| yield match }
|
54
|
+
else
|
55
|
+
line
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class ContentBuilder
|
61
|
+
attr_accessor :html
|
62
|
+
|
63
|
+
def initialize(html)
|
64
|
+
@html = html
|
65
|
+
end
|
66
|
+
|
67
|
+
def modify(tag, options={}, conditions=true)
|
68
|
+
if conditions.respond_to? :call
|
69
|
+
html_lines do |line|
|
70
|
+
if conditions.call(Matcher.new(tag, line))
|
71
|
+
set_options_for_tags_per_line(options, tag, line)
|
72
|
+
else
|
73
|
+
line
|
74
|
+
end
|
75
|
+
end
|
76
|
+
elsif conditions.is_a?(Hash)
|
77
|
+
html_lines do |line|
|
78
|
+
if conditions.keys.all? { |key| Matcher.new(tag, line).exact_match?(key, conditions[key]) }
|
79
|
+
set_options_for_tags_per_line(options, tag, line)
|
80
|
+
else
|
81
|
+
line
|
82
|
+
end
|
83
|
+
end
|
84
|
+
elsif !!conditions
|
85
|
+
set_options_for_tags(options, tag)
|
86
|
+
end
|
87
|
+
self.html
|
88
|
+
end
|
89
|
+
|
90
|
+
def wrap(tag, options={})
|
91
|
+
html_lines do |line|
|
92
|
+
line = Matcher.new(tag, line).find(:opening) do |match|
|
93
|
+
"<#{options[:wrapper]} class=\"#{options[:class]}\">#{match}"
|
94
|
+
end
|
95
|
+
line = Matcher.new(tag, line).find(:closing) do |match|
|
96
|
+
"#{match}</#{options[:wrapper]}>"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def replace(tag, new_tag, options={})
|
102
|
+
html_lines do |line|
|
103
|
+
line = Matcher.new(tag, line).find(:opening) do |match|
|
104
|
+
"<#{new_tag} class=\"#{options[:class]}\">"
|
105
|
+
end
|
106
|
+
line = Matcher.new(tag, line).find(:closing) do |match|
|
107
|
+
"</#{new_tag}>"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end # ContentBuilder
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def html_lines(&block)
|
115
|
+
self.html = html.lines.map { |line| yield line }.join
|
116
|
+
end
|
117
|
+
|
118
|
+
def set_options_for_tags(options, tag)
|
119
|
+
html_lines { |line| set_options_for_tags_per_line(options, tag, line) }
|
120
|
+
end
|
121
|
+
|
122
|
+
def set_options_for_tags_per_line(options, tag, line)
|
123
|
+
options.each { |k, v| line = line.gsub(/<#{tag}/, "<#{tag} #{k}=\"#{v}\"") }
|
124
|
+
line
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def render_content(file, options = markdown_extensions)
|
129
|
+
html = Redcarpet::Markdown.new(html_renderer, options).render(markdown(file))
|
130
|
+
if block_given?
|
131
|
+
yield(ContentBuilder.new(html))
|
132
|
+
else
|
133
|
+
html.strip
|
134
|
+
end
|
135
|
+
rescue OpenURI::HTTPError
|
136
|
+
"404: could not find '#{file}'"
|
137
|
+
rescue SocketError
|
138
|
+
'Error: content_path not available'
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
def html_renderer
|
144
|
+
Redcarpet::Render::HTML.new(config.render_options)
|
145
|
+
end
|
146
|
+
|
147
|
+
def markdown_extensions
|
148
|
+
config.markdown_extensions
|
149
|
+
end
|
150
|
+
|
151
|
+
def markdown(file)
|
152
|
+
url = "#{content_path}/#{file}.md"
|
153
|
+
if caching?
|
154
|
+
cache.fetch(url) { cache[url] = open(url).read }
|
155
|
+
else
|
156
|
+
open(url).read
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def config
|
161
|
+
ClientPages.config
|
162
|
+
end
|
163
|
+
|
164
|
+
def cache
|
165
|
+
ClientPages.cache
|
166
|
+
end
|
167
|
+
|
168
|
+
def caching?
|
169
|
+
config.caching
|
170
|
+
end
|
171
|
+
|
172
|
+
def remote?
|
173
|
+
config.remote
|
174
|
+
end
|
175
|
+
|
176
|
+
def content_path
|
177
|
+
remote? ? config.remote_content_path : config.content_path
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
class Configuration
|
182
|
+
attr_accessor :content_path, :remote, :remote_content_path, :caching, :fallback, :i18n
|
183
|
+
attr_reader :render_options, :markdown_extensions
|
184
|
+
|
185
|
+
def initialize
|
186
|
+
@content_path = 'content'
|
187
|
+
@render_options = {}
|
188
|
+
@markdown_extensions = {}
|
189
|
+
end
|
190
|
+
|
191
|
+
def render_options=(options)
|
192
|
+
@render_options.merge!(options)
|
193
|
+
end
|
194
|
+
|
195
|
+
def markdown_extensions=(options)
|
196
|
+
@markdown_extensions.merge!(options)
|
197
|
+
end
|
198
|
+
|
199
|
+
def to_hash
|
200
|
+
# TODO: instance vars get
|
201
|
+
{
|
202
|
+
content_path: content_path,
|
203
|
+
render_options: render_options,
|
204
|
+
markdown_extensions: markdown_extensions
|
205
|
+
}
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
SimpleCov.start do
|
3
|
+
add_filter '/spec/'
|
4
|
+
end
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
7
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
8
|
+
require 'client_pages'
|
9
|
+
|
10
|
+
require 'webmock/rspec'
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
14
|
+
config.run_all_when_everything_filtered = true
|
15
|
+
config.filter_run :focus
|
16
|
+
config.order = 'random' # --seed 1234
|
17
|
+
|
18
|
+
config.expect_with :rspec do |c|
|
19
|
+
c.syntax = :expect
|
20
|
+
end
|
21
|
+
|
22
|
+
config.mock_with :rspec do |c|
|
23
|
+
c.syntax = :expect
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# helper for markdown fixture files
|
28
|
+
def fixture(name)
|
29
|
+
File.open(File.expand_path("spec/fixtures/#{name}.md"), 'r')
|
30
|
+
end
|
@@ -0,0 +1,199 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module ClientPages
|
4
|
+
describe MarkdownContent do
|
5
|
+
describe 'Renderer' do
|
6
|
+
subject { Class.send(:include, ClientPages::MarkdownContent).new }
|
7
|
+
let(:markdown) { fixture('test_content') }
|
8
|
+
let(:content) { subject.render_content(:test_content) }
|
9
|
+
|
10
|
+
before do
|
11
|
+
ClientPages.config do |c|
|
12
|
+
c.remote = true
|
13
|
+
c.caching = false
|
14
|
+
c.remote_content_path = 'http://somewhere.com'
|
15
|
+
end
|
16
|
+
stub_request(:get, 'http://somewhere.com/test_content.md').
|
17
|
+
to_return(body: markdown, status: 200)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'renders HTML content for given markdown file from somewhere' do
|
21
|
+
expect(content).to include('<h1>Content from <a href="http://somewhere.com">somewhere</a></h1>')
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'with a block' do
|
25
|
+
let(:link_list) { fixture('link_list') }
|
26
|
+
let(:text_list) { fixture('text_list') }
|
27
|
+
before do
|
28
|
+
stub_request(:get, 'http://somewhere.com/link_list.md').to_return(body: link_list, status: 200)
|
29
|
+
stub_request(:get, 'http://somewhere.com/text_list.md').to_return(body: text_list, status: 200)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'returns a content builder' do
|
33
|
+
expect(
|
34
|
+
subject.render_content(:test_content) { |c| c }
|
35
|
+
).to be_kind_of(MarkdownContent::ContentBuilder)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'modifies tags and html attributes' do
|
39
|
+
expect(
|
40
|
+
subject.render_content(:test_content) do |c|
|
41
|
+
c.modify :h1, class: 'mycss_class', 'data-foo' => 'bar'
|
42
|
+
c.modify :a, class: 'fancy', title: 'woah'
|
43
|
+
end
|
44
|
+
).to include('<h1 data-foo="bar" class="mycss_class">Content from <a title="woah" class="fancy" href="http://somewhere.com">somewhere</a></h1>')
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'adds css classes conditionally' do
|
48
|
+
expect(
|
49
|
+
subject.render_content(:test_content) do |c|
|
50
|
+
c.modify :h1, { class: 'add_me cool' }, true
|
51
|
+
c.modify :a, { class: 'add_me_not' }, false
|
52
|
+
end
|
53
|
+
).to include('<h1 class="add_me cool">Content from <a href="http://somewhere.com">somewhere</a></h1>')
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'adds any attribute conditionally and selective' do
|
57
|
+
expect(
|
58
|
+
subject.render_content(:link_list) do |c|
|
59
|
+
c.modify :li, { class: 'yay' }, ->(m) { m.attr?(:href, '/link1') }
|
60
|
+
c.modify :a, { class: 'active' }, href: '/link2'
|
61
|
+
c.modify :a, { class: 'important' }, title: 'i haz title'
|
62
|
+
end
|
63
|
+
).to include("<li class=\"yay\"><a href=\"/link1\">link1</a></li>\n<li><a class=\"active\" href=\"/link2\">link2</a></li>\n<li><a class=\"important\" href=\"/link3\" title=\"i haz title\">link3</a></li>")
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'wraps multiple tags with container' do
|
67
|
+
expect(
|
68
|
+
subject.render_content(:text_list) do |c|
|
69
|
+
c.wrap :li, wrapper: :span, class: 'wrapper'
|
70
|
+
c.wrap :em, wrapper: :span, class: 'special'
|
71
|
+
end
|
72
|
+
).to eql("<ul>\n<span class=\"wrapper\"><li>Foo <span class=\"special\"><em>Bar</em></span></li></span>\n<span class=\"wrapper\"><li>Baz</li></span>\n</ul>\n")
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'wraps and modifies tags' do
|
76
|
+
expect(
|
77
|
+
subject.render_content(:text_list) do |c|
|
78
|
+
c.wrap :ul, wrapper: :div, class: 'container'
|
79
|
+
c.modify :em, style: 'color: #c0ffee'
|
80
|
+
end
|
81
|
+
).to eql("<div class=\"container\"><ul>\n<li>Foo <em style=\"color: #c0ffee\">Bar</em></li>\n<li>Baz</li>\n</ul></div>\n")
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'replaces tags and adds class' do
|
85
|
+
expect(
|
86
|
+
subject.render_content(:test_content) do |c|
|
87
|
+
c.replace :p, :div, class: 'byebye'
|
88
|
+
c.replace :em, :span, class: 'cooler'
|
89
|
+
end
|
90
|
+
).to eql("<h1>Content from <a href=\"http://somewhere.com\">somewhere</a></h1>\n\n<div class=\"byebye\">Lorem do <span class=\"cooler\">incididunt</span> ut labore</div>\n\n<div class=\"byebye\">Pariatur: <a href=\"/bar.com\" title=\"baz\">Foo</a> culpa</div>\n")
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "caching" do
|
95
|
+
before do
|
96
|
+
ClientPages.config do |c|
|
97
|
+
c.caching = true
|
98
|
+
c.remote = true
|
99
|
+
c.remote_content_path = "http://c.ly"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
after { ClientPages.config { |c| c.caching = false } }
|
103
|
+
before(:each) { ClientPages.clear_cache }
|
104
|
+
|
105
|
+
it 'populates cache when empty' do
|
106
|
+
request = stub_request(:get, %r[/caching.md]).to_return(body: '**cache me**')
|
107
|
+
expect { subject.render_content(:caching) }.to change { ClientPages.cache }.to eql({"http://c.ly/caching.md"=>"**cache me**"})
|
108
|
+
expect(request).to have_been_made.times(1)
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'makes request only once' do
|
112
|
+
request = stub_request(:get, %r[/caching.md]).to_return(body: '**cache me**')
|
113
|
+
5.times { subject.render_content(:caching) }
|
114
|
+
expect { subject.render_content(:caching) }.to_not change { ClientPages.cache }
|
115
|
+
expect(request).to have_been_made.times(1)
|
116
|
+
end
|
117
|
+
|
118
|
+
context 'disabled' do
|
119
|
+
before do
|
120
|
+
ClientPages.config do |c|
|
121
|
+
c.caching = false
|
122
|
+
c.remote = true
|
123
|
+
c.remote_content_path = "http://c.ly"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'will not cache' do
|
128
|
+
stub_request(:get, %r[/no_caching.md]).to_return(body: '_u_no_cache_me_')
|
129
|
+
subject.render_content(:no_caching)
|
130
|
+
expect(ClientPages.cache).to be_empty
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'makes a request per render_content call' do
|
134
|
+
request = stub_request(:get, %r[/no_caching.md]).to_return(body: '_u_no_cache_me_')
|
135
|
+
5.times { subject.render_content(:no_caching) }
|
136
|
+
expect(request).to have_been_made.times(5)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe 'configuration' do
|
143
|
+
subject { ClientPages }
|
144
|
+
before { subject.configuration = nil }
|
145
|
+
|
146
|
+
it 'has default config' do
|
147
|
+
config = {
|
148
|
+
content_path: 'content',
|
149
|
+
render_options: {},
|
150
|
+
markdown_extensions: {}
|
151
|
+
}
|
152
|
+
expect(subject.config.to_hash).to eql(config)
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'is configurable' do
|
156
|
+
config = {
|
157
|
+
content_path: 'somewhere',
|
158
|
+
render_options: { foo: "bar" },
|
159
|
+
markdown_extensions: { bam: true }
|
160
|
+
}
|
161
|
+
expect {
|
162
|
+
subject.config do |c|
|
163
|
+
c.content_path = 'somewhere'
|
164
|
+
c.render_options = { foo: 'bar' }
|
165
|
+
c.markdown_extensions = { bam: true }
|
166
|
+
end
|
167
|
+
}.to change {
|
168
|
+
subject.config.to_hash
|
169
|
+
}.to eql(config)
|
170
|
+
end
|
171
|
+
|
172
|
+
describe 'overwritable config' do
|
173
|
+
before do
|
174
|
+
subject.config do |c|
|
175
|
+
c.render_options = { opt1: true }
|
176
|
+
c.markdown_extensions = { ext1: true }
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'overwrites config and keeps untouched config settings' do
|
181
|
+
config = {
|
182
|
+
content_path: 'here',
|
183
|
+
render_options: { opt1: false, opt2: true },
|
184
|
+
markdown_extensions: { ext1: true, ext2: false }
|
185
|
+
}
|
186
|
+
expect {
|
187
|
+
subject.config do |c|
|
188
|
+
c.content_path = 'here'
|
189
|
+
c.render_options = { opt1: false, opt2: true }
|
190
|
+
c.markdown_extensions = { ext2: false }
|
191
|
+
end
|
192
|
+
}.to change {
|
193
|
+
subject.config.to_hash
|
194
|
+
}.to eql(config)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
metadata
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: client_pages
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.alpha
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Marco Schaden
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-02-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: redcarpet
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.1.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.1.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.5.3
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.5.3
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 10.1.1
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 10.1.1
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.14.1
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.14.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: webmock
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.17.3
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.17.3
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pry-plus
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.0.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.0.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.18.1
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.18.1
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: simplecov
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 0.8.2
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ~>
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 0.8.2
|
125
|
+
description: |2
|
126
|
+
With client_pages you can separate your content from html markup and css styles.
|
127
|
+
use it for small content parts of your app which need to be updated regulary by someone
|
128
|
+
or outsource all your content together, it's up to you.
|
129
|
+
email:
|
130
|
+
- ms@donschado.de
|
131
|
+
executables: []
|
132
|
+
extensions: []
|
133
|
+
extra_rdoc_files: []
|
134
|
+
files:
|
135
|
+
- .gitignore
|
136
|
+
- Gemfile
|
137
|
+
- LICENSE.txt
|
138
|
+
- README.md
|
139
|
+
- Rakefile
|
140
|
+
- client_pages.gemspec
|
141
|
+
- lib/client_pages.rb
|
142
|
+
- lib/client_pages/version.rb
|
143
|
+
- spec/fixtures/link_list.md
|
144
|
+
- spec/fixtures/test_content.md
|
145
|
+
- spec/fixtures/text_list.md
|
146
|
+
- spec/spec_helper.rb
|
147
|
+
- spec/unit/client_pages_spec.rb
|
148
|
+
homepage: ''
|
149
|
+
licenses:
|
150
|
+
- MIT
|
151
|
+
metadata: {}
|
152
|
+
post_install_message:
|
153
|
+
rdoc_options: []
|
154
|
+
require_paths:
|
155
|
+
- lib
|
156
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - '>='
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - '>'
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: 1.3.1
|
166
|
+
requirements: []
|
167
|
+
rubyforge_project:
|
168
|
+
rubygems_version: 2.1.11
|
169
|
+
signing_key:
|
170
|
+
specification_version: 4
|
171
|
+
summary: With client_pages you can separate your content from html markup and css
|
172
|
+
styles
|
173
|
+
test_files:
|
174
|
+
- spec/fixtures/link_list.md
|
175
|
+
- spec/fixtures/test_content.md
|
176
|
+
- spec/fixtures/text_list.md
|
177
|
+
- spec/spec_helper.rb
|
178
|
+
- spec/unit/client_pages_spec.rb
|
179
|
+
has_rdoc:
|