ruhoh 2.5 → 2.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/Gemfile +1 -1
  2. data/bin/ruhoh +10 -3
  3. data/features/_root.feature +11 -0
  4. data/features/data.feature +78 -0
  5. data/features/javascripts.feature +36 -0
  6. data/features/permalinks.feature +23 -0
  7. data/features/plugins.feature +84 -0
  8. data/features/sort_order.feature +121 -0
  9. data/features/step_defs.rb +3 -3
  10. data/features/support/helpers.rb +3 -5
  11. data/history.json +21 -0
  12. data/lib/ruhoh.rb +28 -123
  13. data/lib/ruhoh/base/collectable.rb +273 -0
  14. data/lib/ruhoh/base/compilable.rb +30 -0
  15. data/lib/ruhoh/base/compilable_asset.rb +30 -0
  16. data/lib/ruhoh/base/model_viewable.rb +30 -0
  17. data/lib/ruhoh/base/modelable.rb +44 -0
  18. data/lib/ruhoh/base/page_like.rb +111 -0
  19. data/lib/ruhoh/base/page_viewable.rb +92 -0
  20. data/lib/ruhoh/base/routable.rb +20 -0
  21. data/lib/ruhoh/base/watchable.rb +18 -0
  22. data/lib/ruhoh/cascade.rb +93 -0
  23. data/lib/ruhoh/client.rb +1 -3
  24. data/lib/ruhoh/collections.rb +2 -1
  25. data/lib/ruhoh/config.rb +67 -0
  26. data/lib/ruhoh/console_methods.rb +0 -2
  27. data/lib/ruhoh/parse.rb +7 -5
  28. data/lib/ruhoh/plugins/initializer.rb +24 -0
  29. data/lib/ruhoh/plugins/local_plugins_plugin.rb +10 -0
  30. data/lib/ruhoh/plugins/plugin.rb +27 -0
  31. data/lib/ruhoh/programs/compile.rb +2 -6
  32. data/lib/ruhoh/programs/preview.rb +5 -2
  33. data/lib/ruhoh/programs/watch.rb +4 -6
  34. data/lib/ruhoh/publish/rsync.rb +2 -2
  35. data/lib/ruhoh/resources/_base/collection.rb +6 -0
  36. data/lib/ruhoh/resources/_base/compiler.rb +3 -0
  37. data/lib/ruhoh/resources/_base/model.rb +3 -0
  38. data/lib/ruhoh/resources/_base/model_view.rb +3 -0
  39. data/lib/ruhoh/resources/_base/watcher.rb +4 -0
  40. data/lib/ruhoh/resources/data/collection.rb +30 -9
  41. data/lib/ruhoh/resources/javascripts/collection_view.rb +5 -1
  42. data/lib/ruhoh/resources/javascripts/model_view.rb +15 -0
  43. data/lib/ruhoh/resources/layouts/client.rb +1 -1
  44. data/lib/ruhoh/resources/pages/client.rb +2 -2
  45. data/lib/ruhoh/resources/pages/collection.rb +2 -21
  46. data/lib/ruhoh/resources/theme/compiler.rb +2 -2
  47. data/lib/ruhoh/resources/widgets/collection.rb +2 -2
  48. data/lib/ruhoh/routes.rb +1 -1
  49. data/lib/ruhoh/summarizer.rb +2 -2
  50. data/lib/ruhoh/ui/dashboard.rb +13 -0
  51. data/lib/ruhoh/ui/page_not_found.rb +3 -2
  52. data/lib/ruhoh/url_slug.rb +23 -9
  53. data/lib/ruhoh/version.rb +1 -1
  54. data/lib/ruhoh/views/master_view.rb +1 -1
  55. data/spec/lib/ruhoh/plugins/initializer_spec.rb +43 -0
  56. data/spec/lib/ruhoh/plugins/plugin_spec.rb +40 -0
  57. data/spec/spec_helper.rb +1 -0
  58. data/system/config.json +21 -0
  59. data/system/{dash/index.html → dashboard.html} +1 -1
  60. data/{lib/ruhoh/ui → system}/page_not_found.html +0 -0
  61. data/system/plugins/sprockets/compiler.rb +1 -0
  62. data/system/widgets/comments/disqus.html +1 -1
  63. metadata +34 -15
  64. data/lib/ruhoh/base/collection.rb +0 -284
  65. data/lib/ruhoh/base/compiler.rb +0 -67
  66. data/lib/ruhoh/base/model.rb +0 -161
  67. data/lib/ruhoh/base/model_view.rb +0 -129
  68. data/lib/ruhoh/base/watcher.rb +0 -25
  69. data/lib/ruhoh/resources/dash/collection.rb +0 -10
  70. data/lib/ruhoh/resources/dash/model.rb +0 -5
  71. data/lib/ruhoh/resources/dash/model_view.rb +0 -5
  72. data/lib/ruhoh/resources/dash/previewer.rb +0 -13
