action_page 0.1.0

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
+ SHA256:
3
+ metadata.gz: 8c102ef0c54f16662928586b6340c1b0b7cacb12e4625ef3eff8afa630c3105a
4
+ data.tar.gz: 1dca2f2a761119fdaaf2fbf7e06e40c384699abaa13958b55ba35cd23dab5a13
5
+ SHA512:
6
+ metadata.gz: d688f8f9f18170f56ea3363f2273509a76ea8a8ba3f7c10f351f5a0686c6efffe5016ab8da19209b769ad71e4d73e818791a6dd08829e347211f416c752b2379
7
+ data.tar.gz: 4d08ae5cd821e1fd3902d4eadffd32a0b2dd6042bcec9ce9fe78b98de7bd972a819ad060be62c770430d2d24a9daafa8439b7ee44bf5de56a5d1a16ff8c1a97c
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2020 Caleb Hearth
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,118 @@
1
+ # ActionPage
2
+
3
+ ActionPage provides a Rails template handler for views ending in .yaml or .yml
4
+ which contain "frontmatter" that is valid YAML contained between two sets of
5
+ `---`, such as:
6
+
7
+ ---
8
+ author: Caleb Hearth
9
+ website: https://calebhearth.com
10
+ likes:
11
+ - Dogs
12
+ - Frontmatter
13
+ - Rails
14
+ dislikes:
15
+ - Beets
16
+ ---
17
+
18
+ This frontmatter is parsed and made available as getter methods in a model you
19
+ define per "collection" of views. This collection might be "posts" or "talks"
20
+ and conceptually it can be considered similarly to an ActiveRecord::Base
21
+ subclass, but with the view file as the data source rather than a database.
22
+
23
+ ## Usage
24
+
25
+ A simple use case would be to import blog posts into Rails from a Jekyll site.
26
+ The code to do that might look like the below. Note that the semantics are very
27
+ familiar to anyone who's used Rails applications for similar purposes.
28
+
29
+ ### app/models/post.rb
30
+
31
+ ```ruby
32
+ class Post < ActionPost::Base
33
+ end
34
+ ```
35
+
36
+ `ActionPost::Base` provides class-level "all" and "find(slug)" methods which
37
+ list all pages and finds the first page matching the slug. It also extracts all
38
+ frontmatter and provides it as getter methods such as `post.title`.
39
+
40
+ ### app/controllers/posts_controller.rb
41
+
42
+ A controller is required, but there is no need to define the default actions.
43
+ Default index and show actions are provided that list all posts (all files in
44
+ app/views/posts with a .yaml or .yml extension) and finds posts based on their
45
+ "slug" which is the filename without any extensions.
46
+
47
+ ```ruby
48
+ class PostsController < ActionPage::BaseController
49
+ end
50
+ ```
51
+
52
+ The ActionPost::Base subclass is inferred to be the singular of the Controller's
53
+ class name without "Controller", so `PostsController` would render `Post`
54
+ objects and look for views in `app/views/posts`. The class-level `renders_page`
55
+ method overrides this:
56
+
57
+ ```
58
+ class PostsController < ActionPage::BaseController
59
+ renders_page :blog_post # BlogPost objects / app/views/blog_posts views.
60
+ end
61
+ ```
62
+
63
+ ### config/routes.rb
64
+
65
+ ```ruby
66
+ resources :posts, only: %i(index show)
67
+ ```
68
+
69
+ ### app/views/posts/my-first-post.md.yaml
70
+
71
+ This original Jekyll file would be called `_posts/2020-05-30-my-first-post.md`
72
+ and would not have a date field in the frontmatter. That would need to be
73
+ migrated manually or scripted. Date extraction from filename is not a feature of
74
+ ActionPage.
75
+
76
+ ```markdown
77
+
78
+ ---
79
+ title: My First Post
80
+ date: 2020-05-30
81
+ ---
82
+
83
+ Look at all the things I'm not doing!
84
+ ```
85
+
86
+ ### app/views/posts/show.html.erb
87
+
88
+ ```erb
89
+ <h1><%= @page.title %></h1>
90
+ <aside><%= time_tag @post.date %></aside>
91
+ <%= page_content @page %>
92
+ ```
93
+
94
+ ### app/views/posts/index.html.erb
95
+
96
+ ```erb
97
+ <h1>My Posts</h1>
98
+ <ul><% @posts.each do |post| %>
99
+ <li><%= link_to @post.title, post_path(@post) %></li>
100
+ <% end %></ul>
101
+ ```
102
+
103
+ ## Installation
104
+ Add this line to your application's Gemfile:
105
+
106
+ ```ruby
107
+ gem 'action_page'
108
+ ```
109
+
110
+ And then execute:
111
+ ```bash
112
+ $ bundle
113
+ ```
114
+
115
+ Or install it yourself as:
116
+ ```bash
117
+ $ gem install action_page
118
+ ```
@@ -0,0 +1,32 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'ActionPage'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+ load 'rails/tasks/statistics.rake'
21
+
22
+ require 'bundler/gem_tasks'
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'test'
28
+ t.pattern = 'test/**/*_test.rb'
29
+ t.verbose = false
30
+ end
31
+
32
+ task default: :test
@@ -0,0 +1 @@
1
+ //= link_directory ../stylesheets/action_page .css
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,36 @@
1
+ module ActionPage
2
+ class BaseController < ::ApplicationController
3
+ cattr_reader :page_class
4
+ helper ActionPage::ApplicationHelper
5
+
6
+ def self.renders_page(page)
7
+ @@page_class = page
8
+ end
9
+
10
+ def self.page_class
11
+ @@page_class ||= begin
12
+ page_class = name.sub(/Controller\z/, '')
13
+ page_class.singularize.constantize
14
+ rescue NameError
15
+ raise #ActionPage::MissingPageClass.new(page: page_class)
16
+ end
17
+ end
18
+
19
+ def initialize(*args)
20
+ super
21
+ append_view_path("app/views/#{self.class.page_class.view_key}")
22
+ end
23
+
24
+ def index
25
+ @pages = page_class.all
26
+ end
27
+
28
+ def show
29
+ @page = page_class.find(params[:id])
30
+ if @page.nil?
31
+ raise ActionController::RoutingError.new("Not Found")
32
+ end
33
+ render
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,7 @@
1
+ module ActionPage
2
+ module ApplicationHelper
3
+ def page_content(page)
4
+ controller.render_to_string("#{page.class.view_key}/#{page.slug}", layout: false)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ module ActionPage
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module ActionPage
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module ActionPage
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,45 @@
1
+ module ActionPage
2
+ class Base
3
+ extend ActiveModel::Naming
4
+ attr_reader :slug
5
+
6
+ def self.all
7
+ Dir[pages_directory]
8
+ .reject { %w(index show).include? File.basename(_1.split('.')[0]) }
9
+ .map { |f| new(f) }
10
+ end
11
+
12
+ def self.find(slug)
13
+ all.find { _1.slug == slug }
14
+ end
15
+
16
+ def self.pages_directory
17
+ Rails.root.join("app", "views", view_key, '*.yaml').freeze
18
+ end
19
+
20
+ def initialize(filename)
21
+ @slug = File.basename(filename.split('.')[0])
22
+ @frontmatter = YAML.load(File.read(filename)).with_indifferent_access
23
+ end
24
+
25
+ # TODO: Define attribute methods to speed this up
26
+ def method_missing(m, *args, &block)
27
+ if value = @frontmatter[m]
28
+ return value
29
+ end
30
+ super
31
+ end
32
+
33
+ def respond_to_missing(m)
34
+ @frontmatter.key(m)
35
+ end
36
+
37
+ def to_param
38
+ slug
39
+ end
40
+
41
+ def self.view_key
42
+ model_name.route_key
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Action page</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= stylesheet_link_tag "action_page/application", media: "all" %>
9
+ </head>
10
+ <body>
11
+
12
+ <%= yield %>
13
+
14
+ </body>
15
+ </html>
File without changes
@@ -0,0 +1,2 @@
1
+ ActionPage::Engine.routes.draw do
2
+ end
@@ -0,0 +1,5 @@
1
+ require "action_page/engine"
2
+ require "action_page/template_handler"
3
+
4
+ module ActionPage
5
+ end
@@ -0,0 +1,15 @@
1
+ module ActionPage
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace ActionPage
4
+ initializer "actionpage.load" do
5
+ ActiveSupport.on_load :action_view do
6
+ ActionView::Template.register_template_handler(
7
+ # Accept all existing handlers with additional .yaml or .yml extension
8
+ *ActionView::Template.template_handler_extensions.flat_map { %W(#{_1}.yaml #{_1}.yml) },
9
+ :yml, :yaml,
10
+ ActionPage::TemplateHandler
11
+ )
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,34 @@
1
+ module ActionPage
2
+ class TemplateHandler
3
+ def self.call(template, source)
4
+ new(template, source).call
5
+ end
6
+
7
+ def initialize(template, source)
8
+ @template = template
9
+ @source = source
10
+ end
11
+
12
+ def call
13
+ handler_for_template.call(@template, source_without_yaml)
14
+ end
15
+
16
+ def handler_for_template
17
+ ApplicationController.new.view_paths.paths.first
18
+ .extract_handler_and_format_and_variant(path_without_yaml)
19
+ .first
20
+ end
21
+
22
+ def source_without_yaml
23
+ @source.lines[last_line+1...].join
24
+ end
25
+
26
+ def path_without_yaml
27
+ @template.short_identifier.sub(/.ya?ml\z/, '')
28
+ end
29
+
30
+ def last_line
31
+ YAML.parse(@source).root.end_line
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ module ActionPage
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :action_page do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: action_page
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Caleb Hearth
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-05-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: railties
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
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: rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
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: pg
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email:
71
+ - caleb@calebhearth.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - LICENSE
77
+ - README.md
78
+ - Rakefile
79
+ - app/assets/config/action_page_manifest.js
80
+ - app/assets/stylesheets/action_page/application.css
81
+ - app/controllers/action_page/base_controller.rb
82
+ - app/helpers/action_page/application_helper.rb
83
+ - app/jobs/action_page/application_job.rb
84
+ - app/mailers/action_page/application_mailer.rb
85
+ - app/models/action_page/application_record.rb
86
+ - app/models/action_page/base.rb
87
+ - app/views/layouts/action_page/application.html.erb
88
+ - config/environment.rb
89
+ - config/routes.rb
90
+ - lib/action_page.rb
91
+ - lib/action_page/engine.rb
92
+ - lib/action_page/template_handler.rb
93
+ - lib/action_page/version.rb
94
+ - lib/tasks/action_page_tasks.rake
95
+ homepage: https://calebhearth.com
96
+ licenses:
97
+ - MIT
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubygems_version: 3.1.2
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: Semi-static pages with data in Rails.
118
+ test_files: []