gumdrop 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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