seamess 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +166 -0
  4. data/Rakefile +24 -0
  5. data/app/assets/config/seamess_manifest.js +2 -0
  6. data/app/assets/javascripts/seamess/application.js +13 -0
  7. data/app/assets/javascripts/seamess/manager.js +6 -0
  8. data/app/assets/stylesheets/seamess/manager.sass +2 -0
  9. data/app/assets/stylesheets/seamess/partials/footer.sass +7 -0
  10. data/app/assets/stylesheets/seamess/partials/layouts.sass +19 -0
  11. data/app/assets/stylesheets/seamess/partials/manager.sass +48 -0
  12. data/app/assets/stylesheets/seamess/partials/masthead.sass +27 -0
  13. data/app/assets/stylesheets/seamess/partials/shared.sass +5 -0
  14. data/app/assets/stylesheets/seamess/partials/variables.sass +2 -0
  15. data/app/assets/stylesheets/seamess/seamess.sass +1 -0
  16. data/app/controllers/seamess/application_controller.rb +5 -0
  17. data/app/controllers/seamess/manager/base_controller.rb +5 -0
  18. data/app/controllers/seamess/manager/dashboard_controller.rb +4 -0
  19. data/app/controllers/seamess/manager/pages_controller.rb +52 -0
  20. data/app/controllers/seamess/pages_controller.rb +10 -0
  21. data/app/helpers/seamess/application_helper.rb +4 -0
  22. data/app/jobs/seamess/application_job.rb +4 -0
  23. data/app/mailers/seamess/application_mailer.rb +6 -0
  24. data/app/models/seamess/application_record.rb +5 -0
  25. data/app/models/seamess/page.rb +16 -0
  26. data/app/views/layouts/seamess/application.html.haml +16 -0
  27. data/app/views/layouts/seamess/columnar.html.haml +3 -0
  28. data/app/views/layouts/seamess/fluid.html.haml +3 -0
  29. data/app/views/layouts/seamess/manager.html.haml +19 -0
  30. data/app/views/layouts/seamess/pages.html.haml +5 -0
  31. data/app/views/seamess/common/_after_head.html.haml +3 -0
  32. data/app/views/seamess/common/_after_yield.html.haml +7 -0
  33. data/app/views/seamess/common/_before_yield.html.haml +5 -0
  34. data/app/views/seamess/common/_callouts.html.haml +1 -0
  35. data/app/views/seamess/common/_drip.html.haml +13 -0
  36. data/app/views/seamess/common/_facebook_pixel.html.haml +13 -0
  37. data/app/views/seamess/common/_footer.html.haml +1 -0
  38. data/app/views/seamess/common/_google_analytics.html.haml +10 -0
  39. data/app/views/seamess/common/_head.html.haml +12 -0
  40. data/app/views/seamess/common/_javascripts.html.haml +3 -0
  41. data/app/views/seamess/common/_manager_sidebar.html.haml +13 -0
  42. data/app/views/seamess/common/_navbar.html.haml +18 -0
  43. data/app/views/seamess/manager/dashboard/dashboard.html.haml +1 -0
  44. data/app/views/seamess/manager/pages/_form.html.haml +8 -0
  45. data/app/views/seamess/manager/pages/edit.html.haml +4 -0
  46. data/app/views/seamess/manager/pages/index.html.haml +21 -0
  47. data/app/views/seamess/manager/pages/new.html.haml +3 -0
  48. data/app/views/seamess/pages/show.html.haml +2 -0
  49. data/app/views/seamess/pages/styles.html.haml +50 -0
  50. data/config/initializers/simple_form.rb +209 -0
  51. data/config/routes.rb +14 -0
  52. data/db/migrate/20170827174002_create_seamess_pages.rb +10 -0
  53. data/db/migrate/20170829085602_change_seamess_pages_slug_to_nullable.rb +9 -0
  54. data/db/seeds.rb +25 -0
  55. data/lib/seamess.rb +14 -0
  56. data/lib/seamess/engine.rb +9 -0
  57. data/lib/seamess/version.rb +3 -0
  58. data/lib/tasks/seamess_tasks.rake +4 -0
  59. metadata +269 -0
