cloudpress 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.travis.yml +3 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +39 -0
  7. data/Rakefile +30 -0
  8. data/app/assets/cloudpress/cloudpress.css.sass +1 -0
  9. data/app/assets/cloudpress/code/railscasts.css +70 -0
  10. data/app/controllers/cloudpress/application_controller.rb +12 -0
  11. data/app/controllers/cloudpress/archives_controller.rb +34 -0
  12. data/app/controllers/cloudpress/posts_controller.rb +23 -0
  13. data/app/controllers/cloudpress/tags_controller.rb +20 -0
  14. data/app/helpers/cloudpress/renderer_helper.rb +23 -0
  15. data/app/jobs/cloudpress/update_posts_job.rb +9 -0
  16. data/app/models/cloudpress/archive.rb +50 -0
  17. data/app/models/cloudpress/post.rb +65 -0
  18. data/app/views/cloudpress/archives/_archive.html.slim +3 -0
  19. data/app/views/cloudpress/archives/_archives.html.slim +2 -0
  20. data/app/views/cloudpress/archives/_month.html.slim +2 -0
  21. data/app/views/cloudpress/archives/show.html.slim +1 -0
  22. data/app/views/cloudpress/posts/_intro.html.slim +9 -0
  23. data/app/views/cloudpress/posts/_post.html.slim +8 -0
  24. data/app/views/cloudpress/posts/index.html.slim +1 -0
  25. data/app/views/cloudpress/posts/show.html.slim +1 -0
  26. data/app/views/cloudpress/tags/_tag.html.slim +2 -0
  27. data/app/views/cloudpress/tags/_tags.html.slim +2 -0
  28. data/app/views/cloudpress/tags/show.html.slim +1 -0
  29. data/bin/console +14 -0
  30. data/bin/rails +12 -0
  31. data/bin/setup +7 -0
  32. data/cloudpress.gemspec +35 -0
  33. data/config/initializers/tag_extensions.rb +4 -0
  34. data/config/routes.rb +9 -0
  35. data/db/migrate/20150309115639_create_cloudpress_posts.rb +19 -0
  36. data/db/migrate/20150315192521_create_tags_table.rb +30 -0
  37. data/db/migrate/20150315193206_create_friendly_id_slugs.rb +15 -0
  38. data/db/migrate/20150317121651_add_slug_to_tags.rb +6 -0
  39. data/db/migrate/20150317122608_add_taggings_counter_cache.rb +14 -0
  40. data/lib/cloudpress.rb +105 -0
  41. data/lib/cloudpress/dropbox/cleaner.rb +20 -0
  42. data/lib/cloudpress/dropbox/client.rb +15 -0
  43. data/lib/cloudpress/dropbox/connector.rb +24 -0
  44. data/lib/cloudpress/dropbox/directory.rb +57 -0
  45. data/lib/cloudpress/dropbox/file.rb +57 -0
  46. data/lib/cloudpress/engine.rb +10 -0
  47. data/lib/cloudpress/formatters/html_with_pygments.rb +7 -0
  48. data/lib/cloudpress/renderers/archive.rb +18 -0
  49. data/lib/cloudpress/renderers/archives.rb +24 -0
  50. data/lib/cloudpress/renderers/post.rb +39 -0
  51. data/lib/cloudpress/renderers/posts.rb +38 -0
  52. data/lib/cloudpress/renderers/tag.rb +18 -0
  53. data/lib/cloudpress/renderers/tags.rb +24 -0
  54. data/lib/cloudpress/version.rb +3 -0
  55. data/lib/generators/cloudpress/initializer/USAGE +8 -0
  56. data/lib/generators/cloudpress/initializer/initializer_generator.rb +32 -0
  57. metadata +283 -0
