eucalypt 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +26 -0
- data/Rakefile +2 -0
- data/bin/eucalypt +3 -0
- data/eucalypt.gemspec +39 -0
- data/lib/eucalypt/app.rb +6 -0
- data/lib/eucalypt/controller.rb +10 -0
- data/lib/eucalypt/errors.rb +109 -0
- data/lib/eucalypt/eucalypt-blog/helpers.rb +106 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/__base__.rb +22 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/__require__.rb +1 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/cli/blog.rb +65 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/generators/article.rb +28 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/generators/controller.rb +14 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/generators/helper.rb +12 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/generators/list.rb +74 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/generators/views.rb +20 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/templates/controller/controller.tt +33 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/templates/controller/controller_spec.tt +43 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/templates/helper/helper.tt +5 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/templates/helper/helper_spec.tt +9 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/templates/views/article.erb +1 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/templates/views/article_layout.erb +10 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/templates/views/article_md.tt +9 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/templates/views/articles.erb +1 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/templates/views/articles_layout.erb +10 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog/templates/views/search.erb +1 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog-article/cli/article.rb +120 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog-article-edit/cli/edit-datetime.rb +113 -0
- data/lib/eucalypt/eucalypt-blog/namespaces/blog-article-edit/cli/edit-urltitle.rb +75 -0
- data/lib/eucalypt/eucalypt-core/cli/__base__.rb +11 -0
- data/lib/eucalypt/eucalypt-core/cli/console.rb +15 -0
- data/lib/eucalypt/eucalypt-core/cli/core.rb +6 -0
- data/lib/eucalypt/eucalypt-core/cli/help.rb +11 -0
- data/lib/eucalypt/eucalypt-core/cli/init.rb +71 -0
- data/lib/eucalypt/eucalypt-core/cli/launch.rb +33 -0
- data/lib/eucalypt/eucalypt-core/cli/test.rb +16 -0
- data/lib/eucalypt/eucalypt-core/cli/version.rb +11 -0
- data/lib/eucalypt/eucalypt-core/templates/Gemfile.tt +35 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/.gitignore +48 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/.travis.yml +8 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/Procfile +1 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/Rakefile +7 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/assets/fonts/.empty_directory +0 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/assets/images/.empty_directory +0 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/assets/scripts/application.js +17 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/assets/stylesheets/__partials__.scss +16 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/assets/stylesheets/application.scss +17 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/assets/stylesheets/partials/_mixins.scss +54 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/controllers/application_controller.rb +7 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/helpers/application_helper.rb +3 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/models/.empty_directory +0 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/static/.empty_directory +0 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/static/readme.yml +34 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/views/index.erb +0 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/views/layouts/main.erb +9 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app/views/partials/.empty_directory +0 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/app.rb +42 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/config/active_record.rb +6 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/config/asset_pipeline.rb +15 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/config/database.yml +16 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/config/initializers/.empty_directory +0 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/config/logging.rb +27 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/config/manifest.rb +15 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/config.ru +10 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/log/.empty_directory +0 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/spec/controllers/application_controller_spec.rb +9 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/spec/helpers/application_helper_spec.rb +9 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/spec/models/.empty_directory +0 -0
- data/lib/eucalypt/eucalypt-core/templates/eucalypt/spec/spec_helper.rb +18 -0
- data/lib/eucalypt/eucalypt-destroy/helpers.rb +77 -0
- data/lib/eucalypt/eucalypt-destroy/namespaces/destroy/cli/destroy-controller.rb +16 -0
- data/lib/eucalypt/eucalypt-destroy/namespaces/destroy/cli/destroy-helper.rb +16 -0
- data/lib/eucalypt/eucalypt-destroy/namespaces/destroy/cli/destroy-model.rb +16 -0
- data/lib/eucalypt/eucalypt-destroy/namespaces/destroy/cli/destroy-scaffold.rb +63 -0
- data/lib/eucalypt/eucalypt-destroy/namespaces/destroy/cli/destroy.rb +21 -0
- data/lib/eucalypt/eucalypt-generate/.gitkeep +0 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate/cli/generate-scaffold.rb +62 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate/cli/generate.rb +24 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-controller/cli/generate-controller.rb +29 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-controller/generators/controller.rb +45 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-controller/templates/controller/controller.tt +3 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-controller/templates/controller/policy_rest_controller.tt +71 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-controller/templates/controller/rest_controller.tt +28 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-controller/templates/controller_spec.tt +9 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-helper/cli/generate-helper.rb +23 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-helper/generators/helper.rb +24 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-helper/templates/helper.tt +3 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-helper/templates/helper_spec.tt +9 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-model/cli/generate-model.rb +26 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-model/generators/model.rb +25 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-model/templates/model.tt +3 -0
- data/lib/eucalypt/eucalypt-generate/namespaces/generate-model/templates/model_spec.tt +8 -0
- data/lib/eucalypt/eucalypt-migration/helpers.rb +93 -0
- data/lib/eucalypt/eucalypt-migration/migration_base.tt +4 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration/cli/migration.rb +39 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-add/cli/add-column.rb +25 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-add/cli/add-index.rb +25 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-add/cli/add.rb +18 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-add/generators/column.rb +46 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-add/generators/index.rb +52 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-blank/cli/blank.rb +22 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-blank/generators/blank.rb +28 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-change/cli/change-column.rb +23 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-change/cli/change.rb +17 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-change/generators/column.rb +46 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-create/cli/create-table.rb +25 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-create/cli/create.rb +17 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-create/generators/table.rb +53 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-drop/cli/drop-column.rb +22 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-drop/cli/drop-index.rb +23 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-drop/cli/drop-table.rb +22 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-drop/cli/drop.rb +19 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-drop/generators/column.rb +38 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-drop/generators/index.rb +48 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-drop/generators/table.rb +37 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-rename/cli/rename-column.rb +22 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-rename/cli/rename-index.rb +22 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-rename/cli/rename-table.rb +24 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-rename/cli/rename.rb +19 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-rename/generators/column.rb +39 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-rename/generators/index.rb +39 -0
- data/lib/eucalypt/eucalypt-migration/namespaces/migration-rename/generators/table.rb +38 -0
- data/lib/eucalypt/eucalypt-security/helpers.rb +22 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security/cli/security.rb +31 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-policy/cli/security-policy.rb +91 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-policy/generators/policy.rb +31 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-policy/templates/create_policy_roles_migration.tt +11 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-policy/templates/policy.tt +16 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-policy-permission/cli/security-policy-permission.rb +62 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-policy-permission/generators/policy-permission.rb +28 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-policy-permission/templates/add_permission_to_policy_migration.tt +5 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-policy-role/cli/security-policy-role.rb +66 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-pundit/cli/security-pundit.rb +79 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-pundit/generators/role.rb +24 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-pundit/templates/create_roles_migration.tt +7 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-pundit/templates/pundit.tt +4 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-warden/cli/security-warden.rb +61 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-warden/generators/auth_controller.rb +34 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-warden/generators/user.rb +37 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-warden/templates/auth_controller.tt +25 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-warden/templates/auth_login.tt +1 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-warden/templates/create_users_table_migration.tt +9 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-warden/templates/user.tt +16 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-warden/templates/warden.tt +35 -0
- data/lib/eucalypt/eucalypt-security/namespaces/security-warden/user_confirm.rb +38 -0
- data/lib/eucalypt/helpers/colorize.rb +27 -0
- data/lib/eucalypt/helpers/gemfile.rb +48 -0
- data/lib/eucalypt/helpers/inflect.rb +79 -0
- data/lib/eucalypt/helpers/messages.rb +31 -0
- data/lib/eucalypt/helpers/migration.rb +85 -0
- data/lib/eucalypt/helpers/numeric.rb +10 -0
- data/lib/eucalypt/helpers.rb +6 -0
- data/lib/eucalypt/list.rb +39 -0
- data/lib/eucalypt/static.rb +48 -0
- data/lib/eucalypt/version.rb +3 -0
- data/lib/eucalypt.rb +19 -0
- metadata +373 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: aa6a5ff7a9dcd9363c00239a5f3c4f061cf4462c619cc8b59f1cdf406cd876b3
|
4
|
+
data.tar.gz: 5b40cd192caec208c295bbe054b4bded5bbb87d4c0a8fe50feeb94a16159edfc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7d0814b65d0d6c769e0d4f104066c5aeee5b3be0b4f0c0da18955b38938e951ddf71cf11918e6adca2c33a169cf8ede27a92f9b55b3bd784e5ad06340e5a8bce
|
7
|
+
data.tar.gz: 2445d94f7f54fb716c06220d429b94a895b352172ddb85ce91abc163b23c8dad51bf1a8de31410daf3863d0a468764c3846afc6b12cf1dce92e44829dc36a9ff
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2018 Edwin Onuonga
|
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,26 @@
|
|
1
|
+
[![Ruby Version](https://img.shields.io/badge/ruby-~%3E%202.5-red.svg)](https://github.com/eonu/eucalypt/blob/0c509a4e22fd97ec52b6f638af21de783f3aafc8/eucalypt.gemspec#L19)
|
2
|
+
[![Gem](https://img.shields.io/gem/v/eucalypt.svg)](https://rubygems.org/gems/eucalypt)
|
3
|
+
[![Build Status](https://travis-ci.org/eonu/eucalypt.svg?branch=master)]
|
4
|
+
[![License](https://img.shields.io/github/license/eonu/eucalypt.svg)]
|
5
|
+
|
6
|
+
# Eucalypt
|
7
|
+
|
8
|
+
Micro-framework and CLI for the generation and maintenance of structured Sinatra web applications.
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
To install the CLI, run:
|
13
|
+
|
14
|
+
```bash
|
15
|
+
$ gem install eucalypt
|
16
|
+
```
|
17
|
+
|
18
|
+
## Usage
|
19
|
+
|
20
|
+
Initialize a new application with:
|
21
|
+
|
22
|
+
```bash
|
23
|
+
$ eucalypt init my-new-app
|
24
|
+
```
|
25
|
+
|
26
|
+
And you're ready to go!
|
data/Rakefile
ADDED
data/bin/eucalypt
ADDED
data/eucalypt.gemspec
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "eucalypt/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "eucalypt"
|
7
|
+
spec.version = Eucalypt::VERSION
|
8
|
+
spec.authors = ["Edwin Onuonga"]
|
9
|
+
spec.email = ["edwinonuonga@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = %q{Micro-framework and CLI for the generation and maintenance of structured Sinatra web applications.}
|
12
|
+
spec.homepage = "https://eucalypt.gitbook.io/eucalypt/"
|
13
|
+
spec.license = "MIT"
|
14
|
+
spec.files = Dir.glob('lib/**/*', File::FNM_DOTMATCH) + %w[Gemfile LICENSE README.md Rakefile eucalypt.gemspec bin/eucalypt]
|
15
|
+
spec.bindir = "bin"
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) {|f| File.basename(f)}
|
17
|
+
spec.require_paths = ["lib"]
|
18
|
+
|
19
|
+
spec.required_ruby_version = "~> 2.5"
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
22
|
+
spec.add_development_dependency "rake", "~> 12.3"
|
23
|
+
spec.add_development_dependency "rspec", "~> 3.7"
|
24
|
+
spec.add_development_dependency "regexp-examples", "~> 1.4"
|
25
|
+
spec.add_development_dependency "colorize", "~> 0.8"
|
26
|
+
|
27
|
+
spec.add_runtime_dependency "string-builder", "~> 2.3"
|
28
|
+
spec.add_runtime_dependency "activesupport", "~> 5.2"
|
29
|
+
spec.add_runtime_dependency "activerecord", "~> 5.2"
|
30
|
+
spec.add_runtime_dependency "front_matter_parser", "0.2.0"
|
31
|
+
spec.add_runtime_dependency "thor", "~> 0.20"
|
32
|
+
spec.add_runtime_dependency "sinatra", "~> 2.0"
|
33
|
+
spec.add_runtime_dependency "rerun", "~> 0.13"
|
34
|
+
|
35
|
+
spec.metadata = {
|
36
|
+
# "documentation_uri" => "https://eucalypt.gitbook.io/eucalypt/",
|
37
|
+
"source_code_uri" => "https://github.com/eucalypt/eucalypt/"
|
38
|
+
}
|
39
|
+
end
|
data/lib/eucalypt/app.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
class ApplicationController < Sinatra::Base; end
|
3
|
+
module Eucalypt
|
4
|
+
def self.Controller(route:)
|
5
|
+
name = File.basename(caller[0][/[^:]+/],'.*').camelize
|
6
|
+
Object.const_set name, Class.new(::ApplicationController)
|
7
|
+
name.constantize.instance_eval %{def router() "#{route}" end}
|
8
|
+
::ApplicationController
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'eucalypt/app'
|
2
|
+
require 'eucalypt/helpers'
|
3
|
+
require 'string/builder'
|
4
|
+
|
5
|
+
module Eucalypt
|
6
|
+
module Error
|
7
|
+
include Eucalypt::Helpers
|
8
|
+
include Eucalypt::Helpers::Messages
|
9
|
+
using String::Builder
|
10
|
+
using Colorize
|
11
|
+
|
12
|
+
class << self
|
13
|
+
include Eucalypt::Helpers::Messages
|
14
|
+
def wrong_directory
|
15
|
+
puts
|
16
|
+
Out.error "Couldn't find #{Eucalypt::APP_FILE.colorize(:bold)} in current directory."
|
17
|
+
puts
|
18
|
+
Out.info 'Try:'
|
19
|
+
puts " - Changing the current working directory to your application's root directory."
|
20
|
+
puts " - Creating a new application with `#{'eucalypt init'.colorize(:bold)}`."
|
21
|
+
puts " - Creating a #{Eucalypt::APP_FILE.colorize(:bold)} if you deleted it."
|
22
|
+
end
|
23
|
+
|
24
|
+
def found_app_file
|
25
|
+
Out.warning "Found #{Eucalypt::APP_FILE.colorize(:bold)} in the current directory."
|
26
|
+
puts
|
27
|
+
Out.info
|
28
|
+
puts " - The current directory might already be a Eucalypt application."
|
29
|
+
puts " - Proceeding with the initialization will create a new nested application."
|
30
|
+
puts " - This shouldn't be a problem, but is it really what you intended?"
|
31
|
+
puts
|
32
|
+
end
|
33
|
+
|
34
|
+
def no_articles
|
35
|
+
Out.error "Couldn't find any blog articles"
|
36
|
+
end
|
37
|
+
|
38
|
+
def delete_article_warning
|
39
|
+
Out.warning 'Deleting an article will delete both the markdown file and all assets!'
|
40
|
+
end
|
41
|
+
|
42
|
+
def no_mvc(mvc_file)
|
43
|
+
Out.error "Couldn't find any #{mvc_file}s"
|
44
|
+
end
|
45
|
+
|
46
|
+
def no_scaffolds
|
47
|
+
Out.error "Couldn't find any scaffolds."
|
48
|
+
end
|
49
|
+
|
50
|
+
def no_gems(gems, command)
|
51
|
+
Out.error "Couldn't find gems #{gems} in Gemfile."
|
52
|
+
puts
|
53
|
+
Out.info
|
54
|
+
puts " - Ensure you have run the setup command `#{command.colorize(:bold)}`."
|
55
|
+
end
|
56
|
+
|
57
|
+
def no_user_model
|
58
|
+
Out.error "Couldn't find a user model."
|
59
|
+
puts
|
60
|
+
Out.info
|
61
|
+
command = 'eucalypt security warden setup'
|
62
|
+
puts " - Ensure you have run the setup command `#{command.colorize(:bold)}`."
|
63
|
+
end
|
64
|
+
|
65
|
+
def no_role_model
|
66
|
+
Out.error "Couldn't find a role model."
|
67
|
+
puts
|
68
|
+
Out.info
|
69
|
+
command = 'eucalypt security pundit setup'
|
70
|
+
puts " - Ensure you have run the setup command `#{command.colorize(:bold)}`."
|
71
|
+
end
|
72
|
+
|
73
|
+
def no_policy(policy_name)
|
74
|
+
Out.error "Couldn't find a #{policy_name} role model or policy file."
|
75
|
+
puts
|
76
|
+
Out.info
|
77
|
+
command = "eucalypt security policy g #{policy_name}"
|
78
|
+
puts " - Ensure you have run the setup command `#{command.colorize(:bold)}`."
|
79
|
+
end
|
80
|
+
|
81
|
+
def invalid_columns(invalid_declarations, invalid_types)
|
82
|
+
puts if invalid_declarations.any? || invalid_types.any?
|
83
|
+
if invalid_declarations.any?
|
84
|
+
Out.error "Invalid column declaration(s): #{invalid_declarations.inspect}"
|
85
|
+
Out.info "Column declarations should match the regex: #{Eucalypt::Helpers::Migration::Validation::DECLARATION_REGEX.inspect}"
|
86
|
+
puts " - Examples: name:string, price:decimal, elo:primary_key"
|
87
|
+
end
|
88
|
+
if invalid_types.any?
|
89
|
+
output = String.build "Invalid column type(s): " do |s|
|
90
|
+
invalid_types.each_with_index do |obj, i|
|
91
|
+
type, column = obj[:type], obj[:column]
|
92
|
+
s << "#{column}:#{type.colorize(:bold)}"
|
93
|
+
s << ', ' unless i == invalid_types.size-1
|
94
|
+
end
|
95
|
+
end
|
96
|
+
puts if invalid_declarations.any? && invalid_types.any?
|
97
|
+
Out.error output
|
98
|
+
Out.info "To list all permitted column types, run the command `#{"eucalypt migration types".colorize(:bold)}`"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def invalid_type(type)
|
103
|
+
puts
|
104
|
+
Out.error "Invalid column type: #{type.colorize(:bold)}"
|
105
|
+
Out.info "To list all permitted column types, run the command `#{"eucalypt migration types".colorize(:bold)}`"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'thor'
|
2
|
+
module Eucalypt
|
3
|
+
class Blog < Thor
|
4
|
+
module Helpers
|
5
|
+
|
6
|
+
module Array::DateSort
|
7
|
+
refine Array.singleton_class do
|
8
|
+
def date_sorter(order, obj)
|
9
|
+
obj.sort! do |x1, x2|
|
10
|
+
if order == :ascending
|
11
|
+
x1[:datetime] <=> x2[:datetime]
|
12
|
+
else
|
13
|
+
x2[:datetime] <=> x1[:datetime]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
refine Array do
|
19
|
+
def sort_by_date!(order: :descending)
|
20
|
+
obj = self.map!{|x|x[:datetime] = DateTime.strptime(x[:time], "%Y-%m-%d %H:%M:%S"); x}
|
21
|
+
Array.date_sorter(order, obj)
|
22
|
+
end
|
23
|
+
def sort_by_date(order: :descending)
|
24
|
+
obj = self.map{|x|x[:datetime] = DateTime.strptime(x[:time], "%Y-%m-%d %H:%M:%S"); x}
|
25
|
+
Array.date_sorter(order, obj)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Blog
|
31
|
+
class << self
|
32
|
+
def articles=(val) @@articles = val end
|
33
|
+
def articles() @@articles end
|
34
|
+
def all() @@articles.map{|md|FrontMatterParser::Parser.parse_file(md).front_matter.symbolize_keys} end
|
35
|
+
def search(query) all.select{|post|post[:tags].any?{|tag|tag.include?(query)}} end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Article
|
39
|
+
def initialize(md) @parsed = FrontMatterParser::Parser.parse_file(md) end
|
40
|
+
def front_matter() @parsed.front_matter.symbolize_keys end
|
41
|
+
def content() @parsed.content end
|
42
|
+
def route() "/#{front_matter[:time].split(' ').first.gsub(?-,?/)}/#{front_matter[:urltitle]}" end
|
43
|
+
def asset(file) File.join(front_matter[:assetpath].sub('/assets',''), file) end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class << self
|
48
|
+
private
|
49
|
+
|
50
|
+
def construct_date_regex(date)
|
51
|
+
y = date[:year]
|
52
|
+
m = date[:month]
|
53
|
+
d = date[:day]
|
54
|
+
return nil unless y || m || d
|
55
|
+
return /#{y}\-#{m}\-#{d}/ if y && m && d
|
56
|
+
return /#{y}\-#{m}\-[0-9][0-9]/ if y && m && !d
|
57
|
+
return /#{y}\-[0-9][0-9]\-#{d}/ if y && !m && d
|
58
|
+
return /[0-9]*\-#{m}\-#{d}/ if !y && m && d
|
59
|
+
return /#{y}\-[0-9][0-9]\-[0-9][0-9]/ if y && !m && !d
|
60
|
+
return /[0-9]*\-#{m}\-[0-9][0-9]/ if !y && m && !d
|
61
|
+
return /[0-9]*\-[0-9][0-9]\-#{d}/ if !y && !m && d
|
62
|
+
end
|
63
|
+
|
64
|
+
def clean_directory(base)
|
65
|
+
Dir.chdir base do
|
66
|
+
Dir[?*].each do |year|
|
67
|
+
Dir.chdir year do
|
68
|
+
Dir[?*].each do |month|
|
69
|
+
Dir.chdir month do
|
70
|
+
Dir[?*].each do |day|
|
71
|
+
FileUtils.rm_rf day if Dir.empty? day
|
72
|
+
end
|
73
|
+
end
|
74
|
+
FileUtils.rm_rf month if Dir.empty? month
|
75
|
+
end
|
76
|
+
end
|
77
|
+
FileUtils.rm_rf year if Dir.empty? year
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def build_article_hash(articles, article_base)
|
83
|
+
articles_hash = Hash.new
|
84
|
+
articles.each_with_index do |article,i|
|
85
|
+
index_key = (i+1).to_s.to_sym
|
86
|
+
articles_hash[index_key] = {}
|
87
|
+
articles_hash[index_key][:path] = article
|
88
|
+
articles_hash[index_key][:markdown] = article.split(article_base.gsub(/\/$/,'')<<?/).last
|
89
|
+
articles_hash[index_key][:identifier] = articles_hash[index_key][:markdown].rpartition(?.).first
|
90
|
+
articles_hash[index_key][:base_name] = File.basename articles_hash[index_key][:markdown]
|
91
|
+
articles_hash[index_key][:date] = articles_hash[index_key][:identifier].rpartition(?/).first
|
92
|
+
articles_hash[index_key][:front_matter] = FrontMatterParser::Parser.parse_file(
|
93
|
+
articles_hash[index_key][:path]
|
94
|
+
).front_matter
|
95
|
+
title = articles_hash[index_key][:front_matter]['title']
|
96
|
+
puts "\e[1m#{index_key}\e[0m: #{articles_hash[index_key][:identifier]}"
|
97
|
+
puts "#{' '*(index_key.to_s.length+2)}#{title}" if title
|
98
|
+
puts
|
99
|
+
end
|
100
|
+
articles_hash
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'front_matter_parser'
|
3
|
+
require 'string/builder'
|
4
|
+
require 'active_support'
|
5
|
+
require 'active_support/core_ext'
|
6
|
+
|
7
|
+
require 'eucalypt/helpers'
|
8
|
+
require 'eucalypt/eucalypt-blog/helpers'
|
9
|
+
|
10
|
+
module Eucalypt
|
11
|
+
module Generators
|
12
|
+
class Blog < Thor::Group
|
13
|
+
include Thor::Actions
|
14
|
+
include Eucalypt::Helpers
|
15
|
+
include Eucalypt::Blog::Helpers
|
16
|
+
|
17
|
+
def self.source_root
|
18
|
+
File.join __dir__, 'templates'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Dir[File.join __dir__, 'generators', '*.rb'].each{|file| require file}
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'eucalypt/errors'
|
2
|
+
require 'eucalypt/helpers'
|
3
|
+
require 'eucalypt/eucalypt-blog/namespaces/blog/__require__'
|
4
|
+
require 'eucalypt/eucalypt-blog/namespaces/blog-article/cli/article'
|
5
|
+
|
6
|
+
module Eucalypt
|
7
|
+
class Blog < Thor
|
8
|
+
include Thor::Actions
|
9
|
+
include Eucalypt::Helpers
|
10
|
+
include Eucalypt::Helpers::Messages
|
11
|
+
include Eucalypt::Helpers::Gemfile
|
12
|
+
using Colorize
|
13
|
+
|
14
|
+
method_option :route, type: :string, aliases: '-r', default: 'blog', desc: "The route at which the blog lies"
|
15
|
+
desc "setup", "Sets up the blog-aware environment".colorize(:grey)
|
16
|
+
def setup
|
17
|
+
directory = File.expand_path('.')
|
18
|
+
if Eucalypt.app? directory
|
19
|
+
Out.setup 'Setting up blog environment...'
|
20
|
+
|
21
|
+
gemfile_add(
|
22
|
+
'Markdown and YAML front-matter parsing',
|
23
|
+
{front_matter_parser: '0.2.0', rdiscount: '~> 2.2'},
|
24
|
+
directory
|
25
|
+
)
|
26
|
+
|
27
|
+
generator = Eucalypt::Generators::Blog.new
|
28
|
+
generator.destination_root = directory
|
29
|
+
generator.helper
|
30
|
+
generator.controller(route: options[:route])
|
31
|
+
generator.views
|
32
|
+
|
33
|
+
asset_pipeline_file = File.join(directory, 'config', 'asset_pipeline.rb')
|
34
|
+
|
35
|
+
File.open(asset_pipeline_file) do |f|
|
36
|
+
return if f.read.include? "environment.append_path Eucalypt.path 'app', 'assets', 'blog'"
|
37
|
+
end
|
38
|
+
|
39
|
+
insert_into_file(
|
40
|
+
asset_pipeline_file,
|
41
|
+
"\tenvironment.append_path Eucalypt.path 'app', 'assets', 'blog'\n",
|
42
|
+
after: "set :environment, Sprockets::Environment.new\n"
|
43
|
+
)
|
44
|
+
else
|
45
|
+
Eucalypt::Error.wrong_directory
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class << self
|
50
|
+
require 'eucalypt/list'
|
51
|
+
include Eucalypt::List
|
52
|
+
def banner(task, namespace = false, subcommand = true)
|
53
|
+
basename + ' ' + task.formatted_usage(self, true, subcommand).split(':').join(' ')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
register(Eucalypt::BlogArticle, 'article', 'article [COMMAND]', 'Create, edit and destroy blog articles'.colorize(:grey))
|
58
|
+
end
|
59
|
+
|
60
|
+
class CLI < Thor
|
61
|
+
include Eucalypt::Helpers
|
62
|
+
using Colorize
|
63
|
+
register(Blog, 'blog', 'blog [COMMAND]', 'Manage static blog environment'.colorize(:grey))
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'eucalypt/eucalypt-blog/namespaces/blog/__base__'
|
2
|
+
|
3
|
+
module Eucalypt
|
4
|
+
module Generators
|
5
|
+
class Blog < Thor::Group
|
6
|
+
def article(urltitle:)
|
7
|
+
urltitle = Inflect.route(urltitle)
|
8
|
+
|
9
|
+
dt = Hash.new
|
10
|
+
dt[:full] = Time.now.strftime("%Y-%m-%d %H:%M:%S")
|
11
|
+
dt[:date] = dt[:full].split(' ').first
|
12
|
+
|
13
|
+
# Assets path generation
|
14
|
+
asset_base = File.join 'app', 'assets', 'blog'
|
15
|
+
asset_path = File.join asset_base, dt[:date].gsub(?-,?/), urltitle
|
16
|
+
|
17
|
+
empty_directory(asset_path)
|
18
|
+
|
19
|
+
# Markdown file and path generation
|
20
|
+
article_base = File.join 'app', 'views', 'blog', 'markdown'
|
21
|
+
article_path = File.join article_base, dt[:date].gsub(?-,?/), "#{urltitle}.md"
|
22
|
+
|
23
|
+
config = {datetime: dt[:full], date: dt[:date], urltitle: urltitle}
|
24
|
+
template File.join('views', 'article_md.tt'), article_path, config
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'eucalypt/eucalypt-blog/namespaces/blog/__base__'
|
2
|
+
|
3
|
+
module Eucalypt
|
4
|
+
module Generators
|
5
|
+
class Blog < Thor::Group
|
6
|
+
def controller(route:)
|
7
|
+
route = "/#{Inflect.route(route)}"
|
8
|
+
config = {route: route}
|
9
|
+
template File.join('controller','controller.tt'), File.join('app','controllers','blog_controller.rb'), config
|
10
|
+
template File.join('controller','controller_spec.tt'), File.join('spec','controllers','blog_controller_spec.rb')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'eucalypt/eucalypt-blog/namespaces/blog/__base__'
|
2
|
+
|
3
|
+
module Eucalypt
|
4
|
+
module Generators
|
5
|
+
class Blog < Thor::Group
|
6
|
+
def helper
|
7
|
+
template File.join('helper','helper.tt'), File.join('app','helpers','blog_helper.rb')
|
8
|
+
template File.join('helper','helper_spec.tt'), File.join('spec','helpers','blog_helper_spec.rb')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'eucalypt/helpers'
|
2
|
+
require 'eucalypt/eucalypt-blog/namespaces/blog/__base__'
|
3
|
+
|
4
|
+
module Eucalypt
|
5
|
+
module Generators
|
6
|
+
class Blog < Thor::Group
|
7
|
+
include Eucalypt::Helpers
|
8
|
+
using Array::DateSort
|
9
|
+
using String::Builder
|
10
|
+
using Colorize
|
11
|
+
|
12
|
+
def list(tag, order, date)
|
13
|
+
chars = {corner: ?+, vertical: ?║, horizontal: ?═}
|
14
|
+
|
15
|
+
puts
|
16
|
+
|
17
|
+
files = Dir.glob(File.join(self.destination_root,"app/views/blog/markdown/**/*.md"))
|
18
|
+
if files.empty?
|
19
|
+
puts "0 article(s) found."
|
20
|
+
return
|
21
|
+
end
|
22
|
+
|
23
|
+
date_regex = Eucalypt::Blog::Helpers.send :construct_date_regex, date
|
24
|
+
|
25
|
+
metadata = files.map{|md|FrontMatterParser::Parser.parse_file(md).front_matter.symbolize_keys}
|
26
|
+
metadata.select!{|post|post[:tags].any?{|t|t.include?(tag)}} unless tag.empty?
|
27
|
+
metadata.select!{|post|date_regex.match? post[:time].split.first} if date_regex
|
28
|
+
metadata.sort_by_date!(order: order)
|
29
|
+
|
30
|
+
number = metadata.length
|
31
|
+
if number == 0
|
32
|
+
puts "0 article(s) found.\n"
|
33
|
+
return
|
34
|
+
else
|
35
|
+
puts "#{metadata.length} article(s) found.\n"
|
36
|
+
end
|
37
|
+
|
38
|
+
longest = 0
|
39
|
+
metadata.each do |article|
|
40
|
+
article.each do |k,v|
|
41
|
+
next if %i[desc datetime].include? k
|
42
|
+
str = "#{k}: #{v}"
|
43
|
+
longest = str.length if longest < str.length
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
output = String.build "\n" do |s|
|
48
|
+
s << (chars[:corner]+chars[:horizontal]*(longest+2)+chars[:corner]+"\n")
|
49
|
+
metadata.each do |article|
|
50
|
+
article.each do |k,v|
|
51
|
+
next if k == :datetime
|
52
|
+
str = "#{k}: #{v}"
|
53
|
+
if k == :desc
|
54
|
+
value = v.to_s
|
55
|
+
rel = longest-(k.length+5) # 5 is from ": " and "..."
|
56
|
+
unless str.length <= longest
|
57
|
+
str = "#{k}: #{value.length > rel ? "#{value[0...rel]}..." : value}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
len = str.ljust(longest,' ').length-str.length
|
61
|
+
s << "#{chars[:vertical]} "
|
62
|
+
s << "#{k.to_s.colorize(:magenta)}: #{str[(k.to_s.length+2)..-1]}"
|
63
|
+
s << ' '*len
|
64
|
+
s << " #{chars[:vertical]}\n"
|
65
|
+
end
|
66
|
+
s << (chars[:corner]+chars[:horizontal]*(longest+2)+chars[:corner]+"\n")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
puts output
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'eucalypt/eucalypt-blog/namespaces/blog/__base__'
|
2
|
+
|
3
|
+
module Eucalypt
|
4
|
+
module Generators
|
5
|
+
class Blog < Thor::Group
|
6
|
+
def views
|
7
|
+
config = {erb: ["<%= application :css, :js %>","<%= yield %>"]}
|
8
|
+
template File.join('views','article_layout.erb'), File.join('app','views','layouts','blog','article.erb'), config
|
9
|
+
template File.join('views','articles_layout.erb'), File.join('app','views','layouts','blog','articles.erb'), config
|
10
|
+
|
11
|
+
config = {erb: "<%= content %>"}
|
12
|
+
template File.join('views','article.erb'), File.join('app','views','blog','article.erb'), config
|
13
|
+
|
14
|
+
config = {erb: "<%= @articles %>"}
|
15
|
+
template File.join('views','articles.erb'), File.join('app','views','blog','articles.erb'), config
|
16
|
+
template File.join('views','search.erb'), File.join('app','views','blog','search.erb'), config
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class BlogController < Eucalypt::Controller(route: '<%= config[:route] %>')
|
2
|
+
helpers BlogHelper
|
3
|
+
using Array::DateSort
|
4
|
+
|
5
|
+
# Display all blog articles at this route
|
6
|
+
get '/' do
|
7
|
+
@articles = Blog.all.sort_by_date order: :descending
|
8
|
+
erb :'blog/articles', layout: :'layouts/blog/articles'
|
9
|
+
end
|
10
|
+
|
11
|
+
# Display a single blog article at this route
|
12
|
+
Blog.articles.each do |md|
|
13
|
+
article = Blog::Article.new md
|
14
|
+
get article.route do
|
15
|
+
@front_matter = article.front_matter
|
16
|
+
locals = {content: markdown(article.content), article: article}
|
17
|
+
erb :'blog/article', locals: locals, layout: :'layouts/blog/article'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Search for blog articles by tag at this route
|
22
|
+
get '/search/:tag' do
|
23
|
+
@tag = params[:tag]
|
24
|
+
redirect to '/' unless @tag.match /\A[a-zA-Z]*[a-zA-Z0-9\\-_.]*[a-zA-Z0-9]\Z/
|
25
|
+
@articles = Blog.search(@tag).sort_by_date order: :descending
|
26
|
+
erb :'blog/search', layout: :'layouts/blog/articles'
|
27
|
+
end
|
28
|
+
|
29
|
+
# Redirect all other blog routes
|
30
|
+
get %r{/.*} do
|
31
|
+
redirect to '/'
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
require 'front_matter_parser'
|
3
|
+
|
4
|
+
describe BlogController do
|
5
|
+
def app() BlogController end
|
6
|
+
|
7
|
+
ARTICLES = Eucalypt.glob('app', 'views', 'blog', 'markdown', '**', '*.md').map do |md|
|
8
|
+
FrontMatterParser::Parser.parse_file(md).front_matter.symbolize_keys
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'blog page' do
|
12
|
+
it "should display all posts" do
|
13
|
+
get '/'
|
14
|
+
expect(true).to be false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
ARTICLES.each do |post|
|
19
|
+
subpath = post[:time].split(' ').first.gsub(?-,?/)
|
20
|
+
|
21
|
+
describe "Post: #{post[:title]}" do
|
22
|
+
before { get "/#{subpath}/#{post[:urltitle]}" }
|
23
|
+
|
24
|
+
it "should have a timestamp" do
|
25
|
+
expect(true).to be false
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should have a title" do
|
29
|
+
expect(true).to be false
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should have a description" do
|
33
|
+
expect(true).to be false
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should have tags" do
|
37
|
+
post[:tags].each do |tag|
|
38
|
+
expect(true).to be false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|