@@ -0,0 +1,10 @@
1
+ class Seamess::PagesController < ApplicationController
2
+ layout "seamess/pages"
3
+
4
+ def show
5
+ @page = Seamess::Page.for_slug_or_404(params[:slug] || "")
6
+ end
7
+
8
+ def styles
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ module Seamess
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Seamess
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module Seamess
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module Seamess
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ class Seamess::Page < ApplicationRecord
2
+ validates :title, presence: true
3
+ validates :slug, uniqueness: true, format: { with: /\A(?!\/)/, message: "must not begin with a slash" }
4
+
5
+ scope :for_manager_index, -> { order(slug: :asc) }
6
+
7
+ class << self
8
+ def for_slug_or_404(slug)
9
+ for_slug(slug) || for_slug('404')
10
+ end
11
+
12
+ def for_slug(slug)
13
+ find_by(slug: slug)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ !!! 5
2
+ %html{lang: :en}
3
+ %head= render partial: "seamess/common/head"
4
+
5
+ = area :after_head do
6
+ = render partial: "seamess/common/after_head"
7
+
8
+ %body{class: "#{area :body_classes, "application"}" }
9
+ = area :before_yield do
10
+ = render partial: "seamess/common/before_yield"
11
+
12
+ %main{role: "main"}= yield
13
+
14
+ = area :after_yield do
15
+ = render partial: "seamess/common/after_yield"
16
+
@@ -0,0 +1,3 @@
1
+ = extends :application do
2
+ = replace :body_classes, "columnar"
3
+ = yield
@@ -0,0 +1,3 @@
1
+ = extends :application do
2
+ = replace :body_classes, "fluid"
3
+ = yield
@@ -0,0 +1,19 @@
1
+ = extends :'layouts/seamess/fluid' do
2
+ = append :body_classes, " admin"
3
+
4
+ = replace :stylesheets do
5
+ = stylesheet_link_tag 'manager', media: 'all'
6
+
7
+ = replace :before_yield do
8
+ = render partial: "seamess/common/navbar"
9
+
10
+ .sidebar-container
11
+ = area :sidebar do
12
+ = render partial: "seamess/common/manager_sidebar"
13
+
14
+ .content
15
+ = render partial: "seamess/common/callouts"
16
+ = yield
17
+
18
+ = replace :after_yield do
19
+ = javascript_include_tag :'seamess/manager'
@@ -0,0 +1,5 @@
1
+ = extends :'layouts/seamess/columnar' do
2
+ = append :body_classes, " pages"
3
+
4
+ .content
5
+ .text= yield
@@ -0,0 +1,3 @@
1
+ - if Rails.env.production? && ENV.fetch('FACEBOOK_PIXEL_ID', nil)
2
+ :javascript
3
+ fbq('track', 'ViewContent')
@@ -0,0 +1,7 @@
1
+ = area :before_footer
2
+
3
+ = area :footer do
4
+ %footer= render partial: "seamess/common/footer"
5
+
6
+ = area :after_footer do
7
+ = render partial: "seamess/common/javascripts"
@@ -0,0 +1,5 @@
1
+ = area :navbar do
2
+ = render partial: "seamess/common/navbar"
3
+
4
+ = area :callouts do
5
+ = render partial: "seamess/common/callouts"
@@ -0,0 +1 @@
1
+ = flash_messages
@@ -0,0 +1,13 @@
1
+ - if Rails.env.production? && (key = ENV.fetch('DRIP_ACCOUNT_KEY', nil))
2
+ :javascript
3
+ var _dcq = _dcq || [];
4
+ var _dcs = _dcs || {};
5
+ _dcs.account = '#{key}';
6
+
7
+ (function() {
8
+ var dc = document.createElement('script');
9
+ dc.type = 'text/javascript'; dc.async = true;
10
+ dc.src = '//tag.getdrip.com/#{key}.js';
11
+ var s = document.getElementsByTagName('script')[0];
12
+ s.parentNode.insertBefore(dc, s);
13
+ })();
@@ -0,0 +1,13 @@
1
+ - if Rails.env.production? && (pixel_id = ENV.fetch('FACEBOOK_PIXEL_ID', nil))
2
+ :javascript
3
+ !function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
4
+ n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
5
+ n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
6
+ t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
7
+ document,'script','https://connect.facebook.net/en_US/fbevents.js');
8
+
9
+ fbq('init', '#{pixel_id}');
10
+ fbq('track', 'PageView');
11
+
12
+ %noscript
13
+ %img(height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=#{pixel_id}&ev=PageView&noscript=1")
@@ -0,0 +1 @@
1
+ &copy; #{Time.now.year} #{ENV.fetch('COPYRIGHT')}. All rights reserved.
@@ -0,0 +1,10 @@
1
+ - if Rails.env.production? && (key = ENV.fetch('GOOGLE_ANALYTICS_KEY', nil))
2
+ :javascript
3
+ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
4
+ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
5
+ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
6
+ })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
7
+
8
+ ga('create', '#{key}', 'auto');
9
+ ga('require', 'linkid');
10
+ ga('send', 'pageview');
@@ -0,0 +1,12 @@
1
+ %title= area :title, ENV.fetch('SITE_TITLE')
2
+
3
+ = area :meta do
4
+ = csrf_meta_tags
5
+ %meta(charset="utf-8")
6
+ %meta(name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no")
7
+
8
+ = area :stylesheets do
9
+ = stylesheet_link_tag 'application', media: 'all'
10
+
11
+ = area :bottom_of_head do
12
+ = render partial: "seamess/common/facebook_pixel"
@@ -0,0 +1,3 @@
1
+ = javascript_include_tag :application
2
+ = render partial: "seamess/common/drip"
3
+ = render partial: "seamess/common/google_analytics"
@@ -0,0 +1,13 @@
1
+ %nav
2
+ %ul.nav.nav-pills.flex-column
3
+ %li.nav-item= bootstrap_link_to "Dashboard", seamess.manager_dashboard_path, active: :exclusive
4
+ %li.nav-item
5
+ = bootstrap_link_to "Pages", "javascript:;", data: { toggle: "collapse", target: "#menu-pages-content"}, aria: { expanded: false, controls: "menu-pages-content" }
6
+
7
+ #menu-pages-content.collapse
8
+ %ul.nav.nav-pills.flex-column
9
+ %li.nav-item= bootstrap_link_to "All Pages", seamess.manager_pages_path
10
+ %li.nav-item= bootstrap_link_to "New Page", seamess.new_manager_page_path
11
+
12
+ %li.nav-item= bootstrap_link_to "Sign out [#{current_admin.email}]", main_app.destroy_admin_session_path, method: :delete
13
+
@@ -0,0 +1,18 @@
1
+ %header.masthead
2
+ %nav
3
+ %button.navbar-toggler{ type: :button,
4
+ data: { toggle: :collapse, target: "#navbar-contents" },
5
+ aria: { controls: "navbar-contents", expanded: "false", label: "Toggle navigation" } }
6
+
7
+ %span.navbar-toggler-icon
8
+
9
+ #navbar-contents
10
+ = area :nav_brand do
11
+ = link_to ENV.fetch('SITE_TITLE'), seamess.root_url, class: "navbar-brand"
12
+
13
+ = area :navbar_contents do
14
+ %ul.navbar-nav
15
+ - if admin_signed_in?
16
+ = bootstrap_link_to "Admin", seamess.manager_root_path
17
+ %li.nav-item= bootstrap_link_to "Home", main_app.root_url
18
+
@@ -0,0 +1,8 @@
1
+ = simple_form_for [seamess, :manager, @page] do |f|
2
+ = f.input :slug, hint: "The path at which the post should be found. Should <strong>not</strong> start with <code>/</code>, but can end with it, i.e. <code>my-page/</code>".html_safe
3
+ = f.input :title, hint: "Used internally. If you want the title in the body, include the title in the body."
4
+ = f.input :body, as: :text, hint: "Markdown is accepted.", input_html: { rows: 14 }
5
+
6
+ .actions
7
+ = link_to "Cancel", seamess.manager_pages_path, class: 'btn btn-secondary'
8
+ = f.submit class: 'btn btn-primary'
@@ -0,0 +1,4 @@
1
+ %h1 Editing "#{@page.title}"
2
+
3
+ = render "form"
4
+
@@ -0,0 +1,21 @@
1
+ %h1 All Pages
2
+
3
+ .controls.mb-2
4
+ = link_to "New Page", seamess.new_manager_page_path, class: 'btn btn-primary btn-lg'
5
+
6
+ %table.table.table-striped.table-responsive
7
+ %thead
8
+ %th Slug
9
+ %th Title
10
+ %th Actions
11
+
12
+ %tbody
13
+ - @pages.each do |page|
14
+ %tr
15
+ %td= page.slug
16
+ %td= link_to page.title, seamess.manager_page_path(page)
17
+ %td
18
+ = link_to "Edit", seamess.edit_manager_page_path(page)
19
+ |
20
+ = link_to "Destroy", seamess.manager_page_path(page), method: :destroy, data: {confirm: "Are you sure you wish to delete this page?" }
21
+
@@ -0,0 +1,3 @@
1
+ %h1 New Page
2
+
3
+ = render "form"
@@ -0,0 +1,2 @@
1
+ :markdown
2
+ #{raw @page.body}
@@ -0,0 +1,50 @@
1
+ - %w(alert-success alert-danger alert-warning alert-info).each do |klass|
2
+ .alert.fade.show{class: klass, data: {closable: true}}
3
+ Nyah, see?!
4
+ %button.close{data: { dismiss: 'alert' }} &times;
5
+
6
+
7
+ :markdown
8
+ # Header 1
9
+
10
+ lorem ipsum dolor sit amet [consectutur adipiscing elit](/).
11
+
12
+ -----
13
+
14
+
15
+ ## Header 2
16
+
17
+ lorem ipsum dolor sit amet
18
+
19
+ - thing 1
20
+ - thing 2
21
+ - thing 3
22
+ - thing 4
23
+
24
+ ### Header 3
25
+
26
+ lorem ipsum dolor sit amet
27
+
28
+ 1. thing
29
+ 2. thing
30
+ 3. thing
31
+ 4. thing
32
+
33
+ #### Header 4
34
+
35
+ lorem ipsum dolor sit amet
36
+
37
+ ##### Header 5
38
+
39
+ lorem ipsum dolor sit amet
40
+
41
+ ###### Header 6
42
+
43
+ lorem ipsum dolor sit amet
44
+
45
+
46
+ ## Styles
47
+
48
+ - %w(primary secondary success danger warning info light dark).each do |t|
49
+ %button.btn{class: "btn-#{t}"}= t
50
+
@@ -0,0 +1,209 @@
1
+ require_dependency "simple_form"
2
+
3
+ SimpleForm.setup do |config|
4
+ config.error_notification_class = 'alert alert-danger'
5
+ config.button_class = 'btn btn-primary'
6
+ config.boolean_label_class = nil
7
+
8
+ config.wrappers :vertical_form, tag: 'div', class: 'form-group' do |b|
9
+ b.use :html5
10
+ b.use :placeholder
11
+ b.optional :maxlength
12
+ b.optional :minlength
13
+ b.optional :pattern
14
+ b.optional :min_max
15
+ b.optional :readonly
16
+
17
+ b.use :label, class: 'col-form-label' do
18
+
19
+ end
20
+ b.use :input, class: 'form-control'
21
+ b.use :error, wrap_with: {tag: 'span', class: 'form-control-feedback'}
22
+ b.use :hint, wrap_with: {tag: 'small', class: 'form-text text-muted'}
23
+ end
24
+
25
+ config.wrappers :vertical_file_input, tag: 'div', class: 'form-group' do |b|
26
+ b.use :html5
27
+ b.use :placeholder
28
+ b.optional :maxlength
29
+ b.optional :minlength
30
+ b.optional :readonly
31
+
32
+ b.use :label, class: 'col-form-label'
33
+ b.use :input
34
+ b.use :error, wrap_with: {tag: 'span', class: 'form-control-feedback'}
35
+ b.use :hint, wrap_with: {tag: 'p', class: 'form-text text-muted'}
36
+ end
37
+
38
+ config.wrappers :vertical_boolean, tag: 'div', class: 'form-group' do |b|
39
+ b.use :html5
40
+ b.optional :readonly
41
+
42
+ b.wrapper tag: 'div', class: 'checkbox' do |ba|
43
+ ba.use :label_input
44
+ end
45
+
46
+ b.use :error, wrap_with: {tag: 'span', class: 'form-control-feedback'}
47
+ b.use :hint, wrap_with: {tag: 'p', class: 'form-text text-muted'}
48
+ end
49
+
50
+ config.wrappers :vertical_radio_and_checkboxes, tag: 'div', class: 'form-group' do |b|
51
+ b.use :html5
52
+ b.optional :readonly
53
+
54
+ b.use :label, class: 'col-form-label'
55
+ b.use :input
56
+ b.use :error, wrap_with: {tag: 'span', class: 'form-control-feedback'}
57
+ b.use :hint, wrap_with: {tag: 'p', class: 'form-text text-muted'}
58
+ end
59
+
60
+ config.wrappers :horizontal_form, tag: 'div', class: 'form-group row' do |b|
61
+ b.use :html5
62
+ b.use :placeholder
63
+ b.optional :maxlength
64
+ b.optional :minlength
65
+ b.optional :pattern
66
+ b.optional :min_max
67
+ b.optional :readonly
68
+
69
+ b.use :label, class: 'col-md-3 col-form-label'
70
+ b.wrapper tag: 'div', class: 'col-md-9' do |ba|
71
+ ba.use :input, class: 'form-control'
72
+ ba.use :error, wrap_with: {tag: 'span', class: 'form-control-feedback'}
73
+ ba.use :hint, wrap_with: {tag: 'p', class: 'form-text text-muted'}
74
+ end
75
+ end
76
+
77
+ config.wrappers :horizontal_file_input, tag: 'div', class: 'form-group row' do |b|
78
+ b.use :html5
79
+ b.use :placeholder
80
+ b.optional :maxlength
81
+ b.optional :minlength
82
+ b.optional :readonly
83
+ b.use :label, class: 'col-md-3 col-form-label'
84
+
85
+ b.wrapper tag: 'div', class: 'col-md-9' do |ba|
86
+ ba.use :input
87
+ ba.use :error, wrap_with: {tag: 'span', class: 'form-control-feedback'}
88
+ ba.use :hint, wrap_with: {tag: 'p', class: 'form-text text-muted'}
89
+ end
90
+ end
91
+
92
+ config.wrappers :horizontal_boolean, tag: 'div', class: 'form-group row' do |b|
93
+ b.use :html5
94
+ b.optional :readonly
95
+
96
+ b.wrapper tag: 'div', class: 'offset-md-3 col-md-9' do |wr|
97
+ wr.wrapper tag: 'div', class: '' do |ba|
98
+ ba.use :label_input
99
+ end
100
+
101
+ wr.use :error, wrap_with: {tag: 'span', class: 'form-control-feedback'}
102
+ wr.use :hint, wrap_with: {tag: 'p', class: 'form-text text-muted'}
103
+ end
104
+ end
105
+
106
+ config.wrappers :horizontal_radio_and_checkboxes, tag: 'div', class: 'form-group row' do |b|
107
+ b.use :html5
108
+ b.optional :readonly
109
+
110
+ b.use :label, class: 'col-md-3 col-form-label'
111
+ b.wrapper tag: 'div', class: 'col-md-9' do |ba|
112
+ ba.use :input
113
+ ba.use :error, wrap_with: {tag: 'span', class: 'form-control-feedback'}
114
+ ba.use :hint, wrap_with: {tag: 'p', class: 'form-text text-muted'}
115
+ end
116
+ end
117
+
118
+ config.wrappers :inline_form, tag: 'div', class: 'form-group' do |b|
119
+ b.use :html5
120
+ b.use :placeholder
121
+ b.optional :maxlength
122
+ b.optional :minlength
123
+ b.optional :pattern
124
+ b.optional :min_max
125
+ b.optional :readonly
126
+
127
+ b.use :label, class: 'sr-only'
128
+ b.use :input, class: 'form-control'
129
+ b.use :error, wrap_with: {tag: 'span', class: 'form-control-feedback'}
130
+ b.use :hint, wrap_with: {tag: 'p', class: 'form-text text-muted'}
131
+ end
132
+
133
+ config.wrappers :multi_select, tag: 'div', class: 'form-group' do |b|
134
+ b.use :html5
135
+ b.optional :readonly
136
+
137
+ b.use :label, class: 'col-form-label'
138
+ b.wrapper tag: 'div', class: 'form-inline' do |ba|
139
+ ba.use :input, class: 'form-control'
140
+ ba.use :error, wrap_with: {tag: 'span', class: 'form-control-feedback'}
141
+ ba.use :hint, wrap_with: {tag: 'p', class: 'form-text text-muted'}
142
+ end
143
+ end
144
+
145
+
146
+ config.default_wrapper = :vertical_form
147
+ config.wrapper_mappings = {
148
+ check_boxes: :vertical_radio_and_checkboxes,
149
+ radio_buttons: :vertical_radio_and_checkboxes,
150
+ file: :vertical_file_input,
151
+ boolean: :vertical_boolean,
152
+ datetime: :multi_select,
153
+ date: :multi_select,
154
+ time: :multi_select
155
+ }
156
+ end
157
+
158
+ # SimpleForm::ActionViewExtensions::FormHelper.class_eval do
159
+ #
160
+ # private
161
+ #
162
+ # def simple_form_css_class(record, options)
163
+ # html_options = options[:html]
164
+ # as = options[:as]
165
+ #
166
+ # if html_options.key?(:class)
167
+ # html_options[:class]
168
+ # elsif record.is_a?(String) || record.is_a?(Symbol)
169
+ # as || record
170
+ # else
171
+ # form_css_class_from_record(record, as)
172
+ # end
173
+ # end
174
+ #
175
+ # def form_css_class_from_record(record, as)
176
+ # classes = []
177
+ #
178
+ # record = record.last if record.is_a?(Array)
179
+ # action = record.respond_to?(:persisted?) && record.persisted? ? :edit : :new
180
+ # classes << as ? "#{action}_#{as}" : dom_class(record, action)
181
+ #
182
+ # classes << 'was-validated' if record.errors.any?
183
+ #
184
+ # classes.join(' ')
185
+ # end
186
+ # end
187
+
188
+ SimpleForm::Inputs::Base.class_eval do
189
+ # We just need the ability to pass a block on the `use` function
190
+ # then we save it on the Leaf class to be able to call it
191
+ def merge_wrapper_options(options, wrapper_options)
192
+ if wrapper_options
193
+ wrapper_options.merge(options) do |key, oldval, newval|
194
+ case key.to_s
195
+ when 'class'
196
+ classes = Array(oldval) + Array(newval)
197
+ classes << 'is-invalid' if has_errors?
198
+ classes
199
+ when 'data', 'aria'
200
+ oldval.merge(newval)
201
+ else
202
+ newval
203
+ end
204
+ end
205
+ else
206
+ options
207
+ end
208
+ end
209
+ end