canyon 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []