ruhoh 2.5 → 2.6

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.
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
@@ -40,8 +40,6 @@ class Ruhoh
40
40
  return server if %w(s serve server).include?(cmd)
41
41
 
42
42
  @ruhoh = Ruhoh.new
43
- @ruhoh.setup
44
- @ruhoh.setup_paths
45
43
  @ruhoh.setup_plugins
46
44
 
47
45
  return __send__(cmd) if respond_to?(cmd)
@@ -136,7 +134,7 @@ class Ruhoh
136
134
  end
137
135
 
138
136
  if Ruhoh::Publish.const_defined?(service.to_sym)
139
- publish_config = Ruhoh::Parse.data_file(@ruhoh.base, "publish") || {}
137
+ publish_config = Ruhoh::Parse.data_file(@ruhoh.cascade.base, "publish") || {}
140
138
  Ruhoh::Publish.const_get(service.to_sym).new.run(@args, publish_config[service.downcase])
141
139
  else
142
140
  Ruhoh::Friend.say {
@@ -1,3 +1,4 @@
1
+ module Ruhoh::Base ; end
1
2
  module Ruhoh::Resources ; end
2
3
  # Require all the resources
3
4
  FileUtils.cd(File.join(File.dirname(__FILE__), 'base')) do
@@ -71,7 +72,7 @@ class Ruhoh
71
72
  def discover
72
73
  results = Set.new
73
74
 
74
- @ruhoh.cascade.each do |h|
75
+ @ruhoh.cascade.paths.each do |h|
75
76
  FileUtils.cd(h["path"]) do
76
77
  results += Dir['*'].select { |x|
77
78
  File.directory?(x) && !["plugins", 'compiled'].include?(x)
@@ -0,0 +1,67 @@
1
+ class Ruhoh
2
+ class Config < SimpleDelegator
3
+ include Observable
4
+
5
+ def initialize(ruhoh)
6
+ @ruhoh = ruhoh
7
+ @data = {}
8
+ super(@data)
9
+ end
10
+
11
+ # Regenerate the config data
12
+ def touch
13
+ data = @ruhoh.cascade.merge_data_file('config') || {}
14
+ data = Ruhoh::Utils.deep_merge(data, collections_config)
15
+ data = Ruhoh::Utils.deep_merge(data, find_theme_path)
16
+
17
+ @data.clear
18
+ @data.merge!(data)
19
+
20
+ Time.default_format = @data['date_format']
21
+ @data["compiled_path"] = File.expand_path(@data["compiled_path"])
22
+
23
+ changed
24
+ notify_observers(@data)
25
+
26
+ self
27
+ end
28
+
29
+ def base_path
30
+ return '/' unless (@ruhoh.env == 'production')
31
+
32
+ @data['base_path'] += "/" unless @data['base_path'][-1] == '/'
33
+ string = @data['base_path'].chomp('/').reverse.chomp('/').reverse
34
+ return '/' if string.empty? || string == '/'
35
+ "/#{ string }/"
36
+ end
37
+
38
+ private
39
+
40
+ def find_theme_path
41
+ theme_name = @data.find { |resource, data| data.is_a?(Hash) && data['use'] == "theme" }
42
+ if theme_name
43
+ Ruhoh::Friend.say { plain "Using theme: \"#{theme_name[0]}\""}
44
+ { "_theme_collection" => theme_name[0] }
45
+ else
46
+ { "_theme_collection" => nil }
47
+ end
48
+ end
49
+
50
+ # Quick and dirty way to scan for config files in collection folders.
51
+ # This is needed because we don't know which collection defines itself as a theme
52
+ # so we'll scan for any configs and merge the data to find the theme folder.
53
+ def collections_config
54
+ data = {}
55
+ @ruhoh.cascade.paths.map{ |a| a['path'] }.each do |path|
56
+ FileUtils.cd(path) {
57
+ Dir["*/config.*"].each { |id|
58
+ next unless File.exist?(id) && FileTest.file?(id)
59
+ data = Ruhoh::Utils.deep_merge(data, (Ruhoh::Parse.data_file(File.realpath(id)) || {}))
60
+ }
61
+ }
62
+ end
63
+
64
+ data
65
+ end
66
+ end
67
+ end
@@ -7,9 +7,7 @@ class Ruhoh
7
7
  def ruhoh
8
8
  return @ruhoh if @ruhoh
9
9
  @ruhoh = Ruhoh.new
10
- @ruhoh.setup
11
10
  @ruhoh.env = ConsoleMethods.env || 'development'
12
- @ruhoh.setup_paths
13
11
  @ruhoh
14
12
  end
15
13
 
@@ -42,13 +42,15 @@ class Ruhoh
42
42
  end
43
43
 
44
44
  def self.data_file(*args)
45
- base = File.__send__(:join, args)
45
+ filepath = File.__send__(:join, args)
46
+ if File.extname(filepath).to_s.empty?
47
+ path = nil
48
+ ["#{ filepath }.json", "#{ filepath }.yml", "#{ filepath }.yaml"].each do |result|
49
+ filepath = path = result and break if File.exist?(result)
50
+ end
46
51
 
47
- filepath = nil
48
- ["#{ base }.json", "#{ base }.yml", "#{ base }.yaml"].each do |result|
49
- filepath = result and break if File.exist?(result)
52
+ return nil unless path
50
53
  end
51
- return nil unless filepath
52
54
 
53
55
  file = File.open(filepath, 'r:UTF-8') { |f| f.read }
54
56
 
@@ -0,0 +1,24 @@
1
+ module Ruhoh::Plugins
2
+ class Initializer
3
+ attr_reader :name
4
+
5
+ def initialize name, &block
6
+ raise ArgumentError, "block required for initializer '#{name}'" unless block_given?
7
+ @name, @block = name, block
8
+ end
9
+
10
+ def run *args
11
+ raise "Initializer '#{name}' need to be bound" unless context
12
+ context.instance_exec *args, &block
13
+ end
14
+
15
+ def bind ctx
16
+ @context = ctx
17
+ self
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :block, :context
23
+ end
24
+ end
@@ -0,0 +1,10 @@
1
+ module Ruhoh::Plugins
2
+ class LocalPluginsPlugin
3
+ include Plugin
4
+
5
+ initializer 'ruhoh.local_plugins' do
6
+ plugins = Dir[File.join(@base, "plugins", "**/*.rb")]
7
+ plugins.each { |f| require f }
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,27 @@
1
+ require 'ruhoh/plugins/initializer'
2
+
3
+ module Ruhoh::Plugins
4
+ module Plugin
5
+ def self.included base
6
+ base.send :extend, ClassMethods
7
+ end
8
+
9
+ def self.run_all(context, *args)
10
+ initializers.each do |i|
11
+ i.bind(context).run *args
12
+ end
13
+ end
14
+
15
+ protected
16
+
17
+ def self.initializers
18
+ @initializers ||= []
19
+ end
20
+
21
+ module ClassMethods
22
+ def initializer name, &block
23
+ Plugin.initializers << Initializer.new(name, &block)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -5,15 +5,11 @@ class Ruhoh
5
5
  # to properly omit drafts and other development-only settings.
6
6
  def self.compile(target=nil)
7
7
  ruhoh = Ruhoh.new
8
- ruhoh.setup
9
8
  ruhoh.env = 'production'
10
- ruhoh.setup_paths
11
9
  ruhoh.setup_plugins
12
-
10
+
13
11
  if target
14
- ruhoh.paths.compiled = File.expand_path(target)
15
- elsif ruhoh.config["compiled"]
16
- ruhoh.paths.compiled = ruhoh.config["compiled"]
12
+ ruhoh.config['compiled_path'] = File.expand_path(target)
17
13
  end
18
14
 
19
15
  ruhoh.compile
@@ -1,4 +1,5 @@
1
1
  require 'ruhoh/programs/watch'
2
+ require 'ruhoh/ui/dashboard'
2
3
  class Ruhoh
3
4
  module Program
4
5
  # Public: A program for running ruhoh as a rack application
@@ -17,9 +18,7 @@ class Ruhoh
17
18
  opts[:env] ||= 'development'
18
19
 
19
20
  ruhoh = Ruhoh.new
20
- ruhoh.setup
21
21
  ruhoh.env = opts[:env]
22
- ruhoh.setup_paths
23
22
  ruhoh.setup_plugins unless opts[:enable_plugins] == false
24
23
 
25
24
  # initialize the routes dictionary for all page resources.
@@ -52,6 +51,10 @@ class Ruhoh
52
51
  end
53
52
  end
54
53
 
54
+ map '/dash' do
55
+ run Ruhoh::UI::Dashboard.new(ruhoh)
56
+ end
57
+
55
58
  # The generic Page::Previewer is used to render any/all page-like resources,
56
59
  # since they likely have arbitrary urls based on permalink settings.
57
60
  map '/' do
@@ -7,12 +7,10 @@ class Ruhoh
7
7
  # The observer triggers data regeneration as files change
8
8
  # in order to keep the data up to date in real time.
9
9
  def self.watch(ruhoh)
10
- ruhoh.ensure_setup
11
-
12
10
  Ruhoh::Friend.say {
13
- cyan "=> Start watching: #{ruhoh.paths.base}"
11
+ cyan "=> Start watching: #{ruhoh.cascade.base}"
14
12
  }
15
- dw = DirectoryWatcher.new(ruhoh.paths.base, {
13
+ dw = DirectoryWatcher.new(ruhoh.cascade.base, {
16
14
  :glob => "**/*",
17
15
  :pre_load => true
18
16
  })
@@ -21,14 +19,14 @@ class Ruhoh
21
19
  args.each do |event|
22
20
  ruhoh.cache.delete(event['path'])
23
21
 
24
- path = event['path'].gsub(ruhoh.paths.base + '/', '')
22
+ path = event['path'].gsub(ruhoh.cascade.base + '/', '')
25
23
 
26
24
  Ruhoh::Friend.say {
27
25
  yellow "Watch [#{Time.now.strftime("%H:%M:%S")}] [Update #{path}] : #{args.size} files changed"
28
26
  }
29
27
 
30
28
  if %w{ config.json config.yml config.yaml }.include?(path)
31
- ruhoh.config true
29
+ ruhoh.config.touch
32
30
  else
33
31
  separator = File::ALT_SEPARATOR ?
34
32
  %r{#{ File::SEPARATOR }|#{ File::ALT_SEPARATOR }} :
@@ -22,9 +22,9 @@ class Ruhoh
22
22
  if @config["command"]
23
23
  system(@config["command"])
24
24
  else
25
- system('rsync', File.join(ruhoh.paths.compiled, '.'), '-avz', '--delete', '--exclude', '.git', remote)
25
+ system('rsync', File.join(ruhoh.config['compiled_path'], '.'), '-avz', '--delete', '--exclude', '.git', remote)
26
26
  end
27
- FileUtils.rm_r(ruhoh.paths.compiled)
27
+ FileUtils.rm_r(ruhoh.config['compiled_path'])
28
28
  end
29
29
 
30
30
  private
@@ -0,0 +1,6 @@
1
+ # Generic base implementation of a Collection class.
2
+ # All collections use this class by default
3
+ # unless the Collection class is explicitly defined for the collection.
4
+ class Ruhoh::Base::Collection
5
+ include Ruhoh::Base::Collectable
6
+ end
@@ -0,0 +1,3 @@
1
+ class Ruhoh::Base::Compiler
2
+ include Ruhoh::Base::Compilable
3
+ end
@@ -0,0 +1,3 @@
1
+ class Ruhoh::Base::Model
2
+ include Ruhoh::Base::Modelable
3
+ end
@@ -0,0 +1,3 @@
1
+ class Ruhoh::Base::ModelView < SimpleDelegator
2
+ include Ruhoh::Base::ModelViewable
3
+ end
@@ -0,0 +1,4 @@
1
+ # Base watcher class that loads if no custom Watcher class is defined.
2
+ class Ruhoh::Base::Watcher
3
+ include Ruhoh::Base::Watchable
4
+ end
@@ -2,24 +2,45 @@ module Ruhoh::Resources::Data
2
2
  class Collection
3
3
  include Ruhoh::Base::Collectable
4
4
 
5
+ def glob
6
+ "*"
7
+ end
8
+
9
+ def dictionary
10
+ resource_name == "data" ?
11
+ _support_legacy_api :
12
+ _support_new_data_api
13
+ end
14
+
15
+ private
16
+
17
+ def _support_new_data_api
18
+ data = {}
19
+ files.values.each do |pointer|
20
+ name = File.basename(pointer["id"], File.extname(pointer["id"]))
21
+ data[name] = Ruhoh::Parse.data_file(pointer["realpath"]) || {}
22
+ end
23
+
24
+ data
25
+ end
26
+
5
27
  # TODO: This is ugly but it works.
6
28
  # Should handle data extensions in the cascade more elegantly
7
- def dictionary
8
- found_path_prefix = nil
29
+ def _support_legacy_api
30
+ found_paths = []
9
31
 
10
- @ruhoh.cascade.reverse.map do |h|
32
+ @ruhoh.cascade.paths.each do |h|
11
33
  path_prefix = File.join(h["path"], resource_name)
12
34
 
13
35
  ["#{ path_prefix }.json", "#{ path_prefix }.yml", "#{ path_prefix }.yaml"].each do |file|
14
- found_path_prefix = path_prefix and break if File.exist?(file)
36
+ found_paths << path_prefix and break if File.exist?(file)
15
37
  end
16
-
17
- break if found_path_prefix
18
38
  end
19
39
 
20
- return {} unless found_path_prefix
40
+ data = {}
41
+ found_paths.each { |path| data.merge!(Ruhoh::Parse.data_file(path) || {}) }
21
42
 
22
- Ruhoh::Parse.data_file(found_path_prefix) || {}
43
+ data
23
44
  end
24
45
  end
25
- end
46
+ end
@@ -27,7 +27,11 @@ module Ruhoh::Resources::Javascripts
27
27
  }.join("\n")
28
28
  end
29
29
 
30
- protected
30
+ def all()
31
+ files.values.map { |pointer|
32
+ load_model_view(pointer)
33
+ }
34
+ end
31
35
 
32
36
  def make_url(name)
33
37
  return name if name =~ /^(http:|https:)?\/\//i
@@ -0,0 +1,15 @@
1
+ module Ruhoh::Resources::Javascripts
2
+ class ModelView < SimpleDelegator
3
+ def url()
4
+ self.collection.make_url(self.pointer['id'])
5
+ end
6
+
7
+ def id()
8
+ self.pointer['id']
9
+ end
10
+
11
+ def path()
12
+ self.pointer['realpath']
13
+ end
14
+ end
15
+ end
@@ -24,7 +24,7 @@ module Ruhoh::Resources::Layouts
24
24
  exit
25
25
  } if name.nil?
26
26
 
27
- filename = File.join((@ruhoh.paths.theme || @ruhoh.paths.base), "layouts", name.gsub(/\s/, '-').downcase) + ".html"
27
+ filename = File.join((@ruhoh.cascade.theme || @ruhoh.cascade.base), "layouts", name.gsub(/\s/, '-').downcase) + ".html"
28
28
 
29
29
  if File.exist?(filename)
30
30
  abort("Create new layout: aborted!") if ask("#{filename} already exists. Do you want to overwrite?", ['y', 'n']) == 'n'
@@ -105,8 +105,8 @@ module Ruhoh::Resources::Pages
105
105
 
106
106
  name = "#{name}-#{@iterator}" unless @iterator.zero?
107
107
  filename = opts[:draft] ?
108
- File.join(@ruhoh.paths.base, @collection.resource_name, "drafts", "#{name}#{ext}") :
109
- File.join(@ruhoh.paths.base, @collection.resource_name, "#{name}#{ext}")
108
+ File.join(@ruhoh.cascade.base, @collection.resource_name, "drafts", "#{name}#{ext}") :
109
+ File.join(@ruhoh.cascade.base, @collection.resource_name, "#{name}#{ext}")
110
110
  @iterator += 1
111
111
  end while File.exist?(filename)
112
112
 
@@ -1,27 +1,8 @@
1
+ require 'ruhoh/base/routable'
1
2
  module Ruhoh::Resources::Pages
2
- module Routable
3
- def routes
4
- return @routes if @routes
5
- @routes = {}
6
- dictionary
7
- @routes
8
- end
9
-
10
- def routes_add(route, pointer)
11
- @routes ||= {}
12
- @routes[route] = pointer
13
- end
14
-
15
- def routes_delete(pointer)
16
- return unless @routes
17
- route = @routes.find{ |k, v| v == pointer }
18
- @routes.delete(route[0]) if route
19
- end
20
- end
21
-
22
3
  class Collection
23
4
  include Ruhoh::Base::Collectable
24
- include Routable
5
+ include Ruhoh::Base::Routable
25
6
 
26
7
  # model observer callback.
27
8
  def update(model_data)