gumdrop 0.5.2 → 0.6.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.
data/ChangeLog.md CHANGED
@@ -1,3 +1,6 @@
1
+ # v0.6
2
+ - Extracted Gumdrop::Build into Gumdrop::Site. Removed static Gumdrop#site.
3
+
1
4
  # v0.5.2
2
5
  - DeferredLoader changed to DataManager
3
6
  - Added YamlDoc support to data collections -- a data format (.yamldoc) that strips YAML front matter and puts the content under the key 'content', or it will use a custom key from the front matter if the value of the pair is '_YAMLDOC_'
data/License CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (C) 2011 by Matt McCray
1
+ Copyright (C) 2011-12 by Matt McCray
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
data/Notes.md CHANGED
@@ -6,14 +6,22 @@
6
6
  - What would happen with conflicts, last one in wins?
7
7
  - Multiple data_dir too?
8
8
  - Refactor code to not use Gumdrop as a singleton (static really)
9
- - Add YamlDoc support for nodes?
9
+ - Add YamlDoc support for nodes? (Tilt compiler? or in Content)
10
10
 
11
11
 
12
+ - `Gumdrop.mode` :build, :serve (other?)
13
+ - `Gumdrop.env` (specified via -e on cli, default: production)
14
+ - Easy access to ENV in RenderingContext (MODE as well)
15
+
16
+ - configure block for each env/mode?
17
+
12
18
  # TODO:
13
19
  - New/Update Doc Site
14
20
  - API for retrieving pages and pages under a path (simple query)
15
21
  - Need test coverage.
16
22
 
23
+ - Extract Build class into a Site class that can be instansiated (so multiple site can be loaded/run in memory)
24
+
17
25
 
18
26
  # Possible New Internals
19
27
  - Gumdrop (module)
data/gumdrop.gemspec CHANGED
@@ -13,9 +13,9 @@ Gem::Specification.new do |s|
13
13
 
14
14
  s.authors = ["Matt McCray"]
15
15
  s.email = %q{matt@elucidata.net}
