canyon 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 36a8f1d8684684e4323ac062af0294d15e24153a
4
+ data.tar.gz: 675bdcb88f2151e34d525e5f0913a9978128ed11
5
+ SHA512:
6
+ metadata.gz: c5b2473b776ae3965fa0ffb8f4f975971c331ba861d186da32aa7f51c3b1beedc186d64928f2239f00786cc7091c1b38ba367a057a341cb32e1c300fd4e4bdfe
7
+ data.tar.gz: 9ab5818e3dfe53e2aaf40c2d64e4a6f52d3f7da321d2f8db9d3c382432973ec689ef258cf280d6e44c1630a01c6ae356eeec0410378e67265b16724c77dfcb3e
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2015 Martin Jagusch
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # Canyon
2
+
3
+ Canyon is a Rails blogging engine, including only the backend. How you present the blog posts is up to you.
4
+
5
+ ### Installation
6
+
7
+ Include the engine to your projects Gemfile. `Gemfile`
8
+
9
+ ```
10
+ gem 'canyon'
11
+ ```
12
+
13
+ Include the routes from the engine to your applications routing file. `config/routes.rb`
14
+
15
+ ``` ruby
16
+ Rails.application.routes.draw do
17
+ # All your application routes.
18
+
19
+ mount Canyon::Engine => '/cy-admin'
20
+ end
21
+ ```
22
+
23
+ Load the engine sample data. `db/seeds.rb` (optional)
24
+
25
+ ```
26
+ Canyon::Engine.load_seed
27
+ ```
28
+
29
+ Running `rake db:seed` will now include a default user and blog post.
@@ -0,0 +1,3 @@
1
+ //= require jquery
2
+ //= require bootstrap/alert
3
+ //= require canyon/vendor/jquery.autosize
@@ -0,0 +1,211 @@
1
+ /*!
2
+ Autosize 3.0.7
3
+ license: MIT
4
+ http://www.jacklmoore.com/autosize
5
+ */
6
+ (function (global, factory) {
7
+ if (typeof define === 'function' && define.amd) {
8
+ define(['exports', 'module'], factory);
9
+ } else if (typeof exports !== 'undefined' && typeof module !== 'undefined') {
10
+ factory(exports, module);
11
+ } else {
12
+ var mod = {
13
+ exports: {}
14
+ };
15
+ factory(mod.exports, mod);
16
+ global.autosize = mod.exports;
17
+ }
18
+ })(this, function (exports, module) {
19
+ 'use strict';
20
+
21
+ function assign(ta) {
22
+ var _ref = arguments[1] === undefined ? {} : arguments[1];
23
+
24
+ var _ref$setOverflowX = _ref.setOverflowX;
25
+ var setOverflowX = _ref$setOverflowX === undefined ? true : _ref$setOverflowX;
26
+ var _ref$setOverflowY = _ref.setOverflowY;
27
+ var setOverflowY = _ref$setOverflowY === undefined ? true : _ref$setOverflowY;
28
+
29
+ if (!ta || !ta.nodeName || ta.nodeName !== 'TEXTAREA' || ta.hasAttribute('data-autosize-on')) return;
30
+
31
+ var heightOffset = null;
32
+ var overflowY = 'hidden';
33
+
34
+ function init() {
35
+ var style = window.getComputedStyle(ta, null);
36
+
37
+ if (style.resize === 'vertical') {
38
+ ta.style.resize = 'none';
39
+ } else if (style.resize === 'both') {
40
+ ta.style.resize = 'horizontal';
41
+ }
42
+
43
+ if (style.boxSizing === 'content-box') {
44
+ heightOffset = -(parseFloat(style.paddingTop) + parseFloat(style.paddingBottom));
45
+ } else {
46
+ heightOffset = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth);
47
+ }
48
+
49
+ update();
50
+ }
51
+
52
+ function changeOverflow(value) {
53
+ {
54
+ // Chrome/Safari-specific fix:
55
+ // When the textarea y-overflow is hidden, Chrome/Safari do not reflow the text to account for the space
56
+ // made available by removing the scrollbar. The following forces the necessary text reflow.
57
+ var width = ta.style.width;
58
+ ta.style.width = '0px';
59
+ // Force reflow:
60
+ /* jshint ignore:start */
61
+ ta.offsetWidth;
62
+ /* jshint ignore:end */
63
+ ta.style.width = width;
64
+ }
65
+
66
+ overflowY = value;
67
+
68
+ if (setOverflowY) {
69
+ ta.style.overflowY = value;
70
+ }
71
+
72
+ update();
73
+ }
74
+
75
+ function update() {
76
+ var startHeight = ta.style.height;
77
+ var htmlTop = window.pageYOffset;
78
+ var bodyTop = document.body.scrollTop;
79
+ var originalHeight = ta.style.height;
80
+
81
+ ta.style.height = 'auto';
82
+
83
+ var endHeight = ta.scrollHeight + heightOffset;
84
+
85
+ if (ta.scrollHeight === 0) {
86
+ // If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM.
87
+ ta.style.height = originalHeight;
88
+ return;
89
+ }
90
+
91
+ ta.style.height = endHeight + 'px';
92
+
93
+ // prevents scroll-position jumping
94
+ document.documentElement.scrollTop = htmlTop;
95
+ document.body.scrollTop = bodyTop;
96
+
97
+ var style = window.getComputedStyle(ta, null);
98
+
99
+ if (style.height !== ta.style.height) {
100
+ if (overflowY !== 'visible') {
101
+ changeOverflow('visible');
102
+ return;
103
+ }
104
+ } else {
105
+ if (overflowY !== 'hidden') {
106
+ changeOverflow('hidden');
107
+ return;
108
+ }
109
+ }
110
+
111
+ if (startHeight !== ta.style.height) {
112
+ var evt = document.createEvent('Event');
113
+ evt.initEvent('autosize:resized', true, false);
114
+ ta.dispatchEvent(evt);
115
+ }
116
+ }
117
+
118
+ var destroy = (function (style) {
119
+ window.removeEventListener('resize', update);
120
+ ta.removeEventListener('input', update);
121
+ ta.removeEventListener('keyup', update);
122
+ ta.removeAttribute('data-autosize-on');
123
+ ta.removeEventListener('autosize:destroy', destroy);
124
+
125
+ Object.keys(style).forEach(function (key) {
126
+ ta.style[key] = style[key];
127
+ });
128
+ }).bind(ta, {
129
+ height: ta.style.height,
130
+ resize: ta.style.resize,
131
+ overflowY: ta.style.overflowY,
132
+ overflowX: ta.style.overflowX,
133
+ wordWrap: ta.style.wordWrap });
134
+
135
+ ta.addEventListener('autosize:destroy', destroy);
136
+
137
+ // IE9 does not fire onpropertychange or oninput for deletions,
138
+ // so binding to onkeyup to catch most of those events.
139
+ // There is no way that I know of to detect something like 'cut' in IE9.
140
+ if ('onpropertychange' in ta && 'oninput' in ta) {
141
+ ta.addEventListener('keyup', update);
142
+ }
143
+
144
+ window.addEventListener('resize', update);
145
+ ta.addEventListener('input', update);
146
+ ta.addEventListener('autosize:update', update);
147
+ ta.setAttribute('data-autosize-on', true);
148
+
149
+ if (setOverflowY) {
150
+ ta.style.overflowY = 'hidden';
151
+ }
152
+ if (setOverflowX) {
153
+ ta.style.overflowX = 'hidden';
154
+ ta.style.wordWrap = 'break-word';
155
+ }
156
+
157
+ init();
158
+ }
159
+
160
+ function destroy(ta) {
161
+ if (!(ta && ta.nodeName && ta.nodeName === 'TEXTAREA')) return;
162
+ var evt = document.createEvent('Event');
163
+ evt.initEvent('autosize:destroy', true, false);
164
+ ta.dispatchEvent(evt);
165
+ }
166
+
167
+ function update(ta) {
168
+ if (!(ta && ta.nodeName && ta.nodeName === 'TEXTAREA')) return;
169
+ var evt = document.createEvent('Event');
170
+ evt.initEvent('autosize:update', true, false);
171
+ ta.dispatchEvent(evt);
172
+ }
173
+
174
+ var autosize = null;
175
+
176
+ // Do nothing in Node.js environment and IE8 (or lower)
177
+ if (typeof window === 'undefined' || typeof window.getComputedStyle !== 'function') {
178
+ autosize = function (el) {
179
+ return el;
180
+ };
181
+ autosize.destroy = function (el) {
182
+ return el;
183
+ };
184
+ autosize.update = function (el) {
185
+ return el;
186
+ };
187
+ } else {
188
+ autosize = function (el, options) {
189
+ if (el) {
190
+ Array.prototype.forEach.call(el.length ? el : [el], function (x) {
191
+ return assign(x, options);
192
+ });
193
+ }
194
+ return el;
195
+ };
196
+ autosize.destroy = function (el) {
197
+ if (el) {
198
+ Array.prototype.forEach.call(el.length ? el : [el], destroy);
199
+ }
200
+ return el;
201
+ };
202
+ autosize.update = function (el) {
203
+ if (el) {
204
+ Array.prototype.forEach.call(el.length ? el : [el], update);
205
+ }
206
+ return el;
207
+ };
208
+ }
209
+
210
+ module.exports = autosize;
211
+ });
@@ -0,0 +1,11 @@
1
+ @import "bootstrap-sprockets";
2
+ @import "bootstrap";
3
+
4
+ body {
5
+ padding: 60px 0 20px 0;
6
+ }
7
+
8
+ input:focus, textarea:focus {
9
+ outline-style: solid;
10
+ outline-width: 2px;
11
+ }
@@ -0,0 +1,11 @@
1
+ module Canyon
2
+ class ApplicationController < ActionController::Base
3
+ protect_from_forgery with: :exception
4
+
5
+ private
6
+
7
+ def basic_auth_enabled?
8
+ ENV['HTTP_BASIC_AUTH'] == 'true'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,74 @@
1
+ module Canyon
2
+ class PostsController < ApplicationController
3
+ def index
4
+ @posts = Post.published_or_drafted
5
+ end
6
+
7
+ def new
8
+ @post = Post.new(published_at: Time.now)
9
+ end
10
+
11
+ def edit
12
+ @post = Post.find(params[:id])
13
+ end
14
+
15
+ def destroy
16
+ post = Post.find(params[:id])
17
+ raise post.inspect
18
+ end
19
+
20
+ def create
21
+ @post = Post.new(post_params)
22
+
23
+ if @post.save
24
+ if params[:preview].present?
25
+ @post.update_attributes(published: false)
26
+ # TODO Link to preview page.
27
+ flash[:warning] = %Q(A preview can be seen here: Coming Soon.)
28
+ render :edit
29
+ else
30
+ flash[:success] = %Q(Post "#{@post.title}" has been successfully created.)
31
+ redirect_to posts_path
32
+ end
33
+ else
34
+ render :new
35
+ end
36
+ end
37
+
38
+ def update
39
+ origin = Post.find(params[:id])
40
+
41
+ if params[:preview].present?
42
+ @post = origin.dup
43
+ @post.assign_attributes(parent_id: origin.parent_id || origin.id, published: false)
44
+
45
+ if @post.update_attributes(post_params)
46
+ # TODO Link to preview page.
47
+ flash[:warning] = %Q(A preview can be seen here: Coming Soon.)
48
+ end
49
+
50
+ render :edit
51
+ else
52
+ @post = origin.preview? ? origin.parent : origin
53
+
54
+ if params[:delete].present?
55
+ @post.update_attributes(deleted: true)
56
+ flash[:success] = %Q(Post "#{@post.title}" has been successfully deleted.)
57
+ redirect_to posts_path
58
+ elsif @post.update_attributes(post_params)
59
+ flash[:success] = %Q(Post "#{@post.title}" has been successfully updated.)
60
+ redirect_to posts_path
61
+ else
62
+ render :edit
63
+ end
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def post_params
70
+ params[:post].permit(:title, :slug, :language, :author_id, :published, :published_at,
71
+ :summary, :content)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,8 @@
1
+ module Canyon
2
+ module ApplicationHelper
3
+ def page_title(title, skip_suffix: false)
4
+ title_and_suffix = skip_suffix == true ? title : "#{title} &#183; Canyon".html_safe
5
+ content_for(:page_title, title_and_suffix)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ module Canyon
2
+ class Author < ActiveRecord::Base
3
+ has_many :posts
4
+ end
5
+ end
@@ -0,0 +1,49 @@
1
+ module Canyon
2
+ class Post < ActiveRecord::Base
3
+ LANGUAGES = [:en, :de]
4
+
5
+ belongs_to :author
6
+
7
+ delegate :name, to: 'author', prefix: true
8
+
9
+ scope :published, -> do
10
+ where(parent_id: nil).
11
+ where(published: true).
12
+ where(deleted: false).
13
+ where('published_at <= ?', Time.now).
14
+ order(published_at: :desc)
15
+ end
16
+
17
+ scope :published_or_drafted, -> do
18
+ where(parent_id: nil).where(deleted: false).order(published_at: :desc)
19
+ end
20
+
21
+ validates :title, :slug, :language, :content, :published_at, presence: true
22
+
23
+ before_validation :parameterize_slug
24
+
25
+ def parameterize_slug
26
+ self.slug = title.parameterize if slug.blank? && title.present?
27
+ end
28
+
29
+ def draft?
30
+ !published?
31
+ end
32
+
33
+ def preview?
34
+ !parent_id.nil?
35
+ end
36
+
37
+ def parent
38
+ self.class.find(parent_id)
39
+ end
40
+
41
+ def last_preview
42
+ Canyon::Post.where(parent_id: id).order(updated_at: :desc).first
43
+ end
44
+
45
+ def has_newer_preview?
46
+ !new_record? && !preview? && !last_preview.nil? && last_preview.updated_at > updated_at
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,140 @@
1
+ <% if post.errors.any? %>
2
+ <div class="alert alert-danger alert-dismissible" role="alert">
3
+ <button type="button" class="close" data-dismiss="alert" aria-label="Close">
4
+ <span aria-hidden="true">&times;</span>
5
+ </button>
6
+
7
+ <h4>Houston. There has been an error. Imagine what would have been happen if we would be in space. Scary!</h4>
8
+
9
+ <ul>
10
+ <% post.errors.full_messages.each do |message| %>
11
+ <li><%= message %></li>
12
+ <% end %>
13
+ </ul>
14
+ </div>
15
+ <% end %>
16
+
17
+ <% if @post.has_newer_preview? %>
18
+ <div class="alert alert-warning alert-dismissible" role="alert">
19
+ <button type="button" class="close" data-dismiss="alert" aria-label="Close">
20
+ <span aria-hidden="true">&times;</span>
21
+ </button>
22
+
23
+ <p>
24
+ An preview that is newer as this post has been found.
25
+ <%= link_to %Q("#{@post.last_preview.title}"), edit_post_path(@post.last_preview.id) %>
26
+ </p>
27
+ </div>
28
+ <% end %>
29
+
30
+ <%= form_for post do |f| %>
31
+ <div class="row">
32
+ <div class="col-md-6">
33
+ <div class="form-group">
34
+ <%= f.label :title %>
35
+ <%= f.text_field :title, class: 'form-control' %>
36
+ </div>
37
+ </div>
38
+
39
+ <div class="col-md-6">
40
+ <div class="form-group">
41
+ <%= f.label :slug %>
42
+ <%= f.text_field :slug, class: 'form-control', placeholder: 'e.g., separated-by-dashes' %>
43
+ </div>
44
+ </div>
45
+ </div>
46
+
47
+ <div class="row">
48
+ <div class="col-md-6">
49
+ <div class="form-group">
50
+ <%= f.label :language %>
51
+ <%= f.select :language, Canyon::Post::LANGUAGES, {}, class: 'form-control' %>
52
+ </div>
53
+ </div>
54
+
55
+ <div class="col-md-6">
56
+ <div class="form-group">
57
+ <%= f.label :author_id %>
58
+ <%= f.select :author_id, Canyon::Author.all.map { |author| [author.name, author.id] }, {}, class: 'form-control' %>
59
+ </div>
60
+ </div>
61
+ </div>
62
+
63
+ <div class="row">
64
+ <div class="col-md-6">
65
+ <div class="form-group">
66
+ <%= f.label :published_at %>
67
+ <div>
68
+ <%= f.datetime_select :published_at, :minute_step => 5 %>
69
+ </div>
70
+ </div>
71
+ </div>
72
+
73
+ <div class="col-md-6">
74
+ <div class="form-group">
75
+ <%= f.label :published %>
76
+ <div>
77
+ <%= f.label :published do %>
78
+ <%= f.check_box :published %>
79
+ <% end %>
80
+ </div>
81
+ </div>
82
+ </div>
83
+ </div>
84
+
85
+ <div class="form-group">
86
+ <%= f.label :summary %>
87
+ <%= f.text_area :summary, rows: 3, class: 'form-control post-summary', placeholder: 'A short summary for the homepage.' %>
88
+ </div>
89
+
90
+ <div class="form-group">
91
+ <%= f.label :content %>
92
+ <%= f.text_area :content, rows: 5, class: 'form-control post-content', placeholder: 'The main content.' %>
93
+ </div>
94
+
95
+ <%= link_to '#', class: 'btn btn-warning preview-act', role: 'button' do %>
96
+ <span class="glyphicon glyphicon-eye-open" aria-hidden="true"></span>
97
+ Preview Post
98
+ <% end %>
99
+
100
+ <% if @post.new_record? && !@post.preview? %>
101
+ <%= link_to '#', class: 'btn btn-success submit-act', role: 'button' do %>
102
+ <span class="glyphicon glyphicon-ok-circle" aria-hidden="true"></span>
103
+ Create Post
104
+ <% end %>
105
+ <% else %>
106
+ <%= link_to '#', class: 'btn btn-success submit-act', role: 'button' do %>
107
+ <span class="glyphicon glyphicon-ok-circle" aria-hidden="true"></span>
108
+ Update Post
109
+ <% end %>
110
+
111
+ <%= link_to '#', class: 'btn btn-danger delete-act', role: 'button' do %>
112
+ <span class="glyphicon glyphicon-remove-circle" aria-hidden="true"></span>
113
+ Delete Post
114
+ <% end %>
115
+ <% end %>
116
+ <% end %>
117
+
118
+ <script>
119
+ var form = $('form');
120
+
121
+ $('.preview-act').click(function() {
122
+ form.attr('action', form.attr('action') + '?preview=1');
123
+ form.submit();
124
+ return false;
125
+ });
126
+
127
+ $('.delete-act').click(function() {
128
+ form.attr('action', form.attr('action') + '?delete=1');
129
+ form.submit();
130
+ return false;
131
+ });
132
+
133
+ $('.submit-act').click(function() {
134
+ form.submit();
135
+ return false;
136
+ });
137
+
138
+ autosize($('.post-summary'));
139
+ autosize($('.post-content'));
140
+ </script>
@@ -0,0 +1,11 @@
1
+ <% page_title(%Q(Edit Post ##{@post.id})) %>
2
+
3
+ <h3>
4
+ Edit Post
5
+ <%= link_to posts_path, class: 'btn btn-sm btn-primary', role: 'button' do %>
6
+ <span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>
7
+ All Posts
8
+ <% end %>
9
+ </h3>
10
+
11
+ <%= render partial: 'form', locals: {post: @post} %>
@@ -0,0 +1,35 @@
1
+ <% page_title('Posts') %>
2
+
3
+ <h3>
4
+ Posts
5
+ <%= link_to new_post_path, class: 'btn btn-sm btn-primary', role: 'button' do %>
6
+ <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
7
+ New Posts
8
+ <% end %>
9
+ </h3>
10
+
11
+ <table class="table table-bordered">
12
+ <tr>
13
+ <th>Title</th>
14
+ <th>Language</th>
15
+ <th>Author</th>
16
+ <th>Date</th>
17
+ </tr>
18
+ <% @posts.each do |post| %>
19
+ <tr>
20
+ <td>
21
+ <%= link_to post.title, edit_post_path(post) %>
22
+ <% if post.draft? %>
23
+ &#45; Draft
24
+ <% end %>
25
+ </td>
26
+ <td><%= post.language %></td>
27
+ <td><%= post.author_name %></td>
28
+ <td>
29
+ <abbr title="<%= l(post.published_at, format: :detailed) if post.published_at.present? %>">
30
+ <%= l(post.published_at, format: :simple) if post.published_at.present? %>
31
+ </abbr><br />
32
+ </td>
33
+ </tr>
34
+ <% end %>
35
+ </table>
@@ -0,0 +1,11 @@
1
+ <% page_title('New Post') %>
2
+
3
+ <h3>
4
+ New Post
5
+ <%= link_to posts_path, class: 'btn btn-sm btn-primary', role: 'button' do %>
6
+ <span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>
7
+ All Posts
8
+ <% end %>
9
+ </h3>
10
+
11
+ <%= render partial: 'form', locals: {post: @post} %>
@@ -0,0 +1,53 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <%= stylesheet_link_tag 'canyon/application', media: 'all' %>
8
+ <%= javascript_include_tag 'canyon/application' %>
9
+ <%= csrf_meta_tags %>
10
+ <title><%= content_for(:page_title) %></title>
11
+ </head>
12
+ <body>
13
+ <nav class="navbar navbar-inverse navbar-fixed-top">
14
+ <div class="container">
15
+ <div class="navbar-header">
16
+ <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar">
17
+ <span class="sr-only">Toggle navigation</span>
18
+ <span class="icon-bar"></span>
19
+ <span class="icon-bar"></span>
20
+ <span class="icon-bar"></span>
21
+ </button>
22
+ <%= link_to 'Canyon', posts_path, class: 'navbar-brand' %>
23
+ </div>
24
+ <div id="navbar" class="collapse navbar-collapse">
25
+ <ul class="nav navbar-nav">
26
+ <li class="active"><%= link_to 'Posts', posts_path %></li>
27
+ <li><%= link_to 'Categories', '#' %></li>
28
+ <li><%= link_to 'Authors', '#' %></li>
29
+ <li>
30
+ <%= link_to request.base_url, target: '_blank' do %>
31
+ <%= request.base_url %>
32
+ <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span>
33
+ <% end %>
34
+ </li>
35
+ </ul>
36
+ </div>
37
+ </div>
38
+ </nav>
39
+
40
+ <div class="container">
41
+ <% flash.each do |name, flash_message| %>
42
+ <div class="alert alert-<%= name %> alert-dismissible" role="alert">
43
+ <button type="button" class="close" data-dismiss="alert" aria-label="Close">
44
+ <span aria-hidden="true">&times;</span>
45
+ </button>
46
+ <p><%= raw flash_message %></p>
47
+ </div>
48
+ <% end %>
49
+
50
+ <%= yield %>
51
+ </div>
52
+ </body>
53
+ </html>
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application.
3
+
4
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
5
+ ENGINE_PATH = File.expand_path('../../lib/canyon/engine', __FILE__)
6
+
7
+ # Set up gems listed in the Gemfile.
8
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
9
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
10
+
11
+ require 'rails/all'
12
+ require 'rails/engine/commands'
@@ -0,0 +1,22 @@
1
+ en:
2
+ date:
3
+ month_names:
4
+ -
5
+ - January
6
+ - February
7
+ - March
8
+ - April
9
+ - May
10
+ - June
11
+ - July
12
+ - August
13
+ - September
14
+ - October
15
+ - November
16
+ - December
17
+ time:
18
+ am: am
19
+ pm: pm
20
+ formats:
21
+ simple: "%B %d, %Y"
22
+ detailed: "%Y/%m/%d %I:%M %p"
@@ -0,0 +1,5 @@
1
+ Canyon::Engine.routes.draw do
2
+ get '/', to: redirect('posts')
3
+
4
+ resources :posts
5
+ end
@@ -0,0 +1,9 @@
1
+ class CreateAuthors < ActiveRecord::Migration
2
+ def change
3
+ create_table :canyon_authors do |t|
4
+ t.string :name, null: false
5
+ t.string :slug, null: false
6
+ t.timestamps null: false
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ class CreatePosts < ActiveRecord::Migration
2
+ def change
3
+ create_table :canyon_posts do |t|
4
+ t.string :title, null: false
5
+ t.string :slug, null: false
6
+ t.string :language, null: false
7
+ t.text :summary
8
+ t.text :content, null: false
9
+ t.integer :parent_id
10
+ t.integer :author_id, null: false
11
+ t.boolean :deleted, null: false, default: false
12
+ t.boolean :published, null: false, default: false
13
+ t.datetime :published_at, null: false
14
+ t.timestamps null: false
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ Canyon::Author.delete_all
2
+ Canyon::Post.delete_all
3
+
4
+ author = Canyon::Author.create(name: 'Guybrush Threepwood', slug: 'guybrush')
5
+ Canyon::Author.create(name: 'Elaine Marley', slug: 'elaine')
6
+
7
+ Canyon::Post.create!(
8
+ author: author,
9
+ language: 'en',
10
+ title: 'Markdown Syntax Example',
11
+ slug: 'markdown-syntax-example',
12
+ summary: 'This is our fist summary.',
13
+ content: 'This is our **fist posts**.',
14
+ published_at: Time.now,
15
+ )
@@ -0,0 +1,4 @@
1
+ require 'canyon/engine'
2
+
3
+ module Canyon
4
+ end
@@ -0,0 +1,17 @@
1
+ require 'bootstrap-sass'
2
+ require 'sass-rails'
3
+ require 'jquery-rails'
4
+
5
+ module Canyon
6
+ class Engine < ::Rails::Engine
7
+ isolate_namespace Canyon
8
+
9
+ initializer :append_migrations do |app|
10
+ unless app.root.to_s.match root.to_s
11
+ config.paths['db/migrate'].expanded.each do |expanded_path|
12
+ app.config.paths['db/migrate'] << expanded_path
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module Canyon
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,7 @@
1
+ namespace :canyon do
2
+ desc 'Cleanup all preview posts.'
3
+ task :cleanup_preview_posts => :environment do
4
+ count = Canyon::Post.where.not(parent_id: nil).destroy_all.count
5
+ puts "Deleted #{count} posts."
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: canyon
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Martin Jagusch
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bootstrap-sass
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sass-rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: jquery-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sqlite3
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description:
84
+ email:
85
+ - _@mj.io
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - LICENSE.txt
91
+ - README.md
92
+ - app/assets/javascripts/canyon/application.js
93
+ - app/assets/javascripts/canyon/vendor/jquery.autosize.js
94
+ - app/assets/stylesheets/canyon/application.scss
95
+ - app/controllers/canyon/application_controller.rb
96
+ - app/controllers/canyon/posts_controller.rb
97
+ - app/helpers/canyon/application_helper.rb
98
+ - app/models/canyon/author.rb
99
+ - app/models/canyon/post.rb
100
+ - app/views/canyon/posts/_form.html.erb
101
+ - app/views/canyon/posts/edit.html.erb
102
+ - app/views/canyon/posts/index.html.erb
103
+ - app/views/canyon/posts/new.html.erb
104
+ - app/views/layouts/canyon/application.html.erb
105
+ - bin/rails
106
+ - config/locales/en.yml
107
+ - config/routes.rb
108
+ - db/migrate/20150416053508_create_authors.rb
109
+ - db/migrate/20150416053518_create_posts.rb
110
+ - db/seeds.rb
111
+ - lib/canyon.rb
112
+ - lib/canyon/engine.rb
113
+ - lib/canyon/version.rb
114
+ - lib/tasks/canyon_tasks.rake
115
+ homepage: https://github.com/mjio/canyon
116
+ licenses:
117
+ - MIT
118
+ metadata: {}
119
+ post_install_message:
120
+ rdoc_options: []
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ requirements: []
134
+ rubyforge_project:
135
+ rubygems_version: 2.4.5
136
+ signing_key:
137
+ specification_version: 4
138
+ summary: Rails blogging engine
139
+ test_files: []