sinatra-head 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.markdown
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE.markdown
@@ -0,0 +1,22 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+ .rvmrc
21
+
22
+ ## PROJECT::SPECIFIC
@@ -0,0 +1,113 @@
1
+ The Don't Be a Dick License
2
+ ===========================
3
+ _version 0.2_
4
+
5
+ **Project:** [Sinatra::Head](http://github.com/SFEley/sinatra-head)
6
+ **Author:** Stephen Eley (<sfeley@gmail.com>)
7
+
8
+ This is a proposed draft of the **Don't Be a Dick** license for open source projects. The purpose of this license is to permit the broadest feasible scope for reuse and modification of creative work, restricted only by the requirement that one is not a dick about it.
9
+
10
+ 1. Legal Parameters
11
+ -------------------
12
+ For legal purposes, the DBAD license is a superset of the [Apache License, Version 2.0][1] and incorporates all terms, conditions, privileges and limitations therein. The following text is a binding part of this license for this project:
13
+
14
+ > Copyright 2010 Stephen Eley
15
+
16
+ > Licensed under the Apache License, Version 2.0 (the "License");
17
+ you may not use this file except in compliance with the License.
18
+ You may obtain a copy of the License at
19
+
20
+ > <http://www.apache.org/licenses/LICENSE-2.0>
21
+
22
+ > Unless required by applicable law or agreed to in writing, software
23
+ distributed under the License is distributed on an "AS IS" BASIS,
24
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25
+ See the License for the specific language governing permissions and
26
+ limitations under the License.
27
+
28
+ Nothing in the following text should be construed to inhibit, contradict, or override any part of the Apache License, Version 2.0.
29
+
30
+ 2. Definitions
31
+ --------------
32
+ The following terms and definitions shall be in effect for the rest of this document.
33
+
34
+ > **NOTE:** Singular nouns and pronouns are used throughout this License for
35
+ grammatical convenience. However, any of the terms below _may_ refer to a
36
+ collective work or group of people. If this pegs your punctiliousness, you
37
+ are directed to mentally substitute "Person _or persons_," "Dick _or
38
+ dicks,_" etc., throughout this license. Just don't tell us about it.
39
+
40
+ ### A. Project
41
+
42
+ A creative work of (software | writing | visual art | music) into which significant time and energy have been invested by people who _are not you,_ and which has been released into the world for the benefit of the general public (_including you._)
43
+
44
+ The **Project** may include, incorporate, derive from, or be inspired by other works. The Author, being a Reasonable Person, has made use of such source materials only as permitted by their own licenses or applicable law. The License you are reading applies only to those portions of the Project which are original and distinct to it. Source materials may be covered by their own licenses or conditions, and should not be assumed to have coverage under this License. (However, you are strongly encouraged not to be a dick about them either.)
45
+
46
+ ### B. Author
47
+
48
+ A person who has invested significant time and energy into the Project licensed herein. Given the Author's release of the Project to the world under a liberal license, the Author is declared a Reasonable Person (at least within this context) and inherits all attributes, freedoms, and responsibilities thereof. No other assumptions are made about the Author, nor should you make any.
49
+
50
+ ### C. Reasonable Person
51
+
52
+ A person who respects the time and energy that have been invested in the Project licensed herein, and acts accordingly. A Reasonable Person is broadly characterized as one who exercises his or her privilege to use, _not_ use, redistribute, modify, improve, worsen, review, report issues upon, participate in the community of, or ignore the work _without_ placing undue demands upon the Author. I.e., a Reasonable Person is a constituent of the majority of open source users and the population at large who are not Dicks.
53
+
54
+ ### D. Dick
55
+
56
+ A person who _does not_ respect the time and energy that have been invested in the Project, and acts to punish such effort by giving others associated with the Project -- including, but not limited to, the Author -- a hard time. A Dick is nearly always selfish, but not necessarily with deliberate intent; some Dicks are merely thoughtless. The distinguishing characteristic of a Dick is that he or she places burdens upon Reasonable People, reducing their motivation to engage in open source activities. This damping effect is a significant detriment to the Project, to open source in general, to the production of new intellectual value in the world -- and, ultimately, to the Dick himself or herself.
57
+
58
+ > **NOTE:** Despite its original gender association, the word "Dick" is used herein in a _cultural_ context and not a _genital_ context. This License has chosen to adopt the term for its linguistic force and psychological impact, and sincerely regrets any inference of sexism. For purposes of the terms and conditions contained herein, it is understood that both men and women are equally capable of being Dicks.
59
+
60
+ > (But they shouldn't be.)
61
+
62
+ 3. Permissions
63
+ --------------
64
+
65
+ The following privileges are granted explicitly and exclusively to Reasonable People. Although the Author acknowledges the practical unfeasibility of barring Dicks from enjoying the same privileges, they are nevertheless asked to refrain from any activity related to the Project _and/or_ to reconsider their behavior and stop being Dicks.
66
+
67
+ 1. You are permitted to use the Project or any component of the Project.
68
+
69
+ 2. You are permitted to redistribute the Project or any component of the Project, by itself or as part of a different work, provided that you give fair and reasonable credit back to the Author.
70
+
71
+ 3. You are permitted to change the Project for your own needs or anyone else's. You may keep your changes to yourself or submit them back to the Author or the community, as you see fit, provided you are not a Dick about it.
72
+
73
+ 4. You are permitted and encouraged to participate in any community related to the Project, or to create new communities.
74
+
75
+ 5. You are permitted to report issues or problems with the Project, and to request that they be addressed, so long as the request is made in a reasonable fashion. (This privilege does _not_ oblige the Author to respond.)
76
+
77
+ 6. You are permitted to make money from the Project. No recompense is owed to the Author unless by separate agreement, although sharing opportunities for mutual benefit is by no means discouraged.
78
+
79
+ 7. You are permitted to discuss the Project in any medium, in any positive or negative spirit, so long as criticism is not libelous nor _ad hominem._ (I.e., don't lie, and keep it about the _work_ and not the _people._)
80
+
81
+ 8. You are permitted to ignore the Project completely and make no use of it whatsoever. This right is irrevocable and absolute, and extended to Dicks and Reasonable People alike.
82
+
83
+ 4. Limitations
84
+ --------------
85
+
86
+ The following restrictions are imposed explicitly and exclusively upon Dicks. These limitations are the inverse of the above privileges and are also tautological, as the prohibited actions are those _definitive_ of Dickhood.
87
+
88
+ 1. You may not impede Reasonable People from exercising their privilege to use, redistribute, change, participate in, profit from, discuss, or ignore the Project.
89
+
90
+ 2. You may not withhold the Author's credit for the Project, nor otherwise present the work of anyone else as your own.
91
+
92
+ 3. You may not hold the Author responsible for any use or abuse of the Project by you or anyone else.
93
+
94
+ 4. You may not troll, flame, nor dumb down any community associated with the Project.
95
+
96
+ 5. Barring separate agreements, you may not _demand_ any time or attention on the part of the Author nor anyone else in the community. You may not hold the Author personally accountable for any issues you discover nor otherwise spread your own problems to other people.
97
+
98
+ 6. You may not attempt to _compel_ time nor money from the Author nor any other community member by any means, including but not limited to legal action, intimidation, or annoyance.
99
+
100
+ 7. You may not engage in _ad hominem_ (i.e. personal) attacks or criticism of the Author in the course of criticizing the Project.
101
+
102
+ 8. You may not impede the absolute and irrevocable privilege of the Author and other members of the community to ignore you.
103
+
104
+ 5. Use of This License
105
+ ----------------------
106
+ The Don't Be a Dick License is released under the terms of the Don't Be a Dick License. Projects released under this License should remain under the License, and the terms of the License may not be modified by anyone other than the Project's original Author.
107
+
108
+ If you wish to make use of the License for your own open work, you may do so without asking permission, provided said work is truly your own, truly open, and you are truly not a Dick. You may modify the terms of the License to suit your own needs, but are strongly advised to annotate said changes. Modifications may not reduce the freedoms granted to Reasonable People as described in Section 3.
109
+
110
+ Reasonable People are _invited,_ but not compelled, to visit the Web site at <http://dbad-license.org> and browse or add their project to the repository of works released under the License.
111
+
112
+
113
+ [1]: http://apache.org/licenses/LICENSE-2.0
@@ -0,0 +1,169 @@
1
+ Sinatra::Head
2
+ =============
3
+
4
+ This is a simple asset and `<head>` tag manager for Sinatra projects. It allows dynamically adding or
5
+ changing stylesheets, javascript includes, or the page title at any level of Sinatra class inheritance
6
+ or within an action.
7
+
8
+ Setting Up
9
+ ----------
10
+ You should know this part:
11
+
12
+ $ gem install sinatra-head
13
+
14
+ (Or `sudo gem install` if you're the last person on Earth who isn't using [RVM][1] yet.)
15
+
16
+ If you're developing a Sinatra ['classic'][2] application, then all you need to do is require the library:
17
+
18
+ # blah_app.rb
19
+ require 'sinatra'
20
+ require 'sinatra/head'
21
+
22
+ title << 'My Wonderful App'
23
+ stylesheets << 'main.css'
24
+
25
+ get '/blah' do
26
+ title << 'Feeling blah'
27
+ stylesheets << 'blah.css'
28
+ end
29
+
30
+ Then, in your layout, just call the `head_tag` helper:
31
+
32
+ # views/layout.haml
33
+ %html
34
+ =head_tag
35
+ %body
36
+ =yield
37
+
38
+ When called, you'll see a head that sets the charset to UTF-8, "**Feeling blah | My Wonderful App**" as the page title, and
39
+ includes both */stylesheets/main.css* and *stylesheets/blah.css*. You can override the default choices here with Sinatra settings.
40
+
41
+ If you're using the [Sinatra::Base][2] style, you also need to register the extension:
42
+
43
+ # bleh_app.rb
44
+ require 'sinatra/base'
45
+ require 'sinatra/head'
46
+
47
+ class BlehApp < Sinatra::Base
48
+ register Sinatra::Head
49
+
50
+ # Everything else is the same as the 'classic' example above.
51
+ end
52
+
53
+ See the Sinatra documentation on [using extensions][3] for more detail and rationale.
54
+
55
+ Head element
56
+ ------------
57
+ **Tag helpers:** `head_tag`
58
+
59
+ This extension is all about the head and the layout. Accordingly, there's a convenient `head_tag` helper that puts together the four other elements described below:
60
+
61
+ <head>
62
+ <meta charset='UTF-8' />
63
+ <title>All title elements | as appended using the 'title' call | in reverse order</title>
64
+ <link rel='stylesheet' href='/stylesheets/first.css' />
65
+ <!-- ...other stylesheets as declared in order... -->
66
+ <script src="/javascript/first.js"></script>
67
+ <!-- ...other javascripts as declared in order... -->
68
+ </head>
69
+
70
+ If you want something more or something different, you can of course skip this and call the other _*\_tag_ methods any way you want.
71
+
72
+ *A note on style:* My markup flavor of choice is [HTML5][4]. HTML5 is backwards compatible with everything and does not require self-closing tags. However, some of you may be using XHTML, and I don't want to break your stuff over a minor religious difference. So the `meta` and `link` tags are self-closed, to keep your validators from complaining and because it doesn't really hurt anyone else.
73
+
74
+ Charset
75
+ -------
76
+ **Sinatra settings:** `:charset`
77
+ **Tag helpers:** `charset_tag`
78
+
79
+ It's best practice for all Web pages to declare their encoding -- even if the server _should_ be doing it, even if you only use ASCII, etc. etc. **Sinatra::Head** makes an opinionated choice and sets this to **utf-8** for you, but you can override it if you want:
80
+
81
+ set :charset, 'shift-jis'
82
+
83
+ You are of course responsible for making sure that the page output keeps any encoding promise you make. If you're using Ruby 1.9, it's a good idea to set the `Encoding.default_internal` value to utf-8 as well. Also note that using Sinatra's **content_type** helper will override this for any page by setting the HTTP headers directly, which take precedence over `<meta>` tags.
84
+
85
+ *A note on meta tags:* Someone is doubtless going to ask what happened to the _http-equiv_ or _content_ attributes. [This][5] is my short answer. If this fails to validate for any common use case, let me know.
86
+
87
+ Title
88
+ -----
89
+ **Sinatra settings:** `:title`, `:title_separator`
90
+ **Data helpers:** `title`, `title=`, `title_string`
91
+ **Tag helpers:** `title_tag`
92
+
93
+ Multi-part titles that describe the page, the category, and the site are standard practice for SEO and other reasons. **Sinatra::Head** manages this by setting up **title** as a Sinatra setting with an empty array and letting you add to it:
94
+
95
+ title << 'Site'
96
+ title << 'Page' # => 'Page | Site'
97
+
98
+ Note that the title chain unspools in LIFO order, not FIFO. (I.e., it's a stack, not a queue.)
99
+
100
+ If you want to blow away this chain for a single action, we do have a `title=` helper for the purpose. You can also change the separator from ` | ` to anything you want with the `:title_separator` setting.
101
+
102
+ Stylesheets
103
+ -----------
104
+ **Sinatra settings:** `:stylesheets`, `:stylesheet_path`
105
+ **Data helpers:** `stylesheets`, `expand_stylesheet_path`
106
+ **Tag helpers:** `stylesheet_tag`, `stylesheet_tags`
107
+
108
+ Like the _title_ setting, the _stylesheets_ setting begins life as an empty array. Thus, you can add sheets to it at any time. Simple filenames will have the *stylesheet_path* setting prepended for a consistent relative path (it defaults simply to _"stylesheets"_); hyperlinks beginning with `http:` or `https:` will not be touched.
109
+
110
+ stylesheets << 'main.css'
111
+ stylesheets << 'http://someothersite.org/popular_css_extension.css'
112
+
113
+ get '/blah' do
114
+ stylesheets << 'specific.css'
115
+ end
116
+
117
+ In your layout, you can call the `stylesheet_tag` helper for a single filename or URL you provide, or the `stylesheet_tags` helper which walks the array and creates a tag for each. (In FIFO or queue order, unlike _title._)
118
+
119
+ Currently nothing is done for nicer asset packaging, fingerprinting, compressing, etc. There's [Rack][10] [middleware][9] for some of it, and future iterations may include these features. If you'd really really like to see them, [create an issue][7] and ask for them.
120
+
121
+ Javascripts
122
+ -----------
123
+ **Sinatra settings:** `:javascripts`, `:javascript_path`
124
+ **Data helpers:** `javascripts`, `expand_javascript_path`
125
+ **Tag helpers:** `javascript_tag`, `javascript_tags`
126
+
127
+ These helpers are functionally very similar to the _stylesheets_ ones, allowing the addition of relative filenames (which will be expanded with the *javascript_path* setting) or full URLs. One wrinkle here is that you can _also_ include inline code:
128
+
129
+ javascripts << 'main.js'
130
+ javascripts << 'http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js'
131
+
132
+ get '/blah' do
133
+ javascripts << %q[
134
+ window.onload = function(){
135
+ some_silly_thing;
136
+ };]
137
+ end
138
+
139
+ Inline code is detected by the presence of a semicolon, and will be included as the body of the `<script>` tag rather than as the **src=** attribute. (So if you have semicolons in your scripts' filenames or URLs... _What were you thinking?_)
140
+
141
+ As noted above for stylesheets, right now there are no facilities to minify, compress, or recombobulate your Javascript. Also as noted, [feel free to ask][7] or contribute.
142
+
143
+ Credit, Support, and Contributions
144
+ ----------------------------------
145
+ This extension is the fault of **Stephen Eley**. You can reach me at <sfeley@gmail.com>. If you like science fiction stories, I know a [good podcast][6] for them as well.
146
+
147
+ If you find bugs, please report them on the [Github issue tracker][7].
148
+
149
+ The documentation can of course be found on [RDoc.info][8].
150
+
151
+ Contributions are welcome. This was a quick start to get me some features I needed in a complex stack of Sinatra apps. There's a lot more that _can_ be done on this, but I'll keep sniffing the wind to find out what _should_ be done.
152
+
153
+ License
154
+ -------
155
+ This project is licensed under the **Don't Be a Dick License**, version 0.2, and is copyright 2010 by Stephen Eley. See the [LICENSE.markdown][11] file or the [DBAD License site][12] for elaboration on not being a dick.
156
+
157
+
158
+ [1]: http://rvm.beginrescueend.com
159
+ [2]: https://sinatra.lighthouseapp.com/projects/9779/tickets/240-sinatrabase-vs-sinatradefault-vs-sinatraapplication
160
+ [3]: http://www.sinatrarb.com/extensions-wild.html
161
+ [4]: http://diveintohtml5.org/semantics.html
162
+ [5]: http://diveintohtml5.org/semantics.html#encoding
163
+ [6]: http://escapepod.org
164
+ [7]: http://github.com/SFEley/sinatra-head/issues
165
+ [8]: http://rdoc.info/projects/SFEley/sinatra-head
166
+ [9]: http://coderack.org/users/chriskottom/middlewares/66-rackdomainsprinkler
167
+ [10]: http://rack.rubyforge.org/doc/Rack/Static.html
168
+ [11]: http://github.com/SFEley/sinatra-head/blob/master/LICENSE.markdown
169
+ [12]: http://dbad-license.org
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "sinatra-head"
8
+ gem.summary = %Q{A Sinatra extension that gets inside your <HEAD>.}
9
+ gem.description = %Q{Sinatra::Head provides class methods and helpers for dynamically controlling the fields in your <HEAD> element that are usually preset in your layout: the page title, stylesheet and javascript assets, etc. Asset lists are inherited throughout superclasses, subclasses, and within action methods, and basic asset path management is supported.}
10
+ gem.email = "sfeley@gmail.com"
11
+ gem.homepage = "http://github.com/SFEley/sinatra-head"
12
+ gem.authors = ["Stephen Eley"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ gem.add_development_dependency "yard", ">= 0"
15
+ gem.add_development_dependency "haml", ">= 3"
16
+ gem.add_development_dependency "capybara", ">= 0.3.8"
17
+
18
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
19
+ end
20
+ Jeweler::GemcutterTasks.new
21
+ rescue LoadError
22
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
23
+ end
24
+
25
+ require 'spec/rake/spectask'
26
+ Spec::Rake::SpecTask.new(:spec) do |spec|
27
+ spec.libs << 'lib' << 'spec'
28
+ spec.spec_files = FileList['spec/**/*_spec.rb']
29
+ end
30
+
31
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
32
+ spec.libs << 'lib' << 'spec'
33
+ spec.pattern = 'spec/**/*_spec.rb'
34
+ spec.rcov = true
35
+ end
36
+
37
+ task :spec => :check_dependencies
38
+
39
+ task :default => :spec
40
+
41
+ begin
42
+ require 'yard'
43
+ YARD::Rake::YardocTask.new
44
+ rescue LoadError
45
+ task :yardoc do
46
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
47
+ end
48
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,39 @@
1
+ require 'sinatra/base'
2
+
3
+ require 'sinatra/head/data_helpers'
4
+ require 'sinatra/head/tag_helpers'
5
+
6
+ module Sinatra
7
+ module Head
8
+
9
+ # # Exposes the 'title' setting so that it can be overwritten or appended to.
10
+ # # @see DataHelpers#title
11
+ # def title
12
+ # settings.title
13
+ # end
14
+ #
15
+ # # Exposes the 'stylesheets' setting so that it can be overwritten or appended to.
16
+ # # @see DataHelpers#stylesheets
17
+ # def stylesheets
18
+ # settings.stylesheets
19
+ # end
20
+
21
+ def self.registered(app)
22
+ app.helpers DataHelpers
23
+ app.helpers TagHelpers
24
+
25
+ app.configure do
26
+ app.set :charset, 'utf-8'
27
+
28
+ app.set :title, []
29
+ app.set :title_separator, ' | '
30
+
31
+ app.set :stylesheets, []
32
+ app.set :stylesheet_path, '/stylesheets'
33
+
34
+ app.set :javascripts, []
35
+ app.set :javascript_path, '/javascript'
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,94 @@
1
+ module Sinatra
2
+ module Head
3
+
4
+ # Helpers intended to be used in your Sinatra actions rather than your views. (Yes, you can use them both places,
5
+ # but setting any of these in a view _after_ your layout's rendered the <head> element is sort of pointless.)
6
+ module DataHelpers
7
+ # Exposes an array of title elements that you can read or add to. Can be set at both the class and
8
+ # instance (action) level.
9
+ #
10
+ # @example Recommended usage
11
+ # class MyApp < Sinatra::Base
12
+ # register Sinatra::Head
13
+ # title << 'My Application'
14
+ #
15
+ # get '/people' do
16
+ # title << 'Directory'
17
+ # end
18
+ # end
19
+ #
20
+ # This will create a title by default that looks like:
21
+ # Directory | My Application
22
+ def title
23
+ @title ||= Array(settings.title.clone)
24
+ end
25
+
26
+ # Overrides any title elements you've already set. It's more conventional to use the << operator instead.
27
+ def title=(val)
28
+ @title = Array(val)
29
+ end
30
+
31
+ # Returns the title elements you've set, in reverse order, separated by ' | '. (You can override this with
32
+ # a Sinatra option: `set :title_separator, ' <*> '`)
33
+ def title_string
34
+ title.reverse.join(settings.title_separator)
35
+ end
36
+
37
+ # Exposes an array of stylesheets. Can be set at both the class and the instance (action) level.
38
+ # Full hyperlinks or relative filenames can be set; if relative, the final result will include the
39
+ # Sinatra stylesheet_path setting.
40
+ # @example Recommended usage
41
+ # class MyApp < Sinatra::Base
42
+ # register Sinatra::Head
43
+ # stylesheets << 'main.css'
44
+ # stylesheets << 'http://example.org/some_famous_grid_stylesheet.css'
45
+ #
46
+ # get '/form' do
47
+ # stylesheets << 'form.css'
48
+ # end
49
+ # end
50
+ def stylesheets
51
+ @stylesheets ||= Array(settings.stylesheets.clone)
52
+ end
53
+
54
+ # Returns the full path for a stylesheet's filename, with the Sinatra `stylesheet_path` setting
55
+ # prepending it. Full hyperlinks are not altered.
56
+ def expand_stylesheet_path(sheet)
57
+ if sheet =~ %r{^https?://}
58
+ sheet
59
+ else
60
+ File.join(settings.stylesheet_path, sheet)
61
+ end
62
+ end
63
+
64
+ # Exposes an array of javascript sources. Can be set at both the class and the instance (action) level.
65
+ # Full hyperlinks or relative filenames can be set; if relative, the final result will include the
66
+ # Sinatra stylesheet_path setting.
67
+ # @example Recommended usage
68
+ # class MyApp < Sinatra::Base
69
+ # register Sinatra::Head
70
+ # javascripts << 'main.js'
71
+ # javascripts << 'http://example.org/some_popular_script.js'
72
+ #
73
+ # get '/form' do
74
+ # javascripts << 'form.js'
75
+ # end
76
+ # end
77
+ def javascripts
78
+ @javascripts ||= Array(settings.javascripts.clone)
79
+ end
80
+
81
+ # Returns the full path for a javascript source's filename, with the Sinatra `javascript_path` setting
82
+ # prepending it. Full hyperlinks are not altered.
83
+ def expand_javascript_path(script)
84
+ if script =~ %r{^https?://}
85
+ script
86
+ else
87
+ File.join(settings.javascript_path, script)
88
+ end
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,72 @@
1
+ require 'sinatra/head/data_helpers'
2
+
3
+ # Helper methods
4
+ module Sinatra
5
+ module Head
6
+ # Methods intended for view or layout templates, to generate the usual set of metadata and include tags.
7
+ module TagHelpers
8
+
9
+ # Spits out a default <head> element which incorporates the <meta> and <title> elements as well as any given
10
+ # stylesheet or Javascript includes. This is a convenience; if there are other things you want in your head,
11
+ # there's no need to use this instead of making your own inside your layout.
12
+ def head_tag
13
+ <<-HEAD_TAG
14
+ <head>
15
+ #{charset_tag}
16
+ #{title_tag}
17
+ #{stylesheet_tags}
18
+ #{javascript_tags}
19
+ </head>
20
+ HEAD_TAG
21
+ end
22
+
23
+ # Spits out a <meta> tag with the named charset. Defaults to UTF-8, but you can override it
24
+ # with the Sinatra 'charset' setting.
25
+ def charset_tag
26
+ "<meta charset='#{settings.charset}' />"
27
+ end
28
+
29
+ # Spits out a <title> element with anything that's been added to the title by various actions.
30
+ # The title array is treated like a stack; items are popped off of it in reverse order of
31
+ # inclusion.
32
+ def title_tag
33
+ "<title>#{title_string}</title>"
34
+ end
35
+
36
+ # Spits out a <link rel='stylesheet'> element for the given stylesheet reference.
37
+ # Relative filenames will be expanded with the Sinatra assets path, if set.
38
+ #
39
+ # @param [String] sheet
40
+ def stylesheet_tag(sheet)
41
+ "<link rel='stylesheet' href='#{expand_stylesheet_path(sheet)}' />"
42
+ end
43
+
44
+ # Spits out stylesheet tags for all declared stylesheets, one per line.
45
+ def stylesheet_tags
46
+ stylesheets.collect{|s| stylesheet_tag(s)}.join("\n")
47
+ end
48
+
49
+ # Spits out a <script src='filename'> element for the given javascript file.
50
+ # Relative filenames will be expanded with the Sinatra assets path, if set.
51
+ #
52
+ # EXCEPTION: If an item in 'javascripts' contains a semicolon, it will be interpreted
53
+ # as Javascript source code instead of a filename, and will be included inline in the
54
+ # script tag instead of on the 'src' element.
55
+ #
56
+ # @param [String] script
57
+ def javascript_tag(script)
58
+ if script.include?(';')
59
+ "<script>\n#{script}\n</script>"
60
+ else
61
+ "<script src='#{expand_javascript_path(script)}'></script>"
62
+ end
63
+ end
64
+
65
+ # Spits out javascript tags for all declared scripts, one per line.
66
+ def javascript_tags
67
+ javascripts.collect{|s| javascript_tag(s)}.join("\n")
68
+ end
69
+
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,43 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Sinatra::Head do
4
+ include DummyFixture
5
+
6
+ before(:each) do
7
+ visit '/'
8
+ end
9
+
10
+ it "displays the head" do
11
+ page.should have_css('head')
12
+ end
13
+
14
+ it "has a title element" do
15
+ within 'head' do
16
+ page.should have_css('title')
17
+ end
18
+ end
19
+
20
+ it "has a meta tag for the charset" do
21
+ within 'head' do
22
+ page.should have_css("meta[charset='utf-8']")
23
+ end
24
+ end
25
+
26
+ it "lets you override the charset" do
27
+ class DummyGrandkid < DummyFixture::DummyChild
28
+ set :charset, 'iso-8859-1'
29
+ get '/new' do
30
+ title << 'Really Low Level'
31
+ haml "This is something new."
32
+ end
33
+ end
34
+ app DummyGrandkid
35
+
36
+ visit '/'
37
+ within 'head' do
38
+ page.should have_css("meta[charset='iso-8859-1']")
39
+ end
40
+ end
41
+
42
+
43
+ end
@@ -0,0 +1,67 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Sinatra::Head, "javascripts" do
4
+ include DummyFixture
5
+
6
+
7
+ class DummyFixture::DummyApp
8
+ javascripts << 'main.js'
9
+ end
10
+
11
+ class DummyFixture::DummyChild
12
+ javascripts << 'secondary.js'
13
+ set :javascript_path, '/things/javascript'
14
+ end
15
+
16
+ before(:each) do
17
+ app DummyFixture::DummyChild
18
+ @instance = app.new
19
+ end
20
+
21
+ it "can be appended to in a top-level class" do
22
+ @instance.javascripts.should include('main.js')
23
+ end
24
+
25
+ it "can be appended to in a subclass" do
26
+ @instance.javascripts.should include('secondary.js')
27
+ end
28
+
29
+ it "can be appended to in an instance" do
30
+ @instance.javascripts << 'specific.js'
31
+ @instance.javascripts.should == ['main.js', 'secondary.js', 'specific.js']
32
+ end
33
+
34
+
35
+ it "expands the assets path for relative filenames" do
36
+ @instance.expand_javascript_path(@instance.javascripts.first).should == '/things/javascript/main.js'
37
+ end
38
+
39
+ it "leaves fully qualified hyperlinks intact" do
40
+ @instance.javascripts << 'http://google.com/google_script.js'
41
+ @instance.expand_javascript_path(@instance.javascripts[2]).should == 'http://google.com/google_script.js'
42
+ end
43
+
44
+ it "knows how to make a tag" do
45
+ @instance.javascript_tag(@instance.javascripts.first).should == "<script src='/things/javascript/main.js'></script>"
46
+ end
47
+
48
+ it "can make a tag for inline code" do
49
+ @instance.javascripts << "window.onload=alert('This is a page!');"
50
+ @instance.javascript_tag(@instance.javascripts[2]).should == "<script>\nwindow.onload=alert('This is a page!');\n</script>"
51
+ end
52
+
53
+
54
+ it "knows how to make all the tags" do
55
+ @instance.javascripts << 'http://yahoo.com/yahoo_script.js'
56
+ @instance.javascript_tags.should == "<script src='/things/javascript/main.js'></script>\n<script src='/things/javascript/secondary.js'></script>\n<script src='http://yahoo.com/yahoo_script.js'></script>"
57
+ end
58
+
59
+ it "shows up in the header" do
60
+ visit '/'
61
+ within 'head' do
62
+ page.should have_css("script[src='/things/javascript/main.js']")
63
+ page.should have_css("script[src='/things/javascript/secondary.js']")
64
+ end
65
+ end
66
+
67
+ end
@@ -0,0 +1,62 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Sinatra::Head, "stylesheets" do
4
+ include DummyFixture
5
+
6
+
7
+ class DummyFixture::DummyApp
8
+ stylesheets << 'main.css'
9
+ end
10
+
11
+ class DummyFixture::DummyChild
12
+ stylesheets << 'secondary.css'
13
+ set :stylesheet_path, '/stuff/stylesheets'
14
+ end
15
+
16
+ before(:each) do
17
+ app DummyFixture::DummyChild
18
+ @instance = app.new
19
+ end
20
+
21
+ it "can be appended to in a top-level class" do
22
+ @instance.stylesheets.should include('main.css')
23
+ end
24
+
25
+ it "can be appended to in a subclass" do
26
+ @instance.stylesheets.should include('secondary.css')
27
+ end
28
+
29
+ it "can be appended to in an instance" do
30
+ @instance.stylesheets << 'specific.css'
31
+ @instance.stylesheets.should == ['main.css', 'secondary.css', 'specific.css']
32
+ end
33
+
34
+
35
+ it "expands the assets path for relative filenames" do
36
+ @instance.expand_stylesheet_path(@instance.stylesheets.first).should == '/stuff/stylesheets/main.css'
37
+ end
38
+
39
+ it "leaves fully qualified hyperlinks intact" do
40
+ @instance.stylesheets << 'http://google.com/google_style.css'
41
+ @instance.expand_stylesheet_path(@instance.stylesheets[2]).should == 'http://google.com/google_style.css'
42
+ end
43
+
44
+ it "knows how to make a tag" do
45
+ @instance.stylesheet_tag(@instance.stylesheets.first).should == "<link rel='stylesheet' href='/stuff/stylesheets/main.css' />"
46
+ end
47
+
48
+ it "knows how to make all the tags" do
49
+ @instance.stylesheets << 'http://yahoo.com/yahoo_style.css'
50
+ @instance.stylesheet_tags.should == "<link rel='stylesheet' href='/stuff/stylesheets/main.css' />\n<link rel='stylesheet' href='/stuff/stylesheets/secondary.css' />\n<link rel='stylesheet' href='http://yahoo.com/yahoo_style.css' />"
51
+ end
52
+
53
+ it "shows up in the header" do
54
+ visit '/'
55
+ within 'head' do
56
+ page.should have_css("link[rel='stylesheet'][href='/stuff/stylesheets/main.css']")
57
+ page.should have_css("link[rel='stylesheet'][href='/stuff/stylesheets/secondary.css']")
58
+ end
59
+
60
+ end
61
+
62
+ end
@@ -0,0 +1,68 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Sinatra::Head, "titles" do
4
+ include DummyFixture
5
+
6
+
7
+ class DummyFixture::DummyApp
8
+ title << 'High Level'
9
+ end
10
+
11
+ class DummyFixture::DummyChild
12
+ title << 'Mid-Level'
13
+ end
14
+
15
+ before(:each) do
16
+ @instance = app.new
17
+ end
18
+
19
+ it "can be appended to in a top-level class" do
20
+ @instance.title.should include('High Level')
21
+ end
22
+
23
+ it "can be appended to in a subclass" do
24
+ @instance.title.should include('Mid-Level')
25
+ end
26
+
27
+ it "can be appended to in an instance" do
28
+ @instance.title << 'Low Level'
29
+ @instance.title.should == ['High Level', 'Mid-Level', 'Low Level']
30
+ end
31
+
32
+ it "can be overridden" do
33
+ @instance.title = 'Kaboom!'
34
+ @instance.title.should == ['Kaboom!']
35
+ end
36
+
37
+ it "returns itself as a string" do
38
+ @instance.title << 'Low Level'
39
+ @instance.title_string.should == 'Low Level | Mid-Level | High Level'
40
+ end
41
+
42
+ it "can set a different title separator" do
43
+ class DummyGrandkid < DummyFixture::DummyChild
44
+ set :title_separator, ' <*> '
45
+ end
46
+
47
+ instance = DummyGrandkid.new
48
+ instance.title_string.should == 'Mid-Level <*> High Level'
49
+ end
50
+
51
+ it "knows how to make a tag" do
52
+ @instance.title_tag.should == '<title>Mid-Level | High Level</title>'
53
+ end
54
+
55
+ it "shows up in the title tag" do
56
+ class DummyGrandkid < DummyFixture::DummyChild
57
+ get '/new' do
58
+ title << 'Really Low Level'
59
+ haml "This is something new."
60
+ end
61
+ end
62
+ app DummyGrandkid
63
+
64
+ visit '/new'
65
+ page.locate('title').text.should == 'Really Low Level | Mid-Level | High Level'
66
+ end
67
+
68
+ end
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,27 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'rack/test'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+ require 'capybara'
7
+ require 'capybara/dsl'
8
+
9
+ require 'sinatra/head'
10
+ require 'support/dummy_app'
11
+
12
+ Spec::Runner.configure do |config|
13
+ include Rack::Test::Methods
14
+ include Capybara
15
+ Capybara.default_selector = :css
16
+ end
17
+
18
+ # Confirm our dummy app
19
+ describe "Dummy app" do
20
+ include DummyFixture
21
+
22
+ it "returns a page" do
23
+ app DummyApp
24
+ visit('/')
25
+ page.should have_content('Hello dummy')
26
+ end
27
+ end
@@ -0,0 +1,38 @@
1
+ require 'sinatra/base'
2
+
3
+ module DummyFixture
4
+ # A simple dummy app to perform our tests against. We make our templates out of strings so that we
5
+ # can change them easily in our tests.
6
+ class DummyApp < Sinatra::Base
7
+ set :environment, :test
8
+ enable :inline_templates
9
+
10
+ register Sinatra::Head
11
+
12
+ get "/" do
13
+ haml :index
14
+ end
15
+ end
16
+
17
+ class DummyChild < DummyApp
18
+ end
19
+
20
+ def app(klass=nil)
21
+ if klass
22
+ Capybara.app = klass
23
+ else
24
+ Capybara.app ||= DummyChild
25
+ end
26
+ end
27
+ end
28
+
29
+ __END__
30
+
31
+ @@ layout
32
+ %html
33
+ =head_tag
34
+ %body
35
+ = yield
36
+
37
+ @@ index
38
+ Hello dummy!
@@ -0,0 +1,5 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "title" do
4
+
5
+ end
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sinatra-head
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Stephen Eley
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-05-20 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rspec
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 13
30
+ segments:
31
+ - 1
32
+ - 2
33
+ - 9
34
+ version: 1.2.9
35
+ type: :development
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: yard
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 3
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ type: :development
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: haml
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 5
60
+ segments:
61
+ - 3
62
+ version: "3"
63
+ type: :development
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: capybara
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ - 3
77
+ - 8
78
+ version: 0.3.8
79
+ type: :development
80
+ version_requirements: *id004
81
+ description: "Sinatra::Head provides class methods and helpers for dynamically controlling the fields in your <HEAD> element that are usually preset in your layout: the page title, stylesheet and javascript assets, etc. Asset lists are inherited throughout superclasses, subclasses, and within action methods, and basic asset path management is supported."
82
+ email: sfeley@gmail.com
83
+ executables: []
84
+
85
+ extensions: []
86
+
87
+ extra_rdoc_files:
88
+ - LICENSE.markdown
89
+ - README.markdown
90
+ files:
91
+ - .document
92
+ - .gitignore
93
+ - LICENSE.markdown
94
+ - README.markdown
95
+ - Rakefile
96
+ - VERSION
97
+ - lib/sinatra/head.rb
98
+ - lib/sinatra/head/data_helpers.rb
99
+ - lib/sinatra/head/tag_helpers.rb
100
+ - spec/sinatra/head_spec.rb
101
+ - spec/sinatra/javascripts_spec.rb
102
+ - spec/sinatra/stylesheet_spec.rb
103
+ - spec/sinatra/title_spec.rb
104
+ - spec/spec.opts
105
+ - spec/spec_helper.rb
106
+ - spec/support/dummy_app.rb
107
+ - spec/title_spec.rb
108
+ has_rdoc: true
109
+ homepage: http://github.com/SFEley/sinatra-head
110
+ licenses: []
111
+
112
+ post_install_message:
113
+ rdoc_options:
114
+ - --charset=UTF-8
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ hash: 3
123
+ segments:
124
+ - 0
125
+ version: "0"
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ hash: 3
132
+ segments:
133
+ - 0
134
+ version: "0"
135
+ requirements: []
136
+
137
+ rubyforge_project:
138
+ rubygems_version: 1.3.7
139
+ signing_key:
140
+ specification_version: 3
141
+ summary: A Sinatra extension that gets inside your <HEAD>.
142
+ test_files:
143
+ - spec/sinatra/head_spec.rb
144
+ - spec/sinatra/javascripts_spec.rb
145
+ - spec/sinatra/stylesheet_spec.rb
146
+ - spec/sinatra/title_spec.rb
147
+ - spec/spec_helper.rb
148
+ - spec/support/dummy_app.rb
149
+ - spec/title_spec.rb