16
- s.summary = %q{A simple cms/prototyping tool.}
16
+ s.summary = %q{A sweet 'n simple cms/prototyping tool.}
17
17
  s.homepage = %q{https://github.com/darthapo/gumdrop}
18
- s.description = %q{A simple cms/prototyping tool.}
18
+ s.description = %q{A sweet 'n simple cms/prototyping tool for creating static html websites and webapps.}
19
19
 
20
20
  s.files = `git ls-files`.split("\n")
21
21
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
@@ -26,8 +26,8 @@ Gem::Specification.new do |s|
26
26
  s.add_dependency 'tilt'
27
27
  s.add_dependency 'active_support'
28
28
  s.add_dependency 'trollop'
29
- s.add_dependency 'haml'
30
- s.add_dependency 'sass'
29
+ # s.add_dependency 'haml'
30
+ # s.add_dependency 'sass'
31
31
  s.add_dependency 'i18n'
32
32
  s.add_dependency 'bundle'
33
33
 
data/lib/gumdrop/cli.rb CHANGED
@@ -49,6 +49,7 @@ EOS
49
49
 
50
50
  end
51
51
 
52
+ # NO COMMANDS GIVEN
52
53
 
53
54
  unless opts[:create_given] or opts[:build_given] or opts[:server_given] or opts[:new_given] or opts[:template_given] or opts[:list_given]
54
55
  puts banner_text
@@ -56,19 +57,22 @@ unless opts[:create_given] or opts[:build_given] or opts[:server_given] or opts[
56
57
  end
57
58
 
58
59
 
60
+ # BUILD
59
61
 
60
62
  if opts[:build_given]
61
63
  puts banner_text unless opts[:quiet_given]
62
64
  Gumdrop.run(opts)
63
65
 
64
66
 
67
+ # SERVER
68
+
65
69
  elsif opts[:server_given]
66
70
  puts banner_text unless opts[:quiet_given]
67
- Gumdrop.config.auto_run= true
68
- Gumdrop.config.force_reload= opts[:reload_given]
69
71
  Gumdrop::Server
70
72
 
71
73
 
74
+ # LIST TEMPLATES
75
+
72
76
  elsif opts[:list_given]
73
77
  # List templates
74
78
  puts banner_text unless opts[:quiet_given]
@@ -85,11 +89,15 @@ elsif opts[:list_given]
85
89
  end
86
90
 
87
91
 
92
+ # NEW DATA COLLECTION ITEM
93
+
88
94
  elsif opts[:new_given]
89
95
  puts banner_text unless opts[:quiet_given]
90
96
  puts "Not implemented yet..."
91
97
 
92
98
 
99
+ # CREATE NEW SITE
100
+
93
101
  elsif opts[:create_given]
94
102
  puts banner_text unless opts[:quiet_given]
95
103
 
@@ -127,6 +135,8 @@ elsif opts[:create_given]
127
135
  end
128
136
 
129
137
 
138
+ # SAVE CURRENT SITE AS TEMPLATE
139
+
130
140
  elsif opts[:template_given]
131
141
  # Save as template...
132
142
  puts banner_text unless opts[:quiet_given]
@@ -145,7 +155,7 @@ elsif opts[:template_given]
145
155
  end
146
156
 
147
157
 
148
-
158
+ # UNKNOWN COMMAND
149
159
 
150
160
  else
151
161
  puts banner_text
@@ -3,13 +3,15 @@ module Gumdrop
3
3
 
4
4
  class Content
5
5
 
6
- attr_accessor :path, :level, :filename, :source_filename, :type, :ext, :uri, :slug, :template, :params
6
+ attr_accessor :path, :level, :filename, :source_filename, :type, :ext, :uri, :slug, :template, :params, :site, :ignored
7
7
 
8
- def initialize(path, params={})
8
+ def initialize(path, site, params={})
9
+ @site= site
9
10
  @params= HashObject.new params
10
11
  @path= path
11
12
  @level= (@path.split('/').length - 2)
12
13
  @source_filename= File.basename path
14
+ @ignored= false
13
15
 
14
16
  filename_parts= @source_filename.split('.')
15
17
  ext= filename_parts.pop
@@ -35,27 +37,27 @@ module Gumdrop
35
37
  end
36
38
  end
37
39
 
38
- def render(ignore_layout=false, reset_context=true, locals={})
40
+ def render(context=nil, ignore_layout=false, reset_context=true, locals={})
41
+ context= @site.render_context if context.nil?
39
42
  if reset_context
40
-
41
43
  default_layout= (@ext == '.css' or @ext == '.js' or @ext == '.xml') ? nil : 'site'
42
- Context.reset_data 'current_depth'=>@level, 'current_slug'=>@slug, 'page'=>self, 'layout'=>default_layout, 'params'=>self.params
44
+ context.reset_data 'current_depth'=>@level, 'current_slug'=>@slug, 'page'=>self, 'layout'=>default_layout, 'params'=>self.params
43
45
  end
44
- Context.set_content self, locals
45
- content= @template.render(Context)
46
+ context.set_content self, locals
47
+ content= @template.render(context)
46
48
  return content if ignore_layout
47
- layout= Context.get_template()
49
+ layout= context.get_template()
48
50
  while !layout.nil?
49
- content = layout.template.render(Context, content:content) { content }
50
- layout= Context.get_template()
51
+ content = layout.template.render(context, content:content) { content }
52
+ layout= context.get_template()
51
53
  end
52
54
  content
53
55
  end
54
56
 
55
- def renderTo(output_path, filters=[], opts={})
57
+ def renderTo(context, output_path, filters=[], opts={})
56
58
  return copyTo(output_path, opts) unless useLayout?
57
- Gumdrop.report " Rendering: #{@uri}", :warning
58
- output= render()
59
+ @site.report " Rendering: #{@uri}", :warning
60
+ output= render(context)
59
61
  filters.each {|f| output= f.call(output, self) }
60
62
  File.open output_path, 'w' do |f|
61
63
  f.write output
@@ -70,10 +72,10 @@ module Gumdrop
70
72
  true
71
73
  end
72
74
  if do_copy
73
- Gumdrop.report " Copying: #{@uri}", :warning
75
+ @site.report " Copying: #{@uri}", :warning
74
76
  FileUtils.cp_r @path, output, opts
75
77
  else
76
- Gumdrop.report " (same): #{@uri}", :info
78
+ @site.report " (same): #{@uri}", :info
77
79
  end
78
80
  end
79
81
 
@@ -1,130 +1,135 @@
1
1
 
2
2
  module Gumdrop
3
3
 
4
- module Context
5
- class << self
6
-
7
- include ::Gumdrop::ViewHelpers
8
-
9
- attr_accessor :state
10
- #attr_reader :data
11
-
12
- def uri(path, opts={})
13
- path= path[1..-1] if path.starts_with?('/') # and path != "/"
14
- uri_string= if !Gumdrop.config.relative_paths or Context.force_absolute
15
- "/#{path}"
16
- else
17
- "#{'../'*@state['current_depth']}#{path}"
18
- end
19
- if opts[:fresh] and Gumdrop.site.has_key?(path)
20
- uri_string += "?v=#{ Gumdrop.site[path].mtime.to_i }"
21
- end
22
- uri_string = "/" if uri_string == ""
23
- uri_string
24
- end
25
-
26
- def url(path)
27
- path= path[1..-1] if path.starts_with?('/')
28
- "#{Gumdrop.config.site_url}/#{path}"
29
- end
4
+ class Context
5
+ include ::Gumdrop::ViewHelpers
30
6
 
31
- def slug
32
- @state['current_slug']
33
- end
34
-
35
- def get_template
36
- layout= @state['layout']
37
- @state['layout']= nil
38
- unless layout.nil?
39
- Gumdrop.layouts["#{layout}.template"]
40
- else
41
- nil
42
- end
7
+ attr_accessor :state
8
+
9
+ def initialize(site)
10
+ @site= site
11
+ # puts "NEW CONTEXT!!!"
12
+ end
13
+
14
+ def uri(path, opts={})
15
+ path= path[1..-1] if path.starts_with?('/') # and path != "/"
16
+ uri_string= if !@site.config.relative_paths or force_absolute
17
+ "/#{path}"
18
+ else
19
+ "#{'../'*@state['current_depth']}#{path}"
43
20
  end
44
-
45
- def use_template(name)
46
- @state['layout']= name
21
+ if opts[:fresh] and @site.node_tree.has_key?(path)
22
+ uri_string += "?v=#{ @site.node_tree[path].mtime.to_i }"
47
23
  end
48
-
49
- def render(path, opts={})
50
- page= get_page path
51
- unless page.nil?
52
- #TODO: nested state for an inline rendered page?
53
- old_layout= @state['layout']
54
- content= page.render(true, false, opts)
55
- old_layout= @state['layout']
56
- content
57
- else
58
- ""
59
- end
24
+ uri_string = "/" if uri_string == ""
25
+ uri_string
26
+ end
27
+
28
+ def url(path)
29
+ path= path[1..-1] if path.starts_with?('/')
30
+ "#{@site.config.site_url}/#{path}"
31
+ end
32
+
33
+ def slug
34
+ @state['current_slug']
35
+ end
36
+
37
+ def get_template
38
+ layout= @state['layout']
39
+ @state['layout']= nil
40
+ unless layout.nil?
41
+ @site.layouts["#{layout}.template"]
42
+ else
43
+ nil
60
44
  end
61
-
62
- def data
63
- Gumdrop.data
45
+ end
46
+
47
+ def use_template(name)
48
+ @state['layout']= name
49
+ end
50
+ alias_method :use_layout, :use_template
51
+
52
+ def render(path, opts={})
53
+ page= get_page path
54
+ unless page.nil?
55
+ #TODO: nested state for an inline rendered page?
56
+ old_layout= @state['layout']
57
+ content= page.render(nil, true, false, opts)
58
+ old_layout= @state['layout']
59
+ content
60
+ else
61
+ ""
64
62
  end
63
+ end
64
+
65
+ def data
66
+ @site.data
67
+ end
65
68
 
66
- # Access to settings from configure block
67
- def config
68
- Gumdrop.config
69
+ def site
70
+ @site
71
+ end
72
+
73
+ # Access to settings from configure block
74
+ def config
75
+ @site.config
76
+ end
77
+
78
+ def reset_data(preset={})
79
+ # TODO: Add a setting for reloading data on every request/page
80
+ # was this for the server?
81
+ # @site.data.reset if !Gumdrop.config.cache_data
82
+ @state = preset
83
+ end
84
+
85
+ def method_missing(name, value=nil)
86
+ @state= Hash.new {|h,k| h[k]= nil } if @state.nil?
87
+ # puts "Looking for >> #{name} in #{@state.keys}"
88
+ unless value.nil?
89
+ @state[name.to_s]= value
90
+ else
91
+ @state[name.to_s]
69
92
  end
93
+ end
70
94
 
71
- def reset_data(preset={})
72
- # TODO: Add a setting for reloading data on every request/page
73
- # was this for the server?
74
- Gumdrop.data.reset if !Gumdrop.config.cache_data
75
- @state = preset
76
- end
77
-
78
- def method_missing(name, value=nil)
79
- @state= Hash.new {|h,k| h[k]= nil } if @state.nil?
80
- # puts "Looking for >> #{name} in #{@state.keys}"
81
- unless value.nil?
82
- @state[name.to_s]= value
95
+ def params
96
+ @content.params
97
+ end
98
+
99
+ def set_content(content, locals)
100
+ @content= content
101
+ @state= @state.reverse_merge(content.params).merge(locals)
102
+ end
103
+
104
+ def content_for(key, &block)
105
+ keyname= "_content_#{key}"
106
+ if block_given?
107
+ @state[keyname]= block
108
+ nil
109
+ else
110
+ if @state.has_key?(keyname)
111
+ @state[keyname].call
83
112
  else
84
- @state[name.to_s]
85
- end
86
- end
87
-
88
- def params
89
- @content.params
90
- end
91
-
92
- def set_content(content, locals)
93
- @content= content
94
- @state= @state.reverse_merge(content.params).merge(locals)
95
- end
96
-
97
- def content_for(key, &block)
98
- keyname= "_content_#{key}"
99
- if block_given?
100
- @state[keyname]= block
101
113
  nil
102
- else
103
- if @state.has_key?(keyname)
104
- @state[keyname].call
105
- else
106
- nil
107
- end
108
114
  end
109
115
  end
110
-
111
- def content_for?(key)
112
- keyname= "_content_#{key}"
113
- @state.has_key?(keyname)
114
- end
115
-
116
- protected
117
-
118
- def get_page(path)
119
- page= Gumdrop.site[path]
120
- page= Gumdrop.site["#{path}.html"] if page.nil? # Bit of a hack...
121
- page= Gumdrop.partials[path] if page.nil?
122
- page= Gumdrop.layouts[path] if page.nil? # ???
123
- page
124
- end
125
-
126
116
  end
117
+
118
+ def content_for?(key)
119
+ keyname= "_content_#{key}"
120
+ @state.has_key?(keyname)
121
+ end
122
+
123
+ protected
124
+
125
+ def get_page(path)
126
+ page= @site.node_tree[path]
127
+ page= @site.node_tree["#{path}.html"] if page.nil? # Bit of a hack...
128
+ page= @site.partials[path] if page.nil?
129
+ page= @site.layouts[path] if page.nil? # ???
130
+ page
131
+ end
132
+
127
133
  end
128
134
 
129
-
130
135
  end
@@ -1,78 +1,42 @@
1
1
  require 'yaml'
2
2
  require 'ostruct'
3
3
 
4
- def hashes2ostruct(object)
5
- return case object
6
- when Hash
7
- object = object.clone
8
- object.each do |key, value|
9
- object[key] = hashes2ostruct(value)
10
- end
11
- OpenStruct.new(object)
12
- when Array
13
- object = object.clone
14
- object.map! { |i| hashes2ostruct(i) }
15
- else
16
- object
17
- end
18
- end
19
-
20
4
  module Gumdrop
21
-
5
+
6
+ # Supported Data File Types:
7
+ DATA_DOC_EXTS= %w(json yml yaml ymldb yamldb yamldoc ymldoc)
8
+
22
9
  class DataManager
23
10
  attr_reader :cache
24
11
 
25
- def initialize(data_dir="data")
26
- @dir= data_dir
12
+ def initialize(site, data_dir="./data")
13
+ @site= site
14
+ @dir= File.expand_path data_dir
27
15
  @cache= {}
28
- @persisted= {}
29
16
  end
30
17
 
31
18
  def method_missing(key, value=nil)
32
- cache_or_load_data(key)
19
+ cache_dataset(key)
33
20
  @cache[key]
34
21
  end
35
22
 
36
- def set(key, value, opts={})
23
+ def set(key, value)
37
24
  @cache[key]= value
38
- @persisted[key]= value #unless opts[:persist] == false
39
25
  end
40
26
 
41
- def reset(hard=false)
42
- if hard
43
- @cache={}
44
- @persisted={}
45
- else
46
- @cache= @persisted.clone #Hash.new(@persisted) #{|h,k| h[k]= load_data(k) }
47
- end
27
+ def reset
28
+ @cache={}
48
29
  end
49
30
 
50
- # TODO: This is not a great place for this. MOVE IT!
51
- # This'll go on the Site class for query support: .find()/.all()/.paths()/.nodes()
52
- def site
53
- site= Hash.new {|h,k| h[k]= nil }
54
- Gumdrop.site.keys.sort.each do |path|
55
- unless Gumdrop.greylist.any? {|pattern| path_match path, pattern }
56
- site[path]= Gumdrop.site[path]
57
- end
58
- end
59
- site
60
- end
61
- # Oh dear god! This belongs elsewhere!
62
- def path_match(path, pattern)
63
- File.fnmatch pattern, path, File::FNM_PATHNAME | File::FNM_DOTMATCH | File::FNM_CASEFOLD
64
- end
65
-
66
-
67
- def site_all
68
- Gumdrop.site
31
+ def site(pattern=nil, opts={})
32
+ @site.contents(pattern, opts)
69
33
  end
70
34
 
71
35
  def pager_for(key, opts={})
72
36
  base_path= opts.fetch(:base_path, 'page')
73
37
  page_size= opts.fetch(:page_size, 5)
74
38
  data= if key.is_a? Symbol
75
- cache_or_load_data(key)
39
+ cache_dataset(key)
76
40
  @cache[key]
77
41
  else
78
42
  key
@@ -83,17 +47,14 @@ module Gumdrop
83
47
 
84
48
  private
85
49
 
86
- def cache_or_load_data(key)
50
+ def cache_dataset(key)
87
51
  @cache[key]= load_data(key) unless @cache.has_key? key
88
- @persisted[key]= @cache[key] # New persist data loaded from file?
89
52
  end
90
53
 
91
54
  def load_data(key)
92
- path=get_filename(key)
55
+ path=get_filename key
93
56
  return nil if path.nil?
94
- if File.extname(path) == ".yamldb"
95
- load_from_yamldb path
96
- elsif File.extname(path) == ""
57
+ if File.directory? path
97
58
  load_from_directory path
98
59
  else
99
60
  load_from_file path
@@ -104,10 +65,14 @@ module Gumdrop
104
65
  ext=File.extname(filename)
105
66
  if ext == '.yamldoc' or ext == '.ymldoc'
106
67
  load_from_yamldoc filename
68
+ elsif ext == '.yamldb' or ext == '.ymldb'
69
+ load_from_yamldb filename
107
70
  elsif ext == '.yaml' or ext == '.yml' or ext == '.json'
108
71
  hashes2ostruct( YAML.load_file(filename) )
109
72
  else
110
- raise "Unknown data type (#{ext}) for #{filename}"
73
+ # raise "Unknown data type (#{ext}) for #{filename}"
74
+ Gumdrop.report "Unknown data type (#{ext}) for #{filename}", :warning
75
+ nil
111
76
  end
112
77
  end
113
78
 
@@ -148,7 +113,7 @@ module Gumdrop
148
113
 
149
114
  def load_from_directory( filepath )
150
115
  all=[]
151
- Dir[ File.join( "#{filepath}", "{*.yaml,*.json,*.yml,*.yamldoc,*.ymldoc}" ) ].each do |filename|
116
+ Dir[ File.join "#{filepath}", "{*.#{ DATA_DOC_EXTS.join ',*.'}}" ].each do |filename|
152
117
  # Gumdrop.report ">> Loading data file: #{filename}"
153
118
  id= File.basename filename
154
119
  obj_hash= load_from_file filename
@@ -159,19 +124,14 @@ module Gumdrop
159
124
  end
160
125
 
161
126
  def get_filename(path)
162
- if File.exists? local_path_to("#{path}.json")
163
- local_path_to "#{path}.json"
164
- elsif File.exists? local_path_to("#{path}.yml")
165
- local_path_to "#{path}.yml"
166
- elsif File.exists? local_path_to("#{path}.yaml")
167
- local_path_to "#{path}.yaml"
168
- elsif File.exists? local_path_to("#{path}.yamldb")
169
- local_path_to "#{path}.yamldb"
170
- elsif File.directory? local_path_to(path)
171
- local_path_to(path)
127
+ lpath= local_path_to(path)
128
+ if File.directory? lpath
129
+ lpath
172
130
  else
173
- #FIXME: Should it die if it can't find data?\
174
- # raise "No data found for #{path}"
131
+ DATA_DOC_EXTS.each do |ext|
132
+ lpath= local_path_to("#{path}.#{ext}")
133
+ return lpath if File.exists? lpath
134
+ end
175
135
  Gumdrop.report "No data found for #{path}", :warning
176
136
  nil
177
137
  end
@@ -180,9 +140,71 @@ module Gumdrop
180
140
  def local_path_to(filename)
181
141
  File.join(@dir.to_s, filename.to_s)
182
142
  end
143
+
144
+ def hashes2ostruct(object)
145
+ return case object
146
+ when Hash
147
+ object = object.clone
148
+ object.each do |key, value|
149
+ object[key] = hashes2ostruct(value)
150
+ end
151
+ OpenStruct.new(object)
152
+ when Array
153
+ object = object.clone
154
+ object.map! { |i| hashes2ostruct(i) }
155
+ else
156
+ object
157
+ end
158
+ end
159
+
183
160
  end
184
161
 
185
- class YamlDoc
162
+ class Pager
163
+ attr_reader :all, :pages, :base_url, :current_page, :page_sets
164
+
165
+ def initialize(articles, base_path="/page", page_size=5)
166
+ @all= articles
167
+ @page_size= page_size
168
+ @base_path= base_path
169
+ @page_sets= @all.in_groups_of(page_size, false)
170
+ @pages= []
171
+ @current_page=1
172
+ @page_sets.each do |art_ary|
173
+ @pages << HashObject.new({
174
+ items: art_ary,
175
+ page: @current_page,
176
+ uri: "#{base_path}/#{current_page}",
177
+ pager: self
178
+ })
179
+ @current_page += 1
180
+ end
181
+ @current_page= nil
182
+ end
183
+
184
+ def length
185
+ @pages.length
186
+ end
186
187
 
188
+ def first
189
+ @pages.first
190
+ end
191
+
192
+ def last
193
+ @pages.last
194
+ end
195
+
196
+ def each
197
+ @current_page=1
198
+ @pages.each do |page_set|
199
+ yield page_set
200
+ @current_page += 1
201
+ end
202
+ @current_page= nil
203
+ end
204
+
205
+ def [](key)
206
+ @pages[key]
207
+ end
187
208
  end
209
+
188
210
  end