pubba 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,5 +1,12 @@
1
1
  master
2
2
 
3
+ * Conceptual change from Sinatra extension to generic library
4
+ * Namespace changed from Sinatra::Pubba to Pubba
5
+ * Change to configuration implementation
6
+ * Pubba::Site.configure called in Pubba.configure
7
+ * Removed automatic monitoring. Detailed how to set this up as a Rake task in the README
8
+ * Added ability to set asset host(s)
9
+ * Javascript and Stylesheet folders are configurable. Defaults to 'js' and 'css'.
3
10
 
4
11
  0.6.0
5
12
 
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # pubba
2
2
 
3
- pubba is a Sinatra extension designed to help you manage your site. It uses [Sprockets](https://github.com/sstephenson/sprockets) for packaging assets and [R18n](http://r18n.rubyforge.org/) for internationalization/localization. I use R18n as a central location for default text, the internationalization functionality is a nice bonus in the event the application needs to move in that direction.
3
+ pubba is a library designed to help you manage your site. It uses [Sprockets](https://github.com/sstephenson/sprockets) for packaging assets and [R18n](http://r18n.rubyforge.org/) for internationalization/localization. I use R18n as a central location for default text, the internationalization functionality is a nice bonus in the event the application needs to move in that direction.
4
4
 
5
5
  # Note
6
6
 
@@ -31,26 +31,45 @@ In addition, when using R18n, pubba gives you access through a single page objec
31
31
 
32
32
  More details on these later, but here are the configuration options:
33
33
 
34
- **Note: __settings.root__ refers to \<ProjectRoot\>/app/**
34
+ ### A couple of things to note:
35
35
 
36
- ### Location of the config file. REQUIRED
37
- set :pubba_config, File.join(settings.root, '..', 'config', 'pubba.yml')
36
+ * __settings.root__ refers to &lt;ProjectRoot&gt;/app/**
37
+ * Images are not processed and should be stored in your public directory
38
38
 
39
- ### Location of the public_folder. REQUIRED
40
- set :public_folder, File.join(settings.root, '..', 'public')
41
39
 
42
- ### Location of the asset_folder. REQUIRED
43
- set :asset_folder, File.join(settings.root, 'assets')
40
+ ### Sample configuration
41
+ Pubba.configure do |p|
42
+ # REQUIRED.
43
+ p.config_file = File.join( File.dirname(__FILE__), 'pubba.yml')
44
44
 
45
- ### Asset handler. Defaults to [Sprockets](https://github.com/sstephenson/sprockets)
46
- Right now there's only support for Sprockets, but leaving the option open for others.
47
- set :asset_handler, Sinatra::Pubba::Assets::SprocketsHandler
45
+ # REQUIRED.
46
+ p.asset_folder = File.join(settings.root, 'assets')
48
47
 
49
- ### Location of the [R18n](http://r18n.rubyforge.org/) folder. OPTIONAL
50
- set :r18n_folder, File.join(settings.root, 'i18n')
48
+ # REQUIRED.
49
+ p.public_folder = settings.public_folder
51
50
 
52
- ### Locale. Defaults to 'en'
53
- set :r18n_locale, 'en'
51
+ # OPTIONAL. Defaults to 'css'
52
+ p.style_folder = 'css'
53
+
54
+ # OPTIONAL. Defaults to 'js'
55
+ p.script_folder = 'js'
56
+
57
+ # OPTIONAL. Defaults to Sprockets: https://github.com/sstephenson/sprockets/
58
+ p.asset_handler = Pubba::Assets::SprocketsHandler
59
+
60
+ # OPTIONAL. Defaults to YUI Compressor: https://github.com/sstephenson/ruby-yui-compressor/
61
+ p.asset_minifier = Pubba::Assets::YUIMinifier
62
+
63
+ # OPTIONAL. Asset hosts (value must be a Proc)
64
+ p.asset_host = -> asset { [ "http://assets.mysite.com#{asset}",
65
+ "http://assets1.mysite.com#{asset}"].sample }
66
+
67
+ # OPTIONAL.
68
+ p.r18n_folder = File.join(settings.root, 'i18n')
69
+
70
+ # OPTIONAL.
71
+ p.r18n_locale, 'en'
72
+ end
54
73
 
55
74
 
56
75
 
@@ -62,17 +81,18 @@ First things first, you'll want to install the gem:
62
81
 
63
82
  Then you'll want to use it in your app like so:
64
83
 
65
- require 'sinatra/pubba'
84
+ require 'pubba'
66
85
 
67
86
  class App < Sinatra::Application
68
87
  # Settings as described above
69
- set :asset_folder, File.join(settings.root, 'assets')
70
88
  set :public_folder, File.join(settings.root, '..', 'public')
71
- set :r18n_folder, File.join(settings.root, 'i18n')
72
89
 
73
- set :pubba_config, File.join(settings.root, '..', 'config', 'pubba.yml')
74
-
75
- register Sinatra::Pubba
90
+ Pubba.configure do |p|
91
+ p.config_file = File.join(settings.root, '..', 'config', 'pubba.yml')
92
+ p.public_folder = settings.public_folder
93
+ p.asset_folder = File.join(settings.root, 'assets')
94
+ p.r18n_folder = File.join(settings.root, 'i18n')
95
+ end
76
96
  end
77
97
 
78
98
  Next up is creating the all important __pubba.yml__ config file:
@@ -168,6 +188,30 @@ Again, using the above __pubba.yml__ configuration, if you are using the 'home'
168
188
  <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js" type="text/javascript"></script>
169
189
  <script src="/js/home-body.js" type="text/javascript"></script>
170
190
 
191
+ # Monitoring changes
192
+
193
+ To monitor your assets directory to keep the public directory in sync, you'll need to create a rake task:
194
+
195
+ namespace :pubba do
196
+ desc 'Process the js and css assets then monitor for changes'
197
+ task :monitor do
198
+
199
+ # Require your app to configure Pubba
200
+ require_relative 'app/app'
201
+
202
+ require 'pubba/monitor'
203
+
204
+ # The monitor process will only run Site.process when it detects a
205
+ # change so we need to run it once to make sure the public directory
206
+ # is in sync.
207
+
208
+ puts ">> Processing assets..."
209
+ Pubba::Site.process
210
+
211
+ Pubba::Monitor.run
212
+ end
213
+ end
214
+
171
215
  # R18n
172
216
 
173
217
  If you're using R18n, you will need a translation file, here's a sample en.yml:
@@ -186,7 +230,7 @@ If you're using R18n, you will need a translation file, here's a sample en.yml:
186
230
  Take note of the __home__ section in both __pubba.yml__ and __en.yml__. If you have a route in your app that you want to use the __home__ defintions, do this:
187
231
 
188
232
  get '/' do
189
- @page = Sinatra::Pubba::Site.page('home')
233
+ @page = Pubba::Site.page('home')
190
234
  slim :"aux/index"
191
235
  end
192
236
 
@@ -203,9 +247,10 @@ Notice that `title` is defined under the `home` section, but `home_link` is a to
203
247
 
204
248
  # Acknowledgement
205
249
 
206
- Huge thanks to my company, [Primedia](http://primedia.com) for encouraging open source contributions. This particular extension is obviously very new and hasn't hit a production site yet, but it will. I will post a list here as we migrate our applications from Rails to Sinatra.
250
+ Huge thanks to my company, [Primedia](http://primedia.com) for encouraging open source contributions.
207
251
 
208
252
  # Contributors
209
253
 
210
254
  I highly value contributions and will happily list all those who submit accepted pull requests.
211
255
 
256
+ [Chas Lemley](https://github.com/chaslemley) : YUI Compressor support
@@ -0,0 +1,42 @@
1
+ require 'sinatra/base'
2
+ require 'rack/statica_server'
3
+
4
+ require_relative 'pubba/errors'
5
+ require_relative 'pubba/site'
6
+ require_relative 'pubba/html/helpers'
7
+
8
+ module Pubba
9
+ class << self
10
+ attr_accessor :config_file, :public_folder
11
+ attr_accessor :style_folder, :script_folder
12
+ attr_accessor :asset_folder, :asset_host
13
+ attr_accessor :asset_handler, :asset_minifier
14
+ attr_accessor :r18n_folder, :r18n_locale
15
+
16
+ def configure
17
+ yield self
18
+ validate_settings
19
+ Site.configure
20
+ end
21
+
22
+ def set_defaults
23
+ @asset_handler = Pubba::Assets::SprocketsHandler
24
+ @asset_minifier = Pubba::Assets::YUIMinifier
25
+ @script_folder = 'js'
26
+ @style_folder = 'css'
27
+ end
28
+
29
+ def validate_settings
30
+ missing_settings = []
31
+ missing_settings << ":public_folder has not been set!" unless Pubba.public_folder
32
+ missing_settings << ":asset_folder has not been set!" unless Pubba.asset_folder
33
+
34
+ if missing_settings.length > 0
35
+ messages = missing_settings.join("\n")
36
+ raise Pubba::ConfigurationError.new("Missing configuration options:\n#{messages}")
37
+ end
38
+ end
39
+ end
40
+ end # Pubba
41
+
42
+ Pubba.set_defaults
@@ -0,0 +1,25 @@
1
+ require 'psych'
2
+
3
+ module Pubba
4
+ module Assets
5
+ class Configuration
6
+ attr_reader :yaml, :name, :disclaimer
7
+
8
+ def initialize(config_file)
9
+ @yaml = Psych.load_file(config_file)
10
+ @name = File.basename(config_file)
11
+ @disclaimer = "// This file is automatically generated from the contents\n// in #{name}\n//\n"
12
+ end
13
+
14
+ def global_config!
15
+ @global_config ||= (yaml.delete("global") || {})
16
+ end
17
+
18
+ def process
19
+ yaml.each do |page, config|
20
+ yield page, config
21
+ end
22
+ end
23
+ end # Configuration
24
+ end # Assets
25
+ end # Pubba
@@ -0,0 +1,21 @@
1
+ module Pubba
2
+ module Assets
3
+ class Handler
4
+ def self.asset(file)
5
+ raise NotImplementedError
6
+ end
7
+
8
+ def save_as(file)
9
+ raise NotImplementedError
10
+ end
11
+
12
+ def process(pattern, destination)
13
+ raise NotImplementedError
14
+ end
15
+
16
+ def build(name, type, ext, urls)
17
+ raise NotImplementedError
18
+ end
19
+ end # Handler
20
+ end # Assets
21
+ end # Pubba
@@ -0,0 +1,9 @@
1
+ module Pubba
2
+ module Assets
3
+ class Minifier
4
+ def self.minify(folder, handler)
5
+ raise NotImplementedError
6
+ end
7
+ end # Compressor
8
+ end # Assets
9
+ end # Pubba
@@ -0,0 +1,53 @@
1
+ require 'fileutils'
2
+ require 'sprockets'
3
+ require_relative 'handler'
4
+
5
+ module Pubba
6
+ module Assets
7
+ class SprocketsHandler < Handler
8
+ def self.find(file)
9
+ SprocketsHandler.new(sprockets.find_asset(file))
10
+ end
11
+
12
+ def self.asset_paths(*paths)
13
+ paths.each do |path|
14
+ sprockets.append_path path
15
+ end
16
+ end
17
+
18
+ def self.sprockets
19
+ @sprockets ||= Sprockets::Environment.new()
20
+ end
21
+
22
+ def self.process(source, destination)
23
+ FileUtils.mkdir_p destination
24
+
25
+ Dir.glob("#{source}/*") do |file|
26
+ asset = find(file)
27
+ asset.save_as "#{destination}/#{File.basename(file)}"
28
+ end
29
+ end
30
+
31
+ def self.build(name, type, ext, urls)
32
+ content = urls.collect{|url| "//= require #{url}.#{ext}"}.compact.join("\n")
33
+ out_folder = File.join(Pubba.asset_folder, "out", ext)
34
+ FileUtils.mkdir_p out_folder
35
+ fname = File.join(out_folder, "#{name}-#{type}.#{ext}")
36
+ File.open(fname, 'w') do |f|
37
+ f.write Site.asset_configuration.disclaimer
38
+ f.write content
39
+ end
40
+ end
41
+
42
+ attr_reader :asset
43
+
44
+ def initialize(asset)
45
+ @asset = asset
46
+ end
47
+
48
+ def save_as(file)
49
+ asset.write_to(file)
50
+ end
51
+ end # SprocketsHandler
52
+ end # Assets
53
+ end # Pubba
@@ -0,0 +1,26 @@
1
+ require "yui/compressor"
2
+ require_relative 'minifier'
3
+
4
+ module Pubba
5
+ module Assets
6
+ class YUIMinifier < Minifier
7
+ def self.minify(folder, handler)
8
+ compressor = get_compressor(handler)
9
+ Dir.glob("#{folder}/*.*") do |file|
10
+ contents = File.read(file)
11
+ File.open(file, "w") {|f| f.write(compressor.compress(contents))}
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def self.get_compressor(handler)
18
+ case handler
19
+ when :js then YUI::JavaScriptCompressor.new
20
+ when :css then YUI::CssCompressor.new
21
+ else raise ArgumentError, "minify handler must be one of [:js, :css]"
22
+ end
23
+ end
24
+ end # YUIMinifier
25
+ end # Assets
26
+ end # Pubba
@@ -0,0 +1,3 @@
1
+ module Pubba
2
+ class ConfigurationError < StandardError; end
3
+ end
@@ -0,0 +1,50 @@
1
+ module Pubba
2
+ module HTML
3
+ module Helpers
4
+ def page_head_tags
5
+ process_tags(@page.head_tags)
6
+ end
7
+
8
+ def page_body_tags
9
+ process_tags(@page.body_tags)
10
+ end
11
+
12
+ def digest_url(url)
13
+ url.start_with?('http') ? url : Site.asset_host[::Statica.digest_url(url)]
14
+ end
15
+
16
+ private
17
+
18
+ def process_tags(tags)
19
+ array = []
20
+ tags.each do |tag|
21
+ t = tag.dup
22
+ set_url(type = t.delete(:tag), t)
23
+ array << tag_content(type, '', t)
24
+ end
25
+ array.join('')
26
+ end
27
+
28
+ def set_url(type, t)
29
+ if type == 'script'
30
+ t[:src] = digest_url(t[:src])
31
+ else
32
+ t[:href] = digest_url(t[:href])
33
+ end
34
+ end
35
+
36
+ def tag_content(tag, content, attrs={}, self_closing=false)
37
+ base = "<#{tag}#{tag_attrs(attrs)}"
38
+ self_closing ? "#{base}/>":"#{base}>#{content}</#{tag}>"
39
+ end
40
+
41
+ def tag_attrs(attrs)
42
+ return '' if attrs.empty?
43
+
44
+ return " " + attrs.keys.sort.collect do |k|
45
+ %|#{k}="#{attrs[k]}"|
46
+ end.join(' ')
47
+ end
48
+ end
49
+ end # HTML
50
+ end # Pubba
@@ -0,0 +1,16 @@
1
+ require 'r18n-desktop'
2
+
3
+ module Pubba
4
+ class Locale
5
+ include R18n::Helpers
6
+
7
+ R18n.from_env Pubba.r18n_folder, Pubba.r18n_locale
8
+
9
+ def get(category, name, *args)
10
+ res = t.send(category).send(name, *args)
11
+ res = t.send(name, *args) if R18n::Untranslated === res
12
+
13
+ R18n::Untranslated === res ? nil : res
14
+ end
15
+ end # Locale
16
+ end # Pubba
@@ -0,0 +1,29 @@
1
+ require 'fssm'
2
+
3
+ module Pubba
4
+ module Monitor
5
+ extend self
6
+
7
+ def run
8
+ script_asset_folder = File.join(Pubba.asset_folder, Pubba.script_folder)
9
+ style_asset_folder = File.join(Pubba.asset_folder, Pubba.style_folder)
10
+
11
+ puts ">> Pubba is now monitoring:\n>> #{script_asset_folder}\n>> #{style_asset_folder}"
12
+ FSSM.monitor do
13
+ path script_asset_folder do
14
+ glob '**/*'
15
+ update {|base, relative, type| Site.process}
16
+ delete {|base, relative, type| Site.process}
17
+ create {|base, relative, type| Site.process}
18
+ end
19
+
20
+ path style_asset_folder do
21
+ glob '**/*'
22
+ update {|base, relative, type| Site.process}
23
+ delete {|base, relative, type| Site.process}
24
+ create {|base, relative, type| Site.process}
25
+ end
26
+ end
27
+ end
28
+ end # Monitor
29
+ end # Pubba
@@ -0,0 +1,133 @@
1
+ module Pubba
2
+ class Page
3
+ attr_accessor :name
4
+ attr_reader :assets, :head_tags, :body_tags
5
+
6
+ def initialize(name, global_configuration = {})
7
+ @name = name
8
+ @assets = {"styles" => {}, "scripts" => {}}
9
+ @head_tags = []
10
+ @body_tags = []
11
+
12
+ global_configuration["styles"].each do |key, value|
13
+ @assets["styles"][key] = value.dup
14
+ end
15
+
16
+ script_groups do |group|
17
+ @assets["scripts"][group] = global_configuration["scripts"][group] || []
18
+ end
19
+ end
20
+
21
+ def add_asset(type, section)
22
+ if type == "styles"
23
+ section.each do |section_name, hash|
24
+ hash.each do |key, value|
25
+ if key == "urls"
26
+ @assets[type][section_name][key] += value
27
+ else
28
+ @assets[type][section_name][key] = value
29
+ end
30
+ end
31
+ end
32
+ else
33
+ script_groups do |group|
34
+ @assets["scripts"][group] += section[group] if section[group]
35
+ end
36
+ end
37
+ end
38
+
39
+ def assetize
40
+ create_style_assets
41
+ create_script_assets
42
+ end
43
+
44
+ def tagify
45
+ style_groups do |group, hash|
46
+ style_urls(group) do |url|
47
+ add_style_tag(group, hash, url)
48
+ end
49
+ end
50
+
51
+ script_groups do |group|
52
+ script_urls(group) do |url|
53
+ add_script_tag(group, url)
54
+ end
55
+ end
56
+ end
57
+
58
+ def method_missing(meth, *args)
59
+ if Site.locale && (t = Site.locale.get(name, meth, *args))
60
+ return t
61
+ end
62
+ super
63
+ end
64
+
65
+ private
66
+
67
+ def process_scripts(array)
68
+ return [] if array.nil? || array.empty?
69
+
70
+ array.each{|ele| scripts << ele }
71
+ end
72
+
73
+ def create_style_assets
74
+ style_groups do |group, hash|
75
+ urls = []
76
+ style_urls(group) do |url|
77
+ next if url.start_with?("http")
78
+ urls << url
79
+ end
80
+ Pubba.asset_handler.build(name, group, "css", urls)
81
+ end
82
+ end
83
+
84
+ def create_script_assets
85
+ script_groups do |group|
86
+ urls = []
87
+ script_urls(group) do |url|
88
+ next if url.start_with?("http")
89
+ urls << url
90
+ end
91
+ Pubba.asset_handler.build(name, group, "js", urls)
92
+ end
93
+ end
94
+
95
+ def style_groups
96
+ @assets["styles"].each{|group, hash| yield group, hash }
97
+ end
98
+
99
+ def style_urls(group)
100
+ @assets["styles"][group]["urls"].each{|url| yield url }
101
+ end
102
+
103
+ def script_groups
104
+ ["head", "body"].each{|group| yield group }
105
+ end
106
+
107
+ def script_urls(group)
108
+ @assets["scripts"][group].each{|url| yield url }
109
+ end
110
+
111
+ def add_style_tag(group, hash, url)
112
+ h = { tag: 'link', type: 'text/css', rel: 'stylesheet' }
113
+ h[:media] = hash['media'] if hash['media']
114
+ h[:href] = url.start_with?("http") ? url : "/#{Pubba.style_folder}/#{name}-#{group}.css"
115
+
116
+ maybe_add_tag(@head_tags, h, :href)
117
+ end
118
+
119
+ def add_script_tag(group, url)
120
+ h = { tag: 'script', type: "text/javascript" }
121
+ h[:src] = url.start_with?("http") ? url : "/#{Pubba.script_folder}/#{name}-#{group}.js"
122
+
123
+ tag_set = (group == "head") ? @head_tags : @body_tags
124
+ maybe_add_tag(tag_set, h, :src)
125
+ end
126
+
127
+ def maybe_add_tag(tag_set, hash, key)
128
+ found = false
129
+ tag_set.each{|tag| found = true if tag[key] == hash[key]}
130
+ tag_set << hash unless found
131
+ end
132
+ end # Page
133
+ end # Pubba