@@ -0,0 +1,3 @@
1
+ li
2
+ = link_to renderer.archive.year, archives_path(renderer.archive.year)
3
+ = content_tag(:ul, render(partial: 'cloudpress/archives/month', collection: renderer.archive.months, locals: {renderer: renderer}))
@@ -0,0 +1,2 @@
1
+ section#archive-list
2
+ = yield
@@ -0,0 +1,2 @@
1
+ li
2
+ = link_to month.strftime("%B"), archives_path(renderer.archive.year, month.month)
@@ -0,0 +1 @@
1
+ = render_cloudpress_posts(posts)
@@ -0,0 +1,9 @@
1
+ article id="post-#{renderer.post.id}"
2
+ header
3
+ h1 = link_to renderer.post.title, post_path(renderer.post)
4
+ time = renderer.post.publish_date.to_s(:long)
5
+ = render_cloudpress_tags(renderer.post.tags) if renderer.tags?
6
+ p
7
+ = renderer.post.summary
8
+ footer
9
+ = link_to 'Read more', post_path(renderer.post)
@@ -0,0 +1,8 @@
1
+ article id="post-#{renderer.post.id}"
2
+ header
3
+ h1 = renderer.post.title
4
+ time.published-at = renderer.post.publish_date.to_s(:long)
5
+ = render_cloudpress_tags(renderer.post.tags) if renderer.tags?
6
+ = renderer.post.body.html_safe
7
+ footer
8
+ = yield
@@ -0,0 +1 @@
1
+ = render_cloudpress_posts(posts)
@@ -0,0 +1 @@
1
+ = render_cloudpress_post(post)
@@ -0,0 +1,2 @@
1
+ li
2
+ = link_to renderer.tag.name, tag_path(renderer.tag.slug), class: 'tag'
@@ -0,0 +1,2 @@
1
+ ul.tag-list
2
+ = yield
@@ -0,0 +1 @@
1
+ = render_cloudpress_posts(posts)
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "cloudpress"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/rails ADDED
@@ -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/cloudpress/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'
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cloudpress/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "cloudpress"
8
+ spec.version = Cloudpress::VERSION
9
+ spec.authors = ["Adam Carlile"]
10
+ spec.email = ["adam@benchmedia.co.uk"]
11
+
12
+ spec.summary = "A blogging framework that uses dropbox as it's storage engine"
13
+ spec.homepage = "http://github.com/adamcarlile/cloudpress"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "dropbox-sdk"
22
+ spec.add_dependency "metadown"
23
+ spec.add_dependency "pygments.rb"
24
+ spec.add_dependency "nokogiri"
25
+ spec.add_dependency "kaminari"
26
+ spec.add_dependency "builder"
27
+ spec.add_dependency "groupdate"
28
+ spec.add_dependency "slim"
29
+ spec.add_dependency "acts-as-taggable-on"
30
+ spec.add_dependency "friendly_id"
31
+ spec.add_dependency "rails", "~> 4.2.0"
32
+
33
+ spec.add_development_dependency "bundler", "~> 1.8"
34
+ spec.add_development_dependency "rake", "~> 10.0"
35
+ end
@@ -0,0 +1,4 @@
1
+ class ActsAsTaggableOn::Tag
2
+ extend FriendlyId
3
+ friendly_id :name, use: [:slugged, :finders]
4
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,9 @@
1
+ Cloudpress::Engine.routes.draw do
2
+
3
+ resources :tags, only: :show
4
+ resource :archives, path: '/:year(/:month)', only: :show
5
+
6
+ root to: 'posts#index'
7
+ get '*slug', to: 'posts#show', as: :post
8
+
9
+ end
@@ -0,0 +1,19 @@
1
+ class CreateCloudpressPosts < ActiveRecord::Migration
2
+ def change
3
+ create_table :cloudpress_posts do |t|
4
+ t.string :title
5
+ t.string :slug
6
+ t.string :file_path
7
+ t.string :revision
8
+ t.string :state
9
+ t.text :body
10
+ t.json :metadata
11
+ t.datetime :publish_date
12
+ t.timestamps null: false
13
+ end
14
+
15
+ add_index :cloudpress_posts, :state
16
+ add_index :cloudpress_posts, :slug
17
+ add_index :cloudpress_posts, :file_path
18
+ end
19
+ end
@@ -0,0 +1,30 @@
1
+ class CreateTagsTable < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :tags do |t|
4
+ t.string :name
5
+ end
6
+
7
+ create_table :taggings do |t|
8
+ t.references :tag
9
+
10
+ # You should make sure that the column created is
11
+ # long enough to store the required class names.
12
+ t.references :taggable, :polymorphic => true
13
+ t.references :tagger, :polymorphic => true
14
+
15
+ # Limit is created to prevent MySQL error on index
16
+ # length for MyISAM table type: http://bit.ly/vgW2Ql
17
+ t.string :context, :limit => 128
18
+
19
+ t.datetime :created_at
20
+ end
21
+
22
+ add_index :taggings, :tag_id
23
+ add_index :taggings, [:taggable_id, :taggable_type, :context]
24
+ end
25
+
26
+ def self.down
27
+ drop_table :taggings
28
+ drop_table :tags
29
+ end
30
+ end
@@ -0,0 +1,15 @@
1
+ class CreateFriendlyIdSlugs < ActiveRecord::Migration
2
+ def change
3
+ create_table :friendly_id_slugs do |t|
4
+ t.string :slug, :null => false
5
+ t.integer :sluggable_id, :null => false
6
+ t.string :sluggable_type, :limit => 50
7
+ t.string :scope
8
+ t.datetime :created_at
9
+ end
10
+ add_index :friendly_id_slugs, :sluggable_id
11
+ add_index :friendly_id_slugs, [:slug, :sluggable_type]
12
+ add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], :unique => true
13
+ add_index :friendly_id_slugs, :sluggable_type
14
+ end
15
+ end
@@ -0,0 +1,6 @@
1
+ class AddSlugToTags < ActiveRecord::Migration
2
+ def change
3
+ add_column :tags, :slug, :string
4
+ add_index :tags, :slug, unique: true
5
+ end
6
+ end
@@ -0,0 +1,14 @@
1
+ class AddTaggingsCounterCache < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :tags, :taggings_count, :integer, default: 0
4
+
5
+ ActsAsTaggableOn::Tag.reset_column_information
6
+ ActsAsTaggableOn::Tag.find_each do |tag|
7
+ ActsAsTaggableOn::Tag.reset_counters(tag.id, :taggings)
8
+ end
9
+ end
10
+
11
+ def self.down
12
+ remove_column :tags, :taggings_count
13
+ end
14
+ end
data/lib/cloudpress.rb ADDED
@@ -0,0 +1,105 @@
1
+ require "dropbox_sdk"
2
+ require "metadown"
3
+ require "redcarpet"
4
+ require "pygments"
5
+ require "kaminari"
6
+ require "groupdate"
7
+ require "slim"
8
+ require "friendly_id"
9
+ require "acts-as-taggable-on"
10
+ require "cloudpress/version"
11
+ require "cloudpress/engine"
12
+
13
+ require "cloudpress/formatters/html_with_pygments"
14
+ require "cloudpress/dropbox/client"
15
+ require "cloudpress/dropbox/connector"
16
+ require "cloudpress/dropbox/directory"
17
+ require "cloudpress/dropbox/file"
18
+ require "cloudpress/dropbox/cleaner"
19
+
20
+ require "cloudpress/renderers/archive"
21
+ require "cloudpress/renderers/archives"
22
+ require "cloudpress/renderers/post"
23
+ require "cloudpress/renderers/posts"
24
+ require "cloudpress/renderers/tag"
25
+ require "cloudpress/renderers/tags"
26
+
27
+ module Cloudpress
28
+
29
+ module_function
30
+
31
+ def config
32
+ @config ? @config : configure
33
+ end
34
+
35
+ def configure &block
36
+ @config = ActiveSupport::OrderedOptions.new
37
+ @config.app_key = nil
38
+ @config.app_secret = nil
39
+ @config.app_token = nil
40
+ @config.base_path = 'cloudpress'
41
+ @config.live_path = 'live'
42
+ @config.draft_path = 'draft'
43
+ @config.markdown_renderer = Redcarpet::Markdown.new(Formatters::HTMLWithPygments, fenced_code_blocks: true)
44
+ yield(@config) if block_given?
45
+ @config
46
+ end
47
+
48
+ def base_path
49
+ "/#{config.base_path}"
50
+ end
51
+
52
+ def reload!
53
+ @file_index = nil
54
+ end
55
+
56
+ def live_path
57
+ [base_path, config.live_path].join('/')
58
+ end
59
+
60
+ def draft_path
61
+ [base_path, config.draft_path].join('/')
62
+ end
63
+
64
+ def update!
65
+ reload!
66
+ import!
67
+ purge!
68
+ end
69
+
70
+ def import!
71
+ file_index.values.each {|x| x.import!}
72
+ end
73
+
74
+ def purge!
75
+ Cloudpress::Dropbox::Cleaner.new(remote_paths, local_paths).clean!
76
+ end
77
+
78
+ def file_index
79
+ @file_index ||= {
80
+ live: Cloudpress::Dropbox::Directory.new(client, live_path, :live),
81
+ draft: Cloudpress::Dropbox::Directory.new(client, draft_path, :draft)
82
+ }
83
+ end
84
+
85
+ def client
86
+ @client ||= Cloudpress::Dropbox::Client.new(config.app_token)
87
+ end
88
+
89
+ def unconfigured?
90
+ config.app_key.blank? || config.app_secret.blank?
91
+ end
92
+
93
+ def unauthorized?
94
+ config.app_token.blank?
95
+ end
96
+
97
+ def remote_paths
98
+ file_index.values.map {|x| x.paths }.flatten
99
+ end
100
+
101
+ def local_paths
102
+ Cloudpress::Post.pluck(:file_path)
103
+ end
104
+
105
+ end
@@ -0,0 +1,20 @@
1
+ module Cloudpress
2
+ module Dropbox
3
+ class Cleaner
4
+
5
+ def initialize(remote_paths, local_paths)
6
+ @local_paths = local_paths
7
+ @remote_paths = remote_paths
8
+ end
9
+
10
+ def orphaned_paths
11
+ @orphaned_paths ||= @local_paths - @remote_paths
12
+ end
13
+
14
+ def clean!
15
+ Post.where(file_path: orphaned_paths).destroy_all if orphaned_paths.any?
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ module Cloudpress
2
+ module Dropbox
3
+ class Client
4
+
5
+ def initialize(token)
6
+ @token = token
7
+ end
8
+
9
+ def connection
10
+ @connection ||= DropboxClient.new(@token)
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ module Cloudpress
2
+ module Dropbox
3
+ class Connector
4
+
5
+ def initialize(key, secret)
6
+ @key = key
7
+ @secret = secret
8
+ end
9
+
10
+ def flow
11
+ @flow ||= DropboxOAuth2FlowNoRedirect.new(@key, @secret)
12
+ end
13
+
14
+ def authorize_url
15
+ @authorize_url ||= flow.start
16
+ end
17
+
18
+ def authorize!(code)
19
+ flow.finish(code)
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,57 @@
1
+ module Cloudpress
2
+ module Dropbox
3
+ class Directory
4
+
5
+ attr_reader :path, :client, :state
6
+
7
+ def initialize(client, path, state)
8
+ @client = client
9
+ @path = path
10
+ @state = state
11
+ end
12
+
13
+ def import!
14
+ files.each { |x| x.import! }
15
+ subdirectories.each {|x| x.import! }
16
+ end
17
+
18
+ def paths
19
+ @paths ||= begin
20
+ files.map {|x| x.path } + subdirectories.map { |x| x.paths }
21
+ end.flatten.compact.uniq
22
+ end
23
+
24
+ def directory_contents
25
+ remote_path_metadata.with_indifferent_access[:contents]
26
+ end
27
+
28
+ def files
29
+ @files ||= directory_contents.select { |x| x[:is_dir] == false }.map do |x|
30
+ Cloudpress::Dropbox::File.new(x[:path], x[:rev], state)
31
+ end
32
+ end
33
+
34
+ def subdirectories
35
+ @subdirectories ||= directory_contents.select { |x| x[:is_dir] == true }.map do |x|
36
+ Cloudpress::Dropbox::Directory.new(client, x[:path], state)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def create_path!
43
+ client.connection.file_create_folder path
44
+ end
45
+
46
+ def remote_path_metadata
47
+ @remote_path_metadata ||= begin
48
+ client.connection.metadata(path)
49
+ rescue DropboxError
50
+ create_path!
51
+ client.connection.metadata(path)
52
+ end
53
+ end
54
+
55
+ end
56
+ end
57
+ end