crystalmeta 0.9.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.
Files changed (55) hide show
  1. data/.gitignore +6 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +130 -0
  5. data/Rakefile +1 -0
  6. data/Readme.markdown +210 -0
  7. data/crystalmeta.gemspec +23 -0
  8. data/lib/crystal/controller_extensions.rb +19 -0
  9. data/lib/crystal/hash_with_stringify_keys.rb +52 -0
  10. data/lib/crystal/interpolates_tags.rb +19 -0
  11. data/lib/crystal/meta.rb +29 -0
  12. data/lib/crystal/options_for_controller.rb +52 -0
  13. data/lib/crystal/railtie.rb +17 -0
  14. data/lib/crystal/tag.rb +26 -0
  15. data/lib/crystal/tags.rb +52 -0
  16. data/lib/crystal/version.rb +3 -0
  17. data/lib/crystal/view_helpers.rb +13 -0
  18. data/lib/crystalmeta.rb +24 -0
  19. data/spec/dummy/Rakefile +7 -0
  20. data/spec/dummy/app/assets/javascripts/application.js +12 -0
  21. data/spec/dummy/app/assets/stylesheets/application.css +11 -0
  22. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  23. data/spec/dummy/app/controllers/movies_controller.rb +5 -0
  24. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  25. data/spec/dummy/app/mailers/.gitkeep +0 -0
  26. data/spec/dummy/app/models/.gitkeep +0 -0
  27. data/spec/dummy/app/views/layouts/application.html.erb +11 -0
  28. data/spec/dummy/app/views/movies/index.html.erb +1 -0
  29. data/spec/dummy/app/views/movies/show.html.erb +0 -0
  30. data/spec/dummy/config.ru +4 -0
  31. data/spec/dummy/config/application.rb +64 -0
  32. data/spec/dummy/config/boot.rb +10 -0
  33. data/spec/dummy/config/environment.rb +5 -0
  34. data/spec/dummy/config/environments/development.rb +37 -0
  35. data/spec/dummy/config/environments/production.rb +67 -0
  36. data/spec/dummy/config/environments/test.rb +37 -0
  37. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  38. data/spec/dummy/config/initializers/inflections.rb +15 -0
  39. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  40. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  41. data/spec/dummy/config/initializers/session_store.rb +8 -0
  42. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  43. data/spec/dummy/config/locales/en.yml +18 -0
  44. data/spec/dummy/config/routes.rb +3 -0
  45. data/spec/dummy/lib/assets/.gitkeep +0 -0
  46. data/spec/dummy/log/.gitkeep +0 -0
  47. data/spec/dummy/script/rails +6 -0
  48. data/spec/features/meta_tags_spec.rb +52 -0
  49. data/spec/spec_helper.rb +14 -0
  50. data/spec/support/tag.rb +3 -0
  51. data/spec/unit/hash_with_stringify_keys_spec.rb +31 -0
  52. data/spec/unit/meta_spec.rb +41 -0
  53. data/spec/unit/tag_spec.rb +31 -0
  54. data/spec/unit/tags_spec.rb +132 -0
  55. metadata +185 -0
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ .bundle/
2
+ pkg/
3
+ spec/dummy/db/*.sqlite3
4
+ spec/dummy/log/*.log
5
+ spec/dummy/tmp/
6
+ spec/dummy/.sass-cache
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in opengraphy.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,130 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ crystalmeta (0.9.0)
5
+ rails (>= 3.0.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ actionmailer (3.2.9)
11
+ actionpack (= 3.2.9)
12
+ mail (~> 2.4.4)
13
+ actionpack (3.2.9)
14
+ activemodel (= 3.2.9)
15
+ activesupport (= 3.2.9)
16
+ builder (~> 3.0.0)
17
+ erubis (~> 2.7.0)
18
+ journey (~> 1.0.4)
19
+ rack (~> 1.4.0)
20
+ rack-cache (~> 1.2)
21
+ rack-test (~> 0.6.1)
22
+ sprockets (~> 2.2.1)
23
+ activemodel (3.2.9)
24
+ activesupport (= 3.2.9)
25
+ builder (~> 3.0.0)
26
+ activerecord (3.2.9)
27
+ activemodel (= 3.2.9)
28
+ activesupport (= 3.2.9)
29
+ arel (~> 3.0.2)
30
+ tzinfo (~> 0.3.29)
31
+ activeresource (3.2.9)
32
+ activemodel (= 3.2.9)
33
+ activesupport (= 3.2.9)
34
+ activesupport (3.2.9)
35
+ i18n (~> 0.6)
36
+ multi_json (~> 1.0)
37
+ addressable (2.3.2)
38
+ arel (3.0.2)
39
+ builder (3.0.4)
40
+ capybara (2.0.1)
41
+ mime-types (>= 1.16)
42
+ nokogiri (>= 1.3.3)
43
+ rack (>= 1.0.0)
44
+ rack-test (>= 0.5.4)
45
+ selenium-webdriver (~> 2.0)
46
+ xpath (~> 1.0.0)
47
+ childprocess (0.3.6)
48
+ ffi (~> 1.0, >= 1.0.6)
49
+ diff-lcs (1.1.3)
50
+ erubis (2.7.0)
51
+ ffi (1.2.0)
52
+ hike (1.2.1)
53
+ i18n (0.6.1)
54
+ journey (1.0.4)
55
+ json (1.7.5)
56
+ libwebsocket (0.1.7.1)
57
+ addressable
58
+ websocket
59
+ mail (2.4.4)
60
+ i18n (>= 0.4.0)
61
+ mime-types (~> 1.16)
62
+ treetop (~> 1.4.8)
63
+ mime-types (1.19)
64
+ multi_json (1.5.0)
65
+ nokogiri (1.5.6)
66
+ polyglot (0.3.3)
67
+ rack (1.4.1)
68
+ rack-cache (1.2)
69
+ rack (>= 0.4)
70
+ rack-ssl (1.3.2)
71
+ rack
72
+ rack-test (0.6.2)
73
+ rack (>= 1.0)
74
+ rails (3.2.9)
75
+ actionmailer (= 3.2.9)
76
+ actionpack (= 3.2.9)
77
+ activerecord (= 3.2.9)
78
+ activeresource (= 3.2.9)
79
+ activesupport (= 3.2.9)
80
+ bundler (~> 1.0)
81
+ railties (= 3.2.9)
82
+ railties (3.2.9)
83
+ actionpack (= 3.2.9)
84
+ activesupport (= 3.2.9)
85
+ rack-ssl (~> 1.3.2)
86
+ rake (>= 0.8.7)
87
+ rdoc (~> 3.4)
88
+ thor (>= 0.14.6, < 2.0)
89
+ rake (10.0.3)
90
+ rdoc (3.12)
91
+ json (~> 1.4)
92
+ rspec-core (2.12.2)
93
+ rspec-expectations (2.12.1)
94
+ diff-lcs (~> 1.1.3)
95
+ rspec-mocks (2.12.1)
96
+ rspec-rails (2.12.0)
97
+ actionpack (>= 3.0)
98
+ activesupport (>= 3.0)
99
+ railties (>= 3.0)
100
+ rspec-core (~> 2.12.0)
101
+ rspec-expectations (~> 2.12.0)
102
+ rspec-mocks (~> 2.12.0)
103
+ rubyzip (0.9.9)
104
+ selenium-webdriver (2.27.2)
105
+ childprocess (>= 0.2.5)
106
+ libwebsocket (~> 0.1.3)
107
+ multi_json (~> 1.0)
108
+ rubyzip
109
+ sprockets (2.2.2)
110
+ hike (~> 1.2)
111
+ multi_json (~> 1.0)
112
+ rack (~> 1.0)
113
+ tilt (~> 1.1, != 1.3.0)
114
+ thor (0.16.0)
115
+ tilt (1.3.3)
116
+ treetop (1.4.12)
117
+ polyglot
118
+ polyglot (>= 0.3.1)
119
+ tzinfo (0.3.35)
120
+ websocket (1.0.6)
121
+ xpath (1.0.0)
122
+ nokogiri (~> 1.3)
123
+
124
+ PLATFORMS
125
+ ruby
126
+
127
+ DEPENDENCIES
128
+ capybara (~> 2.0.1)
129
+ crystalmeta!
130
+ rspec-rails (~> 2.12)
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/Readme.markdown ADDED
@@ -0,0 +1,210 @@
1
+ **Crystalmeta helps you control meta tags through I18n and/or manually. It plays well with [OpenGraph](http://ogp.me/).**
2
+
3
+ It gives you 3 helpers:
4
+
5
+ **1.** `meta(options)` — available in both controller & views.
6
+
7
+ Accepts `Hash`.
8
+
9
+ Collects meta tags for future use:
10
+
11
+ ```ruby
12
+ # folded hashes
13
+ meta og: {title: "The Rock (1996)", type: "video.movie"}
14
+
15
+ # one level hash
16
+ meta "og:title" => "The Rock (1996)", "og:type" => "video.movie"
17
+ meta :"og:image" => "the-rock.png"
18
+ ```
19
+
20
+ **2.** `meta_tag(name)` — available in views
21
+
22
+ Accepts `String` or `Symbol`.
23
+
24
+ Returns value for a certain tag:
25
+
26
+ ```erb
27
+ <title><%= meta_tag 'og:title' %></title>
28
+ ```
29
+
30
+ **3.** `meta_tags(pattern = //)`
31
+
32
+ Returns all meta tags which names match the pattern. Under the hoods it uses the three-qual to pattern match (like in `Enumerable#grep`).
33
+
34
+ ```erb
35
+ <%= meta_tags %>
36
+ ```
37
+
38
+ displays in this case:
39
+
40
+ ```html
41
+ <meta property="og:url" content="http://www.imdb.com/title/tt0117500/" />
42
+ <meta property="og:image" content="/assets/the-rock.png" />
43
+ <meta property="og:title" content="The Rock (1996)" />
44
+ <meta property="og:type" content="video.movie" />
45
+ ```
46
+
47
+ Please note that:
48
+
49
+ * `image_path` is automatically used for `og:image` or `og:image:url` (`video_path` for `og:video` and `audio_path` for `og:audio` respectively), so you don't have to bother setting explicit paths;
50
+ * `og:url` is set to `controller.request.url`.
51
+
52
+ ## I18n translations
53
+
54
+ Crystalmeta tries to fetch translations and merge them in this order (consider an example for `pages#show` action):
55
+
56
+ * `meta._defaults` — application defaults (lowest-priority)
57
+ * `meta.pages._defaults` — controller defaults
58
+ * `meta.pages.show` — action
59
+
60
+ Tags set manually by the `meta` helper will be given the highest priority.
61
+
62
+ **Namespaced controllers**
63
+
64
+ Crystalmeta uses `controller_path` in order to get the translation scope, so if you have a namespaced controller, for instance `Library::BooksController`, then the `show` action will use: `meta.library.books._defaults` & `meta.library.books.show` respectively.
65
+
66
+ **Aliases**
67
+
68
+ Crystalmeta uses translations for the `edit` action for `update`:
69
+
70
+ * `meta._defaults`
71
+ * `meta.movies._defaults`
72
+ * `meta.movies.edit`
73
+ * `meta.movies.update`
74
+
75
+ And `new` for `create`:
76
+
77
+ * `meta._defaults`
78
+ * `meta.movies._defaults`
79
+ * `meta.movies.new`
80
+ * `meta.movies.create`
81
+
82
+ You can omit defining translations for `update` & `create` actions if they're the same.
83
+
84
+ ## Displaying only certain meta tags using a pattern
85
+
86
+ Usually I set window title with Crystalmeta separately (I call it `head` for example) and I don't want to display it in meta tags, so I can do:
87
+
88
+ ```erb
89
+ <%= meta_tags /:/ %>
90
+ ```
91
+
92
+ It will filter tag names using `/:/ === name`.
93
+
94
+ You may wish to use a `Proc` in some cases:
95
+
96
+ ```erb
97
+ <%= meta_tags Proc.new{|name| name != "head"} %>
98
+ ```
99
+
100
+ ## Interpolation
101
+
102
+ You can interpolate meta tags like this:
103
+
104
+ ```erb
105
+ <% meta({
106
+ "og:title" => "The Rock (1996)",
107
+ "og:site_name" => "IMDb",
108
+ "head" => "%{og:title} — %{og:site_name}"
109
+ }) %>
110
+ ```
111
+
112
+ `meta_tag :head` will return `"The Rock (1996) — IMDb"`.
113
+
114
+ Please use this feature with care to avoid [SystemStackError](http://ruby-doc.org/core-1.9.3/SystemStackError.html).
115
+
116
+ ## Caveats
117
+
118
+ **Merging tags**
119
+
120
+ When tags are merged (while fetching translations or collecting with `meta`) Crystalmeta doesn't do anything smart — it just merges hashes brutally. So if you defined a tag like that:
121
+
122
+ ```yml
123
+ en:
124
+ meta:
125
+ _defaults:
126
+ og:
127
+ title: IMDb
128
+ ```
129
+
130
+ and then you redefine it in different style:
131
+
132
+ ```ruby
133
+ meta 'og:title' => 'The Rock (1996)'
134
+ ```
135
+
136
+ then you'll just get 2 tags in the end:
137
+
138
+ ```erb
139
+ <%= meta_tags /og:title/ %>
140
+ ```
141
+
142
+ will display
143
+
144
+ ```html
145
+ <meta property="og:title" content="IMDb" />
146
+ <meta property="og:title" content="Newer IMDb" />
147
+ ```
148
+
149
+ So you'd better prefer one style.
150
+
151
+ **Date/Time**
152
+
153
+ Crystalmeta will convert Date/Time/Datetime objects to [iso8601](http://en.wikipedia.org/wiki/ISO_8601) automatically, so if you don't want it, pass them as strings.
154
+
155
+ **Arrays**
156
+
157
+ To maximize [OpenGraph](http://ogp.me/#array) compatibility, Crystalmeta handles Arrays in similar way:
158
+
159
+ ```erb
160
+ <% meta :og => {:image => %w{1.png 2.png}} %>
161
+ <%= meta_tags /og:image/ %>
162
+ ```
163
+
164
+ will display:
165
+
166
+ ```html
167
+ <meta property="og:image" content="/assets/1.png" />
168
+ <meta property="og:image" content="/assets/2.png" />
169
+ ```
170
+
171
+ **Multiple images with properties**
172
+
173
+ If you have multiple images with properties like this:
174
+
175
+ ```html
176
+ <meta property="og:image:url" content="/assets/rock.jpg" />
177
+ <meta property="og:image:width" content="300" />
178
+ <meta property="og:image:height" content="300" />
179
+ <meta property="og:image:url" content="/assets/rock2.jpg" />
180
+ <meta property="og:image:url" content="/assets/rock3.jpg" />
181
+ <meta property="og:image:height" content="1000" />
182
+ ```
183
+
184
+ then you'll have to set them in a special way:
185
+
186
+ ```erb
187
+ <% meta 'og:image' => [
188
+ {:url => 'rock.jpg', :width => 300, :height => 300},
189
+ {:url => 'rock2.jpg'},
190
+ {:url => 'rock3.jpg', :height => 1000},
191
+ ] %>
192
+ ```
193
+
194
+ or:
195
+
196
+ ```yml
197
+ en:
198
+ meta:
199
+ pages:
200
+ show:
201
+ og:image:
202
+ - url: rock.jpg
203
+ width: 300
204
+ height: 300
205
+ - url: rock2.jpg
206
+ - url: rock3.jpg
207
+ height: 1000
208
+ ```
209
+
210
+ It will sort keys so that the `url` property is on top.
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'crystal/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "crystalmeta"
8
+ gem.version = Crystal::VERSION
9
+ gem.authors = ["Max Savchenko"]
10
+ gem.email = ["robotector@gmail.com"]
11
+ gem.description = %q{Crystal clean meta tags for Rails applications. Support I18n. Perfect for OpenGraph.}
12
+ gem.summary = %q{Crystalmeta helps you control meta tags through I18n and/or manually. It plays well with OpenGraph.}
13
+ gem.homepage = "http://github.com/macovsky/crystalmeta"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency("rails", ">= 3.0.0")
21
+ gem.add_development_dependency 'rspec-rails', '~> 2.12'
22
+ gem.add_development_dependency 'capybara', '~> 2.0.1'
23
+ end
@@ -0,0 +1,19 @@
1
+ module Crystal
2
+ module ControllerExtensions
3
+ def self.included(base)
4
+ base.before_filter :_set_meta
5
+ base.helper_method :meta
6
+ end
7
+
8
+ def meta(options = {})
9
+ options.present? && @_meta.store(options)
10
+ @_meta
11
+ end
12
+
13
+ protected
14
+
15
+ def _set_meta
16
+ @_meta = Meta.new(OptionsForController.new(self).options)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,52 @@
1
+ module Crystal
2
+ class HashWithStringifyKeys < Hash
3
+ def initialize(constructor = {})
4
+ if constructor.is_a?(Hash)
5
+ super()
6
+ update(constructor).stringify_keys!
7
+ else
8
+ super(constructor)
9
+ end
10
+ end
11
+
12
+ # https://github.com/intridea/hashie/blob/master/lib/hashie/extensions/key_conversion.rb
13
+
14
+ # Convert all keys in the hash to strings.
15
+ # @example
16
+ # test = {:abc => 'def'}
17
+ # test.stringify_keys!
18
+ # test # => {'abc' => 'def'}
19
+ def stringify_keys!
20
+ keys.each do |k|
21
+ stringify_keys_recursively!(self[k])
22
+ self[k.to_s] = self.delete(k)
23
+ end
24
+ self
25
+ end
26
+
27
+ # Return a new hash with all keys converted
28
+ # to strings.
29
+ def stringify_keys
30
+ dup.stringify_keys!
31
+ end
32
+
33
+ protected
34
+
35
+ # Stringify all keys recursively within nested
36
+ # hashes and arrays.
37
+ def stringify_keys_recursively!(object)
38
+ if self.class === object
39
+ object.stringify_keys!
40
+ elsif ::Array === object
41
+ object.each do |i|
42
+ stringify_keys_recursively!(i)
43
+ end
44
+ object
45
+ elsif object.respond_to?(:stringify_keys!)
46
+ object.stringify_keys!
47
+ else
48
+ object
49
+ end
50
+ end
51
+ end
52
+ end