mint 0.1.1 → 0.1.3

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.
@@ -0,0 +1,31 @@
1
+ require 'pathname'
2
+ require 'fileutils'
3
+ require 'yaml'
4
+
5
+ module Mint
6
+ module Helpers
7
+ # Returns the relative path to dir1 from dir2.
8
+ def self.normalize_path(dir1, dir2)
9
+ path = case dir1
10
+ when String
11
+ Pathname.new dir1
12
+ when Pathname
13
+ dir1
14
+ end
15
+
16
+ path.expand_path.relative_path_from(dir2.expand_path)
17
+ end
18
+
19
+ def self.ensure_directory(dir)
20
+ FileUtils.mkdir_p dir
21
+ end
22
+
23
+ def self.update_yaml(new_opts, file)
24
+ curr_opts = file.exist? ? YAML.load_file(file) : {}
25
+
26
+ File.open file, 'w' do |f|
27
+ YAML.dump curr_opts.merge(new_opts), f
28
+ end
29
+ end
30
+ end
31
+ end
data/lib/mint/mint.rb ADDED
@@ -0,0 +1,309 @@
1
+ require 'pathname'
2
+ require 'fileutils'
3
+ require 'tilt'
4
+ require 'helpers'
5
+
6
+ module Mint
7
+ VERSION = '0.1.3'
8
+ MINT_DIR = Pathname.new(__FILE__).realpath.dirname + '..'
9
+
10
+ # Assume that someone using an Html template has formatted it
11
+ # in Erb and that a Css stylesheet will pass untouched through
12
+ # a Less parser.
13
+ Tilt.register 'html', Tilt::ERBTemplate
14
+ Tilt.register 'css', Tilt::LessTemplate
15
+
16
+ # Return the an array with the Mint template path. Will first look
17
+ # for MINT_PATH environment variable. Otherwise will use smart defaults.
18
+ # Either way, earlier/higher paths take precedence.
19
+ def self.path
20
+ mint_path = ENV['MINT_PATH'] || "#{Dir.getwd}/.mint:~/.mint:#{MINT_DIR}"
21
+ path = mint_path.split(':').map {|p| Pathname.new(p).expand_path }
22
+ end
23
+
24
+ # I want to refactor this so that Mint.path is always a Hash...
25
+ # should take care of this in the Mint.path=() method.
26
+ # Right now, this is a hack. It assumes a sane MINT_PATH, where the
27
+ # first entry is most local and the last entry is most global.
28
+ def self.path_for_scope(scope=:local)
29
+ case Mint.path
30
+ when Array
31
+ index = { local: 0, user: 1, global: 2 }[scope]
32
+ Mint.path[index]
33
+ when Hash
34
+ Mint.path[scope]
35
+ else
36
+ nil
37
+ end
38
+ end
39
+
40
+ # Returns a hash with key Mint directories
41
+ def self.directories
42
+ { templates: 'templates' }
43
+ end
44
+
45
+ # Returns a hash with key Mint files
46
+ def self.files
47
+ { config: 'config.yaml' }
48
+ end
49
+
50
+ def self.default_options
51
+ {
52
+ # Do not set default `template`--will override style and
53
+ # layout when already specified -- causes tricky bugs
54
+ layout: 'default', # default layout
55
+ style: 'default', # default style
56
+ destination: nil, # do not create a subdirectory
57
+ style_destination: nil # do not copy style to root
58
+ }
59
+ end
60
+
61
+ # Returns a list of all file extensions that Tilt will render
62
+ def self.formats
63
+ Tilt.mappings.keys
64
+ end
65
+
66
+ # Registered Css formats, for source -> destination
67
+ # name guessing/conversion only.
68
+ def self.css_formats
69
+ css_formats = ['.css', '.sass', '.scss', '.less']
70
+ end
71
+
72
+ # Decides whether the template specified by `name_or_file` is a real
73
+ # file or the name of a template. If it is a real file, Mint will
74
+ # return a that file. Otherwise, Mint will look for a file with that
75
+ # name in the Mint path. The `type` argument indicates whether the
76
+ # template we are looking for is a layout or a style and will affect
77
+ # which type of template is returned for a given template name. For
78
+ # example, `lookup_template :normal` might return a layout template
79
+ # referring to the file ~/.mint/templates/normal/layout.erb.
80
+ # Adding :style as a second argument returns
81
+ # ~/.mint/templates/normal/style.css.
82
+ def self.lookup_template(name_or_file, type=:layout)
83
+ name = name_or_file.to_s
84
+ File.exist?(name) ? Pathname.new(name) : find_template(name, type)
85
+ end
86
+
87
+ # Finds a template named `name` in the Mint path. If `type` # is :layout,
88
+ # will look for `${MINT_PATH}/templates/layout.*`. If it is :style, will
89
+ # look for `${MINT_PATH}/templates/template_name/style.*`. Mint assumes
90
+ # that a named template will hold only one layout and one style template.
91
+ # It does not know how to decide between style.css and style.less, for
92
+ # example. For predictable results, only include one template file
93
+ # called `layout.*` in the `template_name` directory. Returns nil if
94
+ # it cannot find a template.
95
+ def self.find_template(name, type)
96
+ templates_dir = Mint.directories[:templates]
97
+
98
+ file_name = lambda {|x| x + templates_dir + name + type.to_s }
99
+ find_files = lambda {|x| Pathname.glob "#{x.to_s}.*" }
100
+ acceptable = lambda {|x| x.to_s =~ /#{Mint.formats.join '|'}/ }
101
+
102
+ Mint.path.
103
+ map(&file_name).map(&find_files).flatten.
104
+ select(&acceptable).select(&:exist?).
105
+ first
106
+ end
107
+
108
+ # Guesses an appropriate name for the resource output file based on
109
+ # its source file's base name
110
+ def self.guess_name_from(name)
111
+ css = Mint.css_formats.join '|'
112
+ name.basename.to_s.
113
+ gsub(/(#{css})$/, '.css').
114
+ gsub(/(\.[^css]+)$/, '.html')
115
+ end
116
+
117
+ # Transforms a path into a template that will render the file specified
118
+ # at that path
119
+ def self.renderer(path)
120
+ Tilt.new path.to_s, :smart => true
121
+ end
122
+
123
+ class Resource
124
+ attr_accessor :type
125
+
126
+ attr_reader :source
127
+ def source=(source)
128
+ @source = Pathname.new(source) if source
129
+ end
130
+
131
+ # I haven't tested this - moved empty string from
132
+ # default options into this method, so that default options
133
+ # can be uniform - i.e., style_destination and destination
134
+ # can each be nil to indicate that any rendering will be
135
+ # done in the same folder the file is already in. I need
136
+ # to make sure that adding the empty string here actually
137
+ # keeps us in the current working directory
138
+ attr_reader :destination
139
+ def destination=(destination)
140
+ @destination = Pathname.new(destination || '')
141
+ end
142
+
143
+ attr_reader :name
144
+ def name=(name)
145
+ @name = name
146
+ end
147
+
148
+ def renderer=(renderer)
149
+ @renderer = renderer
150
+ end
151
+
152
+ def initialize(source, type=:resource, options={})
153
+ return nil unless source
154
+
155
+ self.source = source
156
+ self.type = type
157
+ self.destination = options[:destination]
158
+ self.name = Mint.guess_name_from source
159
+ self.renderer = Mint.renderer source
160
+ end
161
+
162
+ def equal?(other)
163
+ destination + name == other.destination + other.name
164
+ end
165
+ alias_method :==, :equal?
166
+
167
+ def render(context=Object.new, args={})
168
+ # see Tilt TEMPLATES.md for more info
169
+ @renderer.render context, args
170
+ end
171
+ end
172
+
173
+ # Layout describes a resource whose type is `:layout`. Beyond its type,
174
+ # it is a simple resource. However, its type helps decide which template
175
+ # file to use when a template name is specified.
176
+ class Layout < Resource
177
+ def initialize(source, opts=Mint.default_options)
178
+ super(source, :layout, opts)
179
+ end
180
+ end
181
+
182
+ # Style describes a resource whose type is `:style`. Beyond its type,
183
+ # it is a simple resource. However, its type helps decide which template
184
+ # file to use when a template name is specified.
185
+ class Style < Resource
186
+ def initialize(source, opts=Mint.default_options)
187
+ super(source, :style, opts)
188
+ end
189
+
190
+ def needs_rendering?
191
+ source.extname !~ /\.css$/
192
+ end
193
+ end
194
+
195
+ class Document < Resource
196
+ include Helpers
197
+
198
+ # The following provide reader/accessor methods for the objects's
199
+ # important attributes. Each implicit reader is paired with an
200
+ # explicit assignment method that processes a variety of input to a
201
+ # standardized state.
202
+
203
+ # When you set content, you are giving the document a renderer based
204
+ # on the content file and are processing the templated content into
205
+ # Html, which you can then access using via the content reader.
206
+ attr_reader :content
207
+ def content=(content)
208
+ meta, body = src.split "\n\n"
209
+ @inline_style = YAML.load meta
210
+ @renderer = Mint.renderer body
211
+ @content = @renderer.render
212
+ rescue
213
+ # I want to dry up this part of the code - and maybe look up which
214
+ # error Yaml will throw if it can't parse the first paragraph
215
+ # in the content
216
+ @renderer = Mint.renderer content
217
+ @content = @renderer.render
218
+ end
219
+
220
+ # The explicit assignment method allows you to pass the document an existing
221
+ # layout or the name of a layout template in the Mint path or an
222
+ # existing layout file.
223
+ attr_reader :layout
224
+ def layout=(layout)
225
+ @layout =
226
+ if layout.respond_to? :render
227
+ layout
228
+ else
229
+ layout_file = Mint.lookup_template layout, :layout
230
+ Layout.new layout_file
231
+ end
232
+ end
233
+
234
+ # The explicit assignment method allows you to pass the document an existing
235
+ # style or the name of a style template in the Mint path or an
236
+ # existing style file.
237
+ attr_reader :style
238
+ def style=(style)
239
+ @style =
240
+ if style.respond_to? :render
241
+ style
242
+ else
243
+ style_file = Mint.lookup_template style, :style
244
+ Style.new style_file, :destination => destination
245
+ end
246
+ end
247
+
248
+ def template=(template)
249
+ layout, style = template, template if template
250
+ end
251
+
252
+ def initialize(source, opts={})
253
+ options = Mint.default_options.merge opts
254
+ super(source, :document, options)
255
+
256
+ # Each of these should invoke explicitly defined method
257
+ self.content = source
258
+ self.layout = options[:layout]
259
+ self.style = options[:style]
260
+
261
+ # The template option takes precedence over the other two
262
+ self.template = options[:template]
263
+
264
+ self.style.destination =
265
+ options[:style_destination] || self.style.source.dirname.expand_path
266
+ end
267
+
268
+ def render(args={})
269
+ layout.render self, args
270
+ end
271
+
272
+ def mint(root=Dir.getwd, render_style=true)
273
+ root = Pathname.new root
274
+
275
+ # Only render style if a) it's specified by the options path and
276
+ # b) it actually needs rendering (i.e., it's in template form and
277
+ # not raw, browser-parseable CSS).
278
+ render_style &&= style.needs_rendering?
279
+
280
+ resources = [self]
281
+ resources << style if render_style
282
+
283
+ resources.compact.each do |r|
284
+ dest = root + r.destination + r.name
285
+ FileUtils.mkdir_p dest.dirname
286
+
287
+ dest.open 'w+' do |f|
288
+ f << r.render
289
+ end
290
+ end
291
+ end
292
+
293
+ # Convenience methods for views
294
+
295
+ # Returns any inline document style that was parsed from the
296
+ # content file, in the header. For use in view where we want
297
+ # document-specific Css modifications.
298
+ def inline_style
299
+ @inline_style
300
+ end
301
+
302
+ # Returns a relative path from the document to its stylesheet. Can
303
+ # be called directly from inside a layout template.
304
+ def stylesheet
305
+ Helpers.normalize_path(style.destination.expand_path, destination) +
306
+ style.name.to_s
307
+ end
308
+ end
309
+ end
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mint
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
5
4
  prerelease: false
6
5
  segments:
7
6
  - 0
8
7
  - 1
9
- - 1
10
- version: 0.1.1
8
+ - 3
9
+ version: 0.1.3
11
10
  platform: ruby
12
11
  authors:
13
12
  - David Jacobs
@@ -15,10 +14,22 @@ autorequire:
15
14
  bindir: bin
16
15
  cert_chain: []
17
16
 
18
- date: 2010-06-28 00:00:00 -04:00
17
+ date: 2011-02-01 00:00:00 -05:00
19
18
  default_executable:
20
- dependencies: []
21
-
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: tilt
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
22
33
  description:
23
34
  email: david@allthingsprogress.com
24
35
  executables:
@@ -31,10 +42,10 @@ files:
31
42
  - README.md
32
43
  - bin/mint
33
44
  - lib/mint.rb
34
- - templates/default/layout.haml
35
- - templates/default/style.sass
45
+ - lib/mint/mint.rb
46
+ - lib/mint/helpers.rb
36
47
  has_rdoc: true
37
- homepage: http://github.com/davejacobs/mint/
48
+ homepage: http://github.com/davejacobs/mint
38
49
  licenses: []
39
50
 
40
51
  post_install_message:
@@ -47,7 +58,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
47
58
  requirements:
48
59
  - - ">="
49
60
  - !ruby/object:Gem::Version
50
- hash: 3
51
61
  segments:
52
62
  - 0
53
63
  version: "0"
@@ -56,7 +66,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
56
66
  requirements:
57
67
  - - ">="
58
68
  - !ruby/object:Gem::Version
59
- hash: 23
60
69
  segments:
61
70
  - 1
62
71
  - 3
@@ -1,7 +0,0 @@
1
- !!!
2
- %html
3
- %head
4
- %link{ :rel => 'stylesheet', :href => stylesheet }
5
-
6
- %body
7
- #container= content
@@ -1,21 +0,0 @@
1
- body
2
- font-family: 'Hoefler Text', Georgia, Garamond, serif
3
-
4
- code
5
- font-family: Monaco, 'Lucida Console', Consolas, Monotype, mono
6
-
7
- @media screen
8
- body
9
- font-size: 16px
10
-
11
- #container
12
- display: block
13
- margin: 1em auto
14
-
15
- @media print
16
- body
17
- font-size: 12pt
18
- margin: 1in 1.25in
19
-
20
- img
21
- display: block