@@ -1,67 +0,0 @@
1
- module Ruhoh::Base
2
-
3
- module Compilable
4
- def self.included(klass)
5
- __send__(:attr_reader, :collection)
6
- end
7
-
8
- def initialize(collection)
9
- @ruhoh = collection.ruhoh
10
- @collection = collection
11
- end
12
-
13
- def setup_compilable
14
- return false unless collection_exists?
15
-
16
- compile_collection_path
17
- end
18
-
19
- def compile_collection_path
20
- FileUtils.mkdir_p(@collection.compiled_path)
21
- end
22
-
23
- def collection_exists?
24
- collection = @collection
25
- unless @collection.paths?
26
- Ruhoh::Friend.say { yellow "#{ collection.resource_name.capitalize }: directory not found - skipping." }
27
- return false
28
- end
29
- Ruhoh::Friend.say { cyan "#{ collection.resource_name.capitalize }: (copying valid files)" }
30
- true
31
- end
32
- end
33
-
34
- module CompilableAsset
35
- include Compilable
36
-
37
- # A basic compiler task which copies each valid collection resource file to the compiled folder.
38
- # This is different from the static compiler in that it supports fingerprinting.
39
- # Valid files are identified by their pointers.
40
- # Invalid files are files that are excluded from the resource's configuration settings.
41
- # The collection's url_endpoint is used to determine the final compiled path.
42
- #
43
- # @returns Nothing.
44
- def run
45
- return unless setup_compilable
46
-
47
- manifest = {}
48
- @collection.files.values.each do |pointer|
49
- digest = Digest::MD5.file(pointer['realpath']).hexdigest
50
- digest_file = pointer['id'].sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" }
51
- manifest[pointer['id']] = digest_file
52
-
53
- compiled_file = File.join(@collection.compiled_path, digest_file)
54
- FileUtils.mkdir_p File.dirname(compiled_file)
55
- FileUtils.cp_r pointer['realpath'], compiled_file
56
- Ruhoh::Friend.say { green " > #{pointer['id']}" }
57
- end
58
-
59
- # Update the paths to the digest format:
60
- @collection.load_collection_view._cache.merge!(manifest)
61
- end
62
- end
63
-
64
- class Compiler
65
- include Ruhoh::Base::Compilable
66
- end
67
- end
@@ -1,161 +0,0 @@
1
- module Ruhoh::Base
2
- module Modelable
3
- include Observable
4
-
5
- def self.included(klass)
6
- klass.__send__(:attr_reader, :pointer, :ruhoh)
7
- end
8
-
9
- def initialize(ruhoh, pointer)
10
- raise "Cannot instantiate a model with a nil pointer" unless pointer
11
- @ruhoh = ruhoh
12
- @pointer = pointer
13
- end
14
-
15
- # @returns[Hash Object] Top page metadata
16
- def data
17
- return @data if @data
18
- process
19
- @data || {}
20
- end
21
-
22
- # @returns[String] Raw (unconverted) page content
23
- def content
24
- return @content if @content
25
- process
26
- @content || ''
27
- end
28
-
29
- def collection
30
- @ruhoh.collection(@pointer['resource'])
31
- end
32
-
33
- # Override this to process custom data
34
- def process
35
- changed
36
- notify_observers(@pointer)
37
- @pointer
38
- end
39
-
40
- def try(method)
41
- return __send__(method) if respond_to?(method)
42
- return data[method.to_s] if data.key?(method.to_s)
43
- false
44
- end
45
- end
46
-
47
- module PageLike
48
- include Modelable
49
-
50
- DateMatcher = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
51
- Matcher = /^(.+\/)*(.*)(\.[^.]+)$/
52
-
53
- # Process this file. See #parse_page_file
54
- # @return[Hash] the processed data from the file.
55
- # ex:
56
- # { "content" => "..", "data" => { "key" => "value" } }
57
- def process
58
- return {} unless file?
59
-
60
- parsed_page = parse_page_file
61
- data = parsed_page['data']
62
-
63
- filename_data = parse_page_filename(@pointer['id'])
64
-
65
- data['pointer'] = @pointer
66
- data['id'] = @pointer['id']
67
-
68
- data['title'] = data['title'] || filename_data['title']
69
- data['date'] ||= filename_data['date']
70
-
71
- # Parse and store date as an object
72
- begin
73
- data['date'] = Time.parse(data['date']) unless data['date'].nil? || data['date'].is_a?(Time)
74
- rescue
75
- Ruhoh.log.error(
76
- "ArgumentError: The date '#{data['date']}' specified in '#{@pointer['id']}' is unparsable."
77
- )
78
- data['date'] = nil
79
- end
80
- data['url'] = url(data)
81
- data['layout'] = collection.config['layout'] if data['layout'].nil?
82
-
83
- parsed_page['data'] = data
84
-
85
- changed
86
- notify_observers(parsed_page)
87
- data
88
- end
89
-
90
- protected
91
-
92
- # Is the resource backed by a physical file in the filesystem?
93
- # For example the pagination system uses a page-stub
94
- # that has no reference to an actual file.
95
- # @return[Boolean]
96
- def file?
97
- !!@pointer['realpath']
98
- end
99
-
100
- # See Ruhoh::Parse.page_file
101
- # @returns[Hash Object] processed top meta-data, raw (unconverted) content body
102
- def parse_page_file
103
- raise "File not found: #{@pointer['realpath']}" unless File.exist?(@pointer['realpath'])
104
- result = Ruhoh::Parse.page_file(@pointer['realpath'])
105
-
106
- # variable cache
107
- @data = result["data"]
108
- @content = result['content']
109
-
110
- result
111
- end
112
-
113
- def parse_page_filename(filename)
114
- data = *filename.match(DateMatcher)
115
- data = *filename.match(Matcher) if data.empty?
116
- return {} if data.empty?
117
-
118
- if filename =~ DateMatcher
119
- {
120
- "path" => data[1],
121
- "date" => data[2],
122
- "slug" => data[3],
123
- "title" => self.to_title(data[3]),
124
- "extension" => data[4]
125
- }
126
- else
127
- {
128
- "path" => data[1],
129
- "slug" => data[2],
130
- "title" => to_title(data[2]),
131
- "extension" => data[3]
132
- }
133
- end
134
- end
135
-
136
- # my-post-title ===> My Post Title
137
- def to_title(file_slug)
138
- if file_slug == 'index' && !@pointer['id'].index('/').nil?
139
- file_slug = @pointer['id'].split('/')[-2]
140
- end
141
-
142
- Ruhoh::StringFormat.titleize(file_slug)
143
- end
144
-
145
- def url(page_data)
146
- page_data['permalink_ext'] ||= collection.config['permalink_ext']
147
-
148
- format = page_data['permalink'] ||
149
- collection.config['permalink'] ||
150
- "/:path/:filename"
151
-
152
- slug = Ruhoh::UrlSlug.new(page_data: page_data, format: format)
153
-
154
- @ruhoh.to_url(slug.generate)
155
- end
156
- end
157
-
158
- class Model
159
- include Modelable
160
- end
161
- end
@@ -1,129 +0,0 @@
1
- require 'ruhoh/summarizer'
2
-
3
- module Ruhoh::Base
4
- module ModelViewable
5
- def initialize(model)
6
- super(model)
7
- @model = model
8
- @ruhoh = model.ruhoh
9
-
10
- # Define direct access to the data Hash object
11
- # but don't overwrite methods if already defined.
12
- data.keys.each do |method|
13
- (class << self; self; end).class_eval do
14
- next if method_defined?(method)
15
- define_method method do |*args, &block|
16
- data[method]
17
- end
18
- end
19
- end
20
- end
21
-
22
- def <=>(other)
23
- id <=> other.id
24
- end
25
-
26
- def [](attribute)
27
- __send__(attribute)
28
- end
29
-
30
- def []=(key, value)
31
- __send__("#{key}=", value)
32
- end
33
- end
34
-
35
- module PageViewable
36
- include ModelViewable
37
-
38
- # Default order by alphabetical title name.
39
- def <=>(other)
40
- sort = @model.collection.config["sort"] || []
41
- attribute = sort[0] || "title"
42
- direction = sort[1] || "asc"
43
-
44
- this_data = __send__(attribute)
45
- other_data = other.__send__(attribute)
46
- if attribute == "date"
47
- if this_data.nil? || other_data.nil?
48
- Ruhoh.log.error(
49
- "ArgumentError:" +
50
- " The '#{ @model.collection.resource_name }' collection is configured to sort based on 'date'" +
51
- " but '#{ @model.pointer['id'] }' has no parseable date in its metadata." +
52
- " Add date: 'YYYY-MM-DD' to its YAML metadata."
53
- )
54
- end
55
- direction = sort[1] || "desc" #default should be reverse
56
- end
57
-
58
- if direction == "asc"
59
- this_data <=> other_data
60
- else
61
- other_data <=> this_data
62
- end
63
- end
64
-
65
- def categories
66
- @model.collection.to_categories(data['categories'])
67
- end
68
-
69
- def tags
70
- @model.collection.to_tags(data['tags'])
71
- end
72
-
73
- # Lazy-load the page body.
74
- # Notes:
75
- # @content is not used for caching, it's used to manually
76
- # define content for a given page. Useful in the case that
77
- # you want to model a resource that does not actually
78
- # reference a file.
79
- def content
80
- return @content if @content
81
- content = @model.collection.master.render(@model.content)
82
- Ruhoh::Converter.convert(content, id)
83
- end
84
-
85
- def is_active_page
86
- id == @model.collection.master.page_data['id']
87
- end
88
-
89
- def summary
90
- model_data = @model.data
91
- collection_config = @model.collection.config
92
-
93
- line_limit = model_data['summary_lines'] ||
94
- collection_config['summary_lines']
95
- stop_at_header = model_data['summary_stop_at_header'] ||
96
- collection_config['summary_stop_at_header']
97
-
98
- Ruhoh::Summarizer.new({
99
- content: @ruhoh.master_view(@model.pointer).render_content,
100
- line_limit: line_limit,
101
- stop_at_header: stop_at_header
102
- }).generate
103
- end
104
-
105
- def next
106
- return unless id
107
- all_cache = @model.collection.all
108
- index = all_cache.index {|p| p["id"] == id}
109
- return unless index && (index-1 >= 0)
110
- _next = all_cache[index-1]
111
- return unless _next
112
- _next
113
- end
114
-
115
- def previous
116
- return unless id
117
- all_cache = @model.collection.all
118
- index = all_cache.index {|p| p["id"] == id}
119
- return unless index && (index+1 >= 0)
120
- prev = all_cache[index+1]
121
- return unless prev
122
- prev
123
- end
124
- end
125
-
126
- class ModelView < SimpleDelegator
127
- include ModelViewable
128
- end
129
- end
@@ -1,25 +0,0 @@
1
- module Ruhoh::Base
2
- module Watchable
3
- def self.included(klass)
4
- klass.__send__(:attr_accessor, :collection)
5
- end
6
-
7
- def initialize(collection)
8
- @collection = collection
9
- end
10
-
11
- def update(path)
12
- # Drop the resource namespace
13
- matcher = File::ALT_SEPARATOR ?
14
- %r{^.+(#{ File::SEPARATOR }|#{ File::ALT_SEPARATOR })} :
15
- %r{^.+#{ File::SEPARATOR }}
16
-
17
- collection.touch(path.gsub(matcher, ''))
18
- end
19
- end
20
-
21
- # Base watcher class that loads if no custom Watcher class is defined.
22
- class Watcher
23
- include Watchable
24
- end
25
- end
@@ -1,10 +0,0 @@
1
- module Ruhoh::Resources::Dash
2
- class Collection
3
- include Ruhoh::Base::Collectable
4
-
5
- def url_endpoint
6
- "/dash"
7
- end
8
-
9
- end
10
- end
@@ -1,5 +0,0 @@
1
- module Ruhoh::Resources::Dash
2
- class Model
3
- include Ruhoh::Base::PageLike
4
- end
5
- end
@@ -1,5 +0,0 @@
1
- module Ruhoh::Resources::Dash
2
- class ModelView < SimpleDelegator
3
- include Ruhoh::Base::PageViewable
4
- end
5
- end
@@ -1,13 +0,0 @@
1
- module Ruhoh::Resources::Dash
2
- class Previewer
3
- def initialize(ruhoh)
4
- @ruhoh = ruhoh
5
- end
6
-
7
- def call(env)
8
- pointer = @ruhoh.collection("dash").find_file('index')
9
- view = @ruhoh.master_view(pointer)
10
- [200, {'Content-Type' => 'text/html'}, [view.render_full]]
11
- end
12
- end
13
- end