inverter 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CONTRIBUTING.md +24 -0
- data/Gemfile +3 -0
- data/LICENSE.md +21 -0
- data/README.md +149 -0
- data/Rakefile +3 -0
- data/app/assets/javascripts/chr/form/input-inverter.coffee +91 -0
- data/app/assets/javascripts/inverter.coffee +1 -0
- data/inverter.gemspec +32 -0
- data/lib/inverter/concerns/inverter.rb +168 -0
- data/lib/inverter/configuration.rb +23 -0
- data/lib/inverter/controller_helper.rb +26 -0
- data/lib/inverter/engine.rb +5 -0
- data/lib/inverter/object.rb +39 -0
- data/lib/inverter/parser.rb +115 -0
- data/lib/inverter/template.rb +24 -0
- data/lib/inverter/version.rb +3 -0
- data/lib/inverter.rb +18 -0
- metadata +92 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 03a7814b4484ca367489034919d4eb9ad5a6ffe4
|
4
|
+
data.tar.gz: 349b6b9dffb7920fb5b9c4abf8d4eb79447f0b85
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d1515cb9f128349bcbe89e7b3f613a72933bd7146178e65c24feb280f4d9252d8994059af7df7157fbead8588cf6d585e82a61d8c693b4b84e0604c519ca514a
|
7
|
+
data.tar.gz: 63ce962e1bfdaea6f196092b9ab5d935b9bc3fddd097d7b1867819e873189b40e86fa2c048177c6af6d86a4bc7be3df2da413b64f66a29bf6eef2a465cd3c576
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
We love pull requests. Here’s a quick guide:
|
2
|
+
|
3
|
+
1. Fork the repository.
|
4
|
+
2. Make your changes in a topic branch.
|
5
|
+
3. Squash your commits into a single one (more on that [here](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html)).
|
6
|
+
4. Rebase against `origin/master`, push to your fork and submit a pull request.
|
7
|
+
5. If you are writing a new feature please add documentation for it by making another pull request to the `gh-pages` branch.
|
8
|
+
|
9
|
+
At this point you’re waiting on us. We like to at least comment on, if not
|
10
|
+
accept, pull requests within three business days (and, typically, one business
|
11
|
+
day). We may suggest some changes or improvements or alternatives.
|
12
|
+
|
13
|
+
Some things that will increase the chance that your pull request is accepted:
|
14
|
+
|
15
|
+
* Fix a bug, refactor code or expand an existing feature.
|
16
|
+
* Use the right syntax and naming conventions.
|
17
|
+
* Update parts of the documentation that are affected by your contribution.
|
18
|
+
|
19
|
+
**Git Commit Messages**
|
20
|
+
|
21
|
+
* Capitalize your commit messages.
|
22
|
+
* Start your message with a verb.
|
23
|
+
* Use present tense.
|
24
|
+
* Refer to the issue/PR number in your squashed commit message.
|
data/Gemfile
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright © 2015 [Slate Studio, LLC](http://slatestudio.com)
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
# Inverter
|
2
|
+
|
3
|
+
## Easy way to connect Rails templates content to CMS
|
4
|
+
|
5
|
+
Mark right in Rails temlpates content that you want to change via CMS. It's automatically populated to models and is accessible via CMS. While Rails rendering template it's pulling editable content from databased automatically.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add to ```Gemfile```:
|
10
|
+
|
11
|
+
gem "inverter", github: "slate-studio/inverter"
|
12
|
+
|
13
|
+
Setup initializer ```config/initializers/inverter.rb```:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
Inverter.configure do |config|
|
17
|
+
# model that stores template editable blocks
|
18
|
+
config.model_class = Page
|
19
|
+
|
20
|
+
# folders which templates are editable
|
21
|
+
config.template_folders = %w( pages )
|
22
|
+
|
23
|
+
# templates from template_folders the are not editable
|
24
|
+
config.excluded_template_names = %w( pages/home )
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
Configure model that stores template content, e.g. ```models/page.rb```:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
class Page
|
32
|
+
include Mongoid::Document
|
33
|
+
include Mongoid::Timestamps
|
34
|
+
include Mongoid::Inverter
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
Setup admin page controller configuration ```controllers/admin/pages_controller.rb```, this keeps models in sync with template files changes:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
class Admin::PagesController < Admin::BaseController
|
42
|
+
mongosteen
|
43
|
+
json_config({ methods: [ :list_item_title ])
|
44
|
+
|
45
|
+
before_filter :syncronize_templates, only: [ :index ]
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
def syncronize_templates
|
50
|
+
if Rails.env.development?
|
51
|
+
resource_class.sync_objects_with_templates!
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
Controller filter above tracks changes in editable templates on every ```index``` request. This helps to keep models up to date while app development. If new template added to folders it's linked automatically and removed if existing one deleted. Editable piece of content in the template is called name, each block should be named.
|
58
|
+
|
59
|
+
An example of editable template with three content blocks and page name (to identify page in CMS), e.g. ```pages/about.html.erb```:
|
60
|
+
|
61
|
+
```html
|
62
|
+
<!--// About //-->
|
63
|
+
<h1>About</h1>
|
64
|
+
|
65
|
+
<!--[ subtitle ]-->
|
66
|
+
<p>
|
67
|
+
This is an example of the content block named subtitle. This content is editable
|
68
|
+
via CMS, please go to website <%= link_to 'admin', admin_path %> and check how
|
69
|
+
it can be changed.</p>
|
70
|
+
<!--END-->
|
71
|
+
|
72
|
+
<!--[ body ]-->
|
73
|
+
<p>
|
74
|
+
Blocks could have not only plain HTML but a Ruby template code as well. For
|
75
|
+
example these links below are going to be rendered and saved as HTML links in
|
76
|
+
the models. You can access <%= link_to 'welcome', page_path('welcome') %> &
|
77
|
+
<%= link_to 'about', page_path('about') %> pages.</p>
|
78
|
+
<!--END-->
|
79
|
+
|
80
|
+
<!--[ footer ]-->
|
81
|
+
<p>
|
82
|
+
This is an example of the content block named footer. This content is editable
|
83
|
+
via CMS, please go to website <%= link_to 'admin', admin_path %> and check how
|
84
|
+
it can be changed.</p>
|
85
|
+
<!--END-->
|
86
|
+
```
|
87
|
+
|
88
|
+
### Character Setup
|
89
|
+
|
90
|
+
Inverter supports [chr](https://github.com/slate-studio/chr) out of the box. Include custom input in the cms configuration file ```admin.coffee```, and setup module configuration:
|
91
|
+
|
92
|
+
```coffeescript
|
93
|
+
|
94
|
+
#= require inverter
|
95
|
+
|
96
|
+
pagesConfig = ->
|
97
|
+
itemTitleField: 'list_item_title'
|
98
|
+
disableNewItems: true
|
99
|
+
disableDelete: true
|
100
|
+
arrayStore: new MongosteenArrayStore({
|
101
|
+
resource: 'page'
|
102
|
+
path: '/admin/pages'
|
103
|
+
})
|
104
|
+
formSchema:
|
105
|
+
_page_title: { type: 'string', label: 'Title' }
|
106
|
+
_page_description: { type: 'text', label: 'Description' }
|
107
|
+
_page_image_url: { type: 'string', label: 'Image URL' }
|
108
|
+
blocks: { type: 'inverter' }
|
109
|
+
```
|
110
|
+
|
111
|
+
Inverter input has an option ```defaultInputType``` which specifies what input type should be used as default, if nothing specified ```text``` is used. This might be set to WYSIWYG editor of your choice, e.g:
|
112
|
+
|
113
|
+
```coffeescript
|
114
|
+
blocks: { type: 'inverter', defaultInputType: 'redactor' }
|
115
|
+
```
|
116
|
+
|
117
|
+
You can also specify input type that you want to use for specific block like this: ```<!--[ Main Body : text ]-->``` — in this case ```Main Body``` would be a label and ```text``` is an input type that will be used to edit this block in CMS.
|
118
|
+
|
119
|
+
### Meta Tags Support
|
120
|
+
|
121
|
+
```Mongoid::Inverter``` concern includes page meta tags fields. Check out [meta-tags](https://github.com/kpumuk/meta-tags) gem documentation for usage details, it helps to make pages SEO friendly.
|
122
|
+
|
123
|
+
To enable meta-tags support include following helper in application layout:
|
124
|
+
|
125
|
+
```erb
|
126
|
+
<%= display_meta_tags title: 'Default Website Title',
|
127
|
+
description: 'Default Website Description',
|
128
|
+
open_graph: { type: 'website',
|
129
|
+
title: 'Default Website Title',
|
130
|
+
description: 'Default Website Description',
|
131
|
+
image: 'https://slate-git-images.s3-us-west-1.amazonaws.com/slate.png' } %>
|
132
|
+
```
|
133
|
+
|
134
|
+
To override default behavior add custom fields and write own ```update_inverter_meta_tags``` implementation.
|
135
|
+
|
136
|
+
## Inverter family
|
137
|
+
|
138
|
+
- [Mongosteen](https://github.com/slate-studio/mongosteen): An easy way to add restful actions for mongoid models
|
139
|
+
- [Character](https://github.com/slate-studio/chr): A simple and lightweight javascript library for building data management web apps
|
140
|
+
|
141
|
+
## Credits
|
142
|
+
|
143
|
+
[![Slate Studio](https://slate-git-images.s3-us-west-1.amazonaws.com/slate.png)](http://slatestudio.com)
|
144
|
+
|
145
|
+
Inverter is maintained and funded by [Slate Studio, LLC](http://slatestudio.com). Tweet your questions or suggestions to [@slatestudio](https://twitter.com/slatestudio) and while you’re at it follow us too.
|
146
|
+
|
147
|
+
## License
|
148
|
+
|
149
|
+
Copyright © 2015 [Slate Studio, LLC](http://slatestudio.com). Character is free software, and may be redistributed under the terms specified in the [license](LICENSE.md).
|
data/Rakefile
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# _slugify(string)
|
2
|
+
if ! @_slugify
|
3
|
+
@_slugify = (string) ->
|
4
|
+
return string.toString().toLowerCase()
|
5
|
+
.replace(/\s+/g, '-') # Replace spaces with -
|
6
|
+
.replace(/[^\w\-]+/g, '') # Remove all non-word chars
|
7
|
+
.replace(/\-\-+/g, '-') # Replace multiple - with single -
|
8
|
+
.trim() # Trim - from start/end of text
|
9
|
+
|
10
|
+
# -----------------------------------------------------------------------------
|
11
|
+
# INPUT HASH
|
12
|
+
# -----------------------------------------------------------------------------
|
13
|
+
class @InputInverter
|
14
|
+
constructor: (@name, @value, @config, @object) ->
|
15
|
+
@_createEl()
|
16
|
+
|
17
|
+
@inputs = {}
|
18
|
+
|
19
|
+
for name, value of @value
|
20
|
+
input = @_addInput(name, value, @config)
|
21
|
+
@inputs[name] = input
|
22
|
+
@$el.append input.$el
|
23
|
+
|
24
|
+
return this
|
25
|
+
|
26
|
+
_addInput: (name, value) ->
|
27
|
+
inputConfig = $.extend {}, @config
|
28
|
+
|
29
|
+
# get input label and type from name, e.g. "Page Title : text"
|
30
|
+
labelAndType = name.split(' : ')
|
31
|
+
|
32
|
+
# input label
|
33
|
+
inputConfig.label = labelAndType[0].titleize()
|
34
|
+
|
35
|
+
# input type
|
36
|
+
inputType = labelAndType[1]
|
37
|
+
inputType ?= @config.defaultInputType || 'text'
|
38
|
+
inputType = $.trim(inputType)
|
39
|
+
|
40
|
+
if ! _chrFormInputs[inputType]
|
41
|
+
inputType = 'text'
|
42
|
+
|
43
|
+
# input css class
|
44
|
+
inputConfig.klassName = 'inverter-block-' + _slugify(inputConfig.label)
|
45
|
+
inputConfig.klass ?= 'stacked'
|
46
|
+
|
47
|
+
inputClass = _chrFormInputs[inputType]
|
48
|
+
|
49
|
+
inputName = if @config.namePrefix then "#{ @config.namePrefix }#{ @name }[#{ name }]" else "#{ @name }[#{ name }]"
|
50
|
+
inputConfig.namePrefix = @config.namePrefix
|
51
|
+
|
52
|
+
return new inputClass(inputName, value, inputConfig, @object)
|
53
|
+
|
54
|
+
_createEl: ->
|
55
|
+
@$el =$ "<div class='input-#{ @config.type } #{ @config.klassName }'>"
|
56
|
+
|
57
|
+
#
|
58
|
+
# PUBLIC
|
59
|
+
#
|
60
|
+
|
61
|
+
initialize: ->
|
62
|
+
for name, input of @inputs
|
63
|
+
input.initialize()
|
64
|
+
|
65
|
+
@config.onInitialize?(this)
|
66
|
+
|
67
|
+
hash: (hash={}) ->
|
68
|
+
obj = {}
|
69
|
+
for key, input of @inputs
|
70
|
+
input.hash(obj)
|
71
|
+
hash[@config.klassName] = obj
|
72
|
+
return hash
|
73
|
+
|
74
|
+
updateValue: (@value) ->
|
75
|
+
for key, input of @inputs
|
76
|
+
input.updateValue(@value[key])
|
77
|
+
|
78
|
+
showErrorMessage: (message) ->
|
79
|
+
@$el.addClass 'error'
|
80
|
+
@$errorMessage.html(message)
|
81
|
+
|
82
|
+
hideErrorMessage: ->
|
83
|
+
@$el.removeClass 'error'
|
84
|
+
@$errorMessage.html('')
|
85
|
+
|
86
|
+
|
87
|
+
_chrFormInputs['inverter'] = InputInverter
|
88
|
+
|
89
|
+
|
90
|
+
|
91
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
#= require ./chr/form/input-inverter
|
data/inverter.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'inverter/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'inverter'
|
7
|
+
s.version = Inverter::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ['Alexander Kravets']
|
10
|
+
s.email = 'alex@slatestudio.com'
|
11
|
+
s.license = 'MIT'
|
12
|
+
s.homepage = 'http://slatestudio.com'
|
13
|
+
s.summary = 'An easy way to connect Rails templates content to CMS.'
|
14
|
+
s.description = <<-DESC
|
15
|
+
Easy way to connect Rails templates content to CMS. HTML content is marked using
|
16
|
+
special formatted comments. Then it automatically populated to models and is accessible
|
17
|
+
via CMS of choice. When template is rendered content stored in models content is
|
18
|
+
pulled from databased automatically.
|
19
|
+
DESC
|
20
|
+
|
21
|
+
s.rubyforge_project = 'inverter'
|
22
|
+
|
23
|
+
s.files = `git ls-files`.split("\n")
|
24
|
+
s.require_paths = ['lib']
|
25
|
+
|
26
|
+
s.add_dependency("render_anywhere", ">= 0.0.10")
|
27
|
+
s.add_dependency("meta-tags", ">= 2.0")
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
|
@@ -0,0 +1,168 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Inverter
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
# attributes
|
7
|
+
field :_page_title, default: ''
|
8
|
+
field :_page_description, default: ''
|
9
|
+
field :_page_image_url, default: ''
|
10
|
+
|
11
|
+
field :_template_name
|
12
|
+
field :_name, default: ''
|
13
|
+
field :_blocks, type: Hash, default: {}
|
14
|
+
|
15
|
+
# indexes
|
16
|
+
index({ _template_name: 1 })
|
17
|
+
|
18
|
+
|
19
|
+
# returns title to be used in cms and identify page in list
|
20
|
+
def list_item_title
|
21
|
+
self._name.empty? ? self._template_name : self._name
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
# populates seo values to cached meta_tags object which is
|
26
|
+
# used by ActionController while template rendering
|
27
|
+
def update_inverter_meta_tags
|
28
|
+
::Inverter.meta_tags[:og] ||= {}
|
29
|
+
|
30
|
+
unless self._page_title.empty?
|
31
|
+
::Inverter.meta_tags[:title] = self._page_title
|
32
|
+
::Inverter.meta_tags[:og][:title] = self._page_title
|
33
|
+
end
|
34
|
+
|
35
|
+
unless self._page_description.empty?
|
36
|
+
::Inverter.meta_tags[:description] = self._page_description
|
37
|
+
::Inverter.meta_tags[:og][:description] = self._page_description
|
38
|
+
end
|
39
|
+
|
40
|
+
unless self._page_image_url.empty?
|
41
|
+
::Inverter.meta_tags[:og][:image] = self._page_image_url
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
# updates blocks in provided html with values from the
|
47
|
+
# objects _blocks hash
|
48
|
+
def update_html(html)
|
49
|
+
map = ::Inverter::Parser.map_blocks_for(html)
|
50
|
+
|
51
|
+
offset = 0
|
52
|
+
|
53
|
+
map.each do |name, pos|
|
54
|
+
block = "\n" + self._blocks[name] + "\n"
|
55
|
+
html[offset+pos[0]..offset+pos[1]] = block
|
56
|
+
|
57
|
+
block_size = block.size
|
58
|
+
template_block_size = pos[1] - pos[0]
|
59
|
+
offset += block_size - template_block_size - 1
|
60
|
+
end
|
61
|
+
|
62
|
+
return html
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
# check if template file was changed after object was saved
|
67
|
+
def template_changed?
|
68
|
+
template_path = Rails.root.to_s + '/app/views/' + self._template_name
|
69
|
+
template_time_updated = File.mtime(template_path)
|
70
|
+
template_time_updated > updated_at
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
# read template blocks and save to objects _blocks hash
|
75
|
+
def update_from_template!
|
76
|
+
template_parser = ::Inverter::Parser.new(self._template_name)
|
77
|
+
template_blocks = template_parser.blocks
|
78
|
+
name = template_parser.name
|
79
|
+
|
80
|
+
# add new blocks
|
81
|
+
keys_to_add = template_blocks.keys - self._blocks.keys
|
82
|
+
keys_to_add.each do |key|
|
83
|
+
self._blocks[key] = template_blocks[key]
|
84
|
+
end
|
85
|
+
|
86
|
+
# remove old blocks
|
87
|
+
keys_to_remove = self._blocks.keys - template_blocks.keys
|
88
|
+
keys_to_remove.each do |key|
|
89
|
+
self._blocks.delete(key)
|
90
|
+
end
|
91
|
+
|
92
|
+
# update page name
|
93
|
+
self._name = name
|
94
|
+
|
95
|
+
save
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
# class methods
|
100
|
+
|
101
|
+
|
102
|
+
# creat new page object from template
|
103
|
+
def self.create_from_template(template_name)
|
104
|
+
template_parser = ::Inverter::Parser.new(template_name)
|
105
|
+
template_blocks = template_parser.blocks
|
106
|
+
name = template_parser.name
|
107
|
+
create(_name: name, _template_name: template_name, _blocks: template_blocks)
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
# syncronize templates with existing page objects
|
112
|
+
def self.sync_with_templates!
|
113
|
+
template_names = get_template_names
|
114
|
+
created_objects = self.all
|
115
|
+
existing_template_names = created_objects.map { |o| o._template_name }
|
116
|
+
|
117
|
+
# add new objects
|
118
|
+
if existing_template_names.size < template_names.size
|
119
|
+
template_names_to_create = template_names - existing_template_names
|
120
|
+
template_names_to_create.each { |name| create_from_template(name) }
|
121
|
+
end
|
122
|
+
|
123
|
+
# delete object for removed templates
|
124
|
+
if existing_template_names.size > template_names.size
|
125
|
+
template_names_to_remove = existing_template_names - template_names
|
126
|
+
template_names_to_remove.each { |name| find_by(_template_name: name).delete }
|
127
|
+
end
|
128
|
+
|
129
|
+
# update objects for changes in templates
|
130
|
+
created_objects.each do |o|
|
131
|
+
if o.template_changed?
|
132
|
+
o.update_from_template!
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
# returns list of template names for gem configuration in
|
139
|
+
# config/initializers/inverter.rb
|
140
|
+
def self.get_template_names
|
141
|
+
template_file_names = []
|
142
|
+
|
143
|
+
::Inverter.template_folders.each do |folder_name|
|
144
|
+
path = Rails.root.join('app/views', "#{ folder_name }/**/*.html.erb")
|
145
|
+
file_names = Dir[path]
|
146
|
+
template_file_names.concat(file_names)
|
147
|
+
end
|
148
|
+
|
149
|
+
template_names = template_file_names.map do |file_name|
|
150
|
+
file_name.gsub(Rails.root.to_s + '/app/views/', '')
|
151
|
+
end
|
152
|
+
|
153
|
+
# skip rails partials
|
154
|
+
template_names.reject! { |n| n.split('/').last.start_with? '_' }
|
155
|
+
|
156
|
+
# exclude names from excluded_templates configuration list
|
157
|
+
excluded_template_names = ::Inverter.excluded_templates.map { |name| "#{ name }.html.erb" }
|
158
|
+
template_names = template_names - excluded_template_names
|
159
|
+
|
160
|
+
return template_names
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
|
168
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Inverter
|
2
|
+
module Configuration
|
3
|
+
|
4
|
+
attr_accessor(
|
5
|
+
:model_class,
|
6
|
+
:template_folders,
|
7
|
+
:excluded_templates
|
8
|
+
)
|
9
|
+
|
10
|
+
def configure
|
11
|
+
yield self
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.extended(base)
|
15
|
+
base.set_default_configuration
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_default_configuration
|
19
|
+
self.excluded_templates = []
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Inverter
|
2
|
+
module ControllerHelper
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
alias_method_chain :render, :inverter_object
|
7
|
+
end
|
8
|
+
|
9
|
+
def render_with_inverter_object(*args, &block)
|
10
|
+
# reset Inveter.object to nil
|
11
|
+
Inverter.reset_object()
|
12
|
+
# make a pointer to meta_tags for future use
|
13
|
+
Inverter.set_meta_tags(meta_tags)
|
14
|
+
|
15
|
+
# regular render method
|
16
|
+
render_without_inverter_object(*args, &block)
|
17
|
+
|
18
|
+
# modify response only if Inverter.object is set
|
19
|
+
if Inverter.object
|
20
|
+
self.response_body[0] = Inverter.object.update_html(self.response_body[0])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
protected :render_with_inverter_object
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Inverter
|
2
|
+
module Object
|
3
|
+
attr_accessor(
|
4
|
+
:object,
|
5
|
+
:meta_tags
|
6
|
+
)
|
7
|
+
|
8
|
+
def update_inverter_object(template_name)
|
9
|
+
# proceed if inverter object is not set
|
10
|
+
if Inverter.object.nil?
|
11
|
+
# template is in inverter template folders
|
12
|
+
if template_name.start_with?(*Inverter.template_folders)
|
13
|
+
# template is not excluded via configuration
|
14
|
+
template = template_name.gsub('.html.erb', '')
|
15
|
+
if not Inverter.excluded_templates.include?(template)
|
16
|
+
|
17
|
+
self.object = Inverter.model_class.where(_template_name: template_name).first
|
18
|
+
return self.object
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
return nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def reset_object
|
28
|
+
self.object = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def set_meta_tags(meta_tags_collection)
|
32
|
+
self.meta_tags = meta_tags_collection
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'render_anywhere'
|
2
|
+
|
3
|
+
module Inverter
|
4
|
+
class Parser
|
5
|
+
include RenderAnywhere
|
6
|
+
|
7
|
+
NAME_START_DELIMITER = "<!--//"
|
8
|
+
NAME_END_DELIMITER = "//-->"
|
9
|
+
|
10
|
+
BLOCK_START_DELIMITER = "<!--["
|
11
|
+
BLOCK_NAME_DELIMITER = "]-->"
|
12
|
+
BLOCK_END_DELIMITER = "<!--END-->"
|
13
|
+
|
14
|
+
def initialize(template_name)
|
15
|
+
@template_name = template_name
|
16
|
+
@file_content = File.open(Rails.root.join("app/views", @template_name), "rb").read
|
17
|
+
end
|
18
|
+
|
19
|
+
# parse name from template
|
20
|
+
def parse_name
|
21
|
+
name = ''
|
22
|
+
content = @file_content
|
23
|
+
|
24
|
+
s = content.index(NAME_START_DELIMITER)
|
25
|
+
e = content.index(NAME_END_DELIMITER)
|
26
|
+
|
27
|
+
unless s.nil?
|
28
|
+
s += NAME_START_DELIMITER.size
|
29
|
+
name = content[s..e-1].strip
|
30
|
+
end
|
31
|
+
|
32
|
+
return name
|
33
|
+
end
|
34
|
+
alias_method :name, :parse_name
|
35
|
+
|
36
|
+
# parses rendered template and returns hash with block
|
37
|
+
# names and content to be editable
|
38
|
+
def parse
|
39
|
+
result = {}
|
40
|
+
content = @file_content
|
41
|
+
|
42
|
+
s = content.index(BLOCK_START_DELIMITER)
|
43
|
+
e = content.index(BLOCK_END_DELIMITER)
|
44
|
+
|
45
|
+
while s && e
|
46
|
+
s += BLOCK_START_DELIMITER.size
|
47
|
+
block = content[s..e-1]
|
48
|
+
|
49
|
+
key, value = parse_block(block)
|
50
|
+
result[key] = value
|
51
|
+
|
52
|
+
e += BLOCK_END_DELIMITER.size
|
53
|
+
content = content[e..-1]
|
54
|
+
|
55
|
+
s = content.index(BLOCK_START_DELIMITER)
|
56
|
+
e = content.index(BLOCK_END_DELIMITER)
|
57
|
+
end
|
58
|
+
|
59
|
+
return result
|
60
|
+
end
|
61
|
+
alias_method :blocks, :parse
|
62
|
+
|
63
|
+
def parse_block(block)
|
64
|
+
n = block.index(BLOCK_NAME_DELIMITER)
|
65
|
+
key = block[0..n-1].strip
|
66
|
+
|
67
|
+
n += BLOCK_NAME_DELIMITER.size
|
68
|
+
value = block[n..-1]
|
69
|
+
|
70
|
+
begin
|
71
|
+
html = render inline: value, layout: false
|
72
|
+
rescue
|
73
|
+
html = "Template block wasn't rendered cause of a template syntax error."
|
74
|
+
end
|
75
|
+
|
76
|
+
return key, html
|
77
|
+
end
|
78
|
+
|
79
|
+
# class methods
|
80
|
+
|
81
|
+
# returns a hash with content blocks name and [start, end] positions
|
82
|
+
# to be replaced with updated content
|
83
|
+
# e.g: { "header"=>[32, 178], "body"=>[206, 352] }
|
84
|
+
def self.map_blocks_for(content)
|
85
|
+
map = {}
|
86
|
+
delta = 0
|
87
|
+
|
88
|
+
s = content.index(BLOCK_START_DELIMITER)
|
89
|
+
e = content.index(BLOCK_END_DELIMITER)
|
90
|
+
|
91
|
+
while s && e
|
92
|
+
s += BLOCK_START_DELIMITER.size
|
93
|
+
|
94
|
+
n = content[s..e-1].index(BLOCK_NAME_DELIMITER)
|
95
|
+
key = content[s..s+n-1].strip
|
96
|
+
|
97
|
+
n += BLOCK_NAME_DELIMITER.size
|
98
|
+
map[key] = [delta+s+n, delta+e-1]
|
99
|
+
|
100
|
+
e += BLOCK_END_DELIMITER.size
|
101
|
+
content = content[e..-1]
|
102
|
+
delta += e
|
103
|
+
|
104
|
+
s = content.index(BLOCK_START_DELIMITER)
|
105
|
+
e = content.index(BLOCK_END_DELIMITER)
|
106
|
+
end
|
107
|
+
|
108
|
+
return map
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
|
115
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Inverter
|
2
|
+
module Template
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
alias_method :initialize_original, :initialize
|
7
|
+
|
8
|
+
def initialize(source, identifier, handler, details)
|
9
|
+
template_name = identifier.gsub(Rails.root.to_s + '/app/views/', '')
|
10
|
+
inverter_object = Inverter.update_inverter_object(template_name)
|
11
|
+
|
12
|
+
if inverter_object
|
13
|
+
inverter_object.update_inverter_meta_tags()
|
14
|
+
end
|
15
|
+
|
16
|
+
initialize_original(source, identifier, handler, details)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
|
data/lib/inverter.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "inverter/concerns/inverter"
|
2
|
+
require "inverter/object"
|
3
|
+
require "inverter/controller_helper"
|
4
|
+
require "inverter/configuration"
|
5
|
+
require "inverter/parser"
|
6
|
+
require "inverter/version"
|
7
|
+
require "inverter/engine"
|
8
|
+
require "inverter/template"
|
9
|
+
|
10
|
+
module Inverter
|
11
|
+
extend Configuration
|
12
|
+
extend Object
|
13
|
+
end
|
14
|
+
|
15
|
+
require "meta_tags"
|
16
|
+
|
17
|
+
ActionController::Base.send :include, Inverter::ControllerHelper
|
18
|
+
ActionView::Template.send :include, Inverter::Template
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: inverter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexander Kravets
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-03-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: render_anywhere
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.0.10
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.0.10
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: meta-tags
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
description: |
|
42
|
+
Easy way to connect Rails templates content to CMS. HTML content is marked using
|
43
|
+
special formatted comments. Then it automatically populated to models and is accessible
|
44
|
+
via CMS of choice. When template is rendered content stored in models content is
|
45
|
+
pulled from databased automatically.
|
46
|
+
email: alex@slatestudio.com
|
47
|
+
executables: []
|
48
|
+
extensions: []
|
49
|
+
extra_rdoc_files: []
|
50
|
+
files:
|
51
|
+
- CONTRIBUTING.md
|
52
|
+
- Gemfile
|
53
|
+
- LICENSE.md
|
54
|
+
- README.md
|
55
|
+
- Rakefile
|
56
|
+
- app/assets/javascripts/chr/form/input-inverter.coffee
|
57
|
+
- app/assets/javascripts/inverter.coffee
|
58
|
+
- inverter.gemspec
|
59
|
+
- lib/inverter.rb
|
60
|
+
- lib/inverter/concerns/inverter.rb
|
61
|
+
- lib/inverter/configuration.rb
|
62
|
+
- lib/inverter/controller_helper.rb
|
63
|
+
- lib/inverter/engine.rb
|
64
|
+
- lib/inverter/object.rb
|
65
|
+
- lib/inverter/parser.rb
|
66
|
+
- lib/inverter/template.rb
|
67
|
+
- lib/inverter/version.rb
|
68
|
+
homepage: http://slatestudio.com
|
69
|
+
licenses:
|
70
|
+
- MIT
|
71
|
+
metadata: {}
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
requirements: []
|
87
|
+
rubyforge_project: inverter
|
88
|
+
rubygems_version: 2.4.5
|
89
|
+
signing_key:
|
90
|
+
specification_version: 4
|
91
|
+
summary: An easy way to connect Rails templates content to CMS.
|
92
|
+
test_files: []
|