galakei 0.3.8 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -10,4 +10,6 @@ group :development, :test do
10
10
  gem 'sqlite3'
11
11
  gem 'rspec-rails'
12
12
  gem 'haml'
13
+ gem 'css_parser'
14
+ gem 'fakeweb'
13
15
  end
data/README.md CHANGED
@@ -2,7 +2,27 @@
2
2
 
3
3
  [Japanese feature phones](http://www.mobalean.com/en/keitai_web_technology_guide) (a.k.a., keitai, galakei) have a number of restrictions over normal web browsers. This library adds support for them.
4
4
 
5
- ### Goals
5
+ ## Goals
6
6
 
7
7
  * Provide support for 3G handsets from the major 3 carriers in Japan (docomo, au, SoftBank)
8
8
  * Avoid modifying Rails internals as much as possible
9
+
10
+ ## Examples
11
+
12
+ ### Inlining Styles
13
+
14
+ Old docomo handsets [don't support external stylesheets](http://www.keitai-dev.net/CSS). Additionally, only very limited CSS is supported. galakei/docomo_css automatically inlines CSS and manipulates markup to overcome these limitations.
15
+
16
+ # css file
17
+ h1 { color: red; background-color: blue}
18
+
19
+ # source html
20
+ <h1>Foo</h1>
21
+
22
+ # outputted html
23
+ <div style="background-color: blue;"><h1><span style="color:red;">Foo</span></h1>/div>
24
+
25
+ ## Thanks
26
+
27
+ * To [jpmobile](https://github.com/jpmobile/jpmobile) for offering the most mature Rails plugin for Rails
28
+ * To [docomo_css](https://github.com/milk1000cc/docomo_css) for providing the inspiration for galakei/docomo_css
data/galakei.gemspec CHANGED
@@ -17,8 +17,7 @@ Gem::Specification.new do |s|
17
17
 
18
18
  s.add_dependency 'actionpack', '>= 3.0.3'
19
19
  s.add_dependency 'rack', '>= 1.2.1'
20
- s.add_dependency 'docomo_css', '~> 0.4.4'
21
-
20
+ s.add_dependency 'css_parser'
22
21
 
23
22
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
23
  end
@@ -0,0 +1,23 @@
1
+ # encoding: UTF-8
2
+ require 'css_parser'
3
+
4
+ class Galakei::DocomoCss::InlineStylesheet
5
+ def self.filter(controller)
6
+ return unless controller.request.imode_browser_1_0?
7
+ doc = Nokogiri::HTML(controller.response.body)
8
+ stylesheets = doc.xpath('//link[@rel="stylesheet"]')
9
+ return if stylesheets.empty?
10
+ stylesheets.each do |e|
11
+ e.unlink
12
+ parser = CssParser::Parser.new
13
+ uri = URI.parse(e['href'])
14
+ uri.scheme = controller.request.scheme unless uri.scheme
15
+ uri.host = controller.request.host unless uri.host
16
+ uri.port = controller.request.port unless uri.port
17
+ parser.load_uri!(uri)
18
+ stylesheet = Galakei::DocomoCss::Stylesheet.new(parser)
19
+ stylesheet.apply(doc)
20
+ end
21
+ controller.response.body = doc.to_xhtml(:encoding => doc.encoding)
22
+ end
23
+ end
@@ -0,0 +1,11 @@
1
+ module Galakei
2
+ module DocomoCss
3
+ class Railtie < Rails::Railtie
4
+ initializer "galakei.docomo_css" do |app|
5
+ ActiveSupport.on_load :action_controller do
6
+ after_filter Galakei::DocomoCss::InlineStylesheet
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,70 @@
1
+ require "css_parser"
2
+
3
+ class Galakei::DocomoCss::Stylesheet
4
+ def initialize(parsed_stylesheet)
5
+ @parsed_stylesheet = parsed_stylesheet
6
+ end
7
+
8
+ def apply(doc)
9
+ pseudo_styles = []
10
+ @parsed_stylesheet.each_rule_set do |ruleset|
11
+ ruleset.each_selector do |selector, declarations_string, specificity|
12
+ if selector =~ /a:(link|focus|visited)/
13
+ pseudo_styles << "#{selector} { #{declarations_string} }"
14
+ else
15
+ embed_style(doc, ruleset, selector)
16
+ end
17
+ end
18
+ end
19
+ unless pseudo_styles.empty?
20
+ doc.at("/html/head").add_child(<<-EOD)
21
+ <style type="text/css">
22
+ <![CDATA[
23
+ #{pseudo_styles.join("\n")}
24
+ ]]>
25
+ </style>
26
+ EOD
27
+ end
28
+ doc
29
+ end
30
+
31
+ private
32
+
33
+ def merge_style(e, s)
34
+ if e["style"]
35
+ e['style'] += ";" unless e['style'] =~ /;\Z/
36
+ e['style'] += s
37
+ else
38
+ e["style"] = s
39
+ end
40
+ end
41
+
42
+ def wrap_all_children(e, s)
43
+ new_parent = e.document.parse(s).first
44
+ e.children.each {|f| new_parent.add_child(f)}
45
+ e.add_child(new_parent)
46
+ end
47
+
48
+ def embed_style(doc, ruleset, selector)
49
+ doc.css(selector).each do |e|
50
+ ruleset.each_declaration do |property, value, is_important|
51
+ s = "#{property}: #{value};"
52
+ if selector =~ /^(h\d|p)[^\s]*$/
53
+ if %w[color font-size].include?(property)
54
+ wrap_all_children(e, '<span>')
55
+ merge_style(e.children.first, s)
56
+ elsif %w[background-color].include?(property)
57
+ div = Nokogiri.make("<div>")
58
+ merge_style(div, s)
59
+ e.replace(div)
60
+ div.add_child(e)
61
+ else
62
+ merge_style(e, s)
63
+ end
64
+ else
65
+ merge_style(e, s)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,4 @@
1
+ module Galakei::DocomoCss
2
+ autoload :Stylesheet, "galakei/docomo_css/stylesheet"
3
+ autoload :InlineStylesheet, "galakei/docomo_css/inline_stylesheet"
4
+ end
@@ -1,3 +1,4 @@
1
+ require 'active_support/core_ext/string/output_safety'
1
2
  module Galakei
2
3
  class EmojiTable
3
4
  MAPPING = {
@@ -22,10 +23,24 @@ module Galakei
22
23
  :sparkle => %w[2747 E6FA E46C E32E],
23
24
  :copyright_sign => %w[00A9 E731 E558 E24E],
24
25
  :registered_sign => %w[00AE E736 E559 E24F],
25
- :trade_mark_sign => %w[2122 E732 E54E E537]
26
+ :trade_mark_sign => %w[2122 E732 E54E E537],
27
+ :hash_key => [ %w[0023 20E3] ] + %w[E6E0 EB84 E210],
28
+ :keycap_1 => [ %w[0031 20E3] ] + %w[E6E2 E522 E21C],
29
+ :keycap_2 => [ %w[0032 20E3] ] + %w[E6E3 E523 E21D],
30
+ :keycap_3 => [ %w[0033 20E3] ] + %w[E6E4 E524 E21E],
31
+ :keycap_4 => [ %w[0034 20E3] ] + %w[E6E5 E525 E21F],
32
+ :keycap_5 => [ %w[0035 20E3] ] + %w[E6E6 E526 E220],
33
+ :keycap_6 => [ %w[0036 20E3] ] + %w[E6E7 E527 E221],
34
+ :keycap_7 => [ %w[0037 20E3] ] + %w[E6E8 E528 E222],
35
+ :keycap_8 => [ %w[0040 20E3] ] + %w[E6E9 E529 E223],
36
+ :keycap_9 => [ %w[0041 20E3] ] + %w[E6EA E52A E224],
37
+ :keycap_0 => [ %w[0030 20E3] ] + %w[E6EB E52C E225],
26
38
  }
27
39
  MAPPING.each do |k,v|
28
- MAPPING[k] = v.map {|s| "&#x#{s};".html_safe}
40
+ MAPPING[k] = v.map do |a|
41
+ a = [ a ] if a.is_a?(String)
42
+ a.map {|s| "&#x#{s};"}.join.html_safe
43
+ end
29
44
  define_method k do
30
45
  MAPPING[k][@carrier]
31
46
  end
@@ -1,12 +1,9 @@
1
- require 'docomo_css'
2
-
3
1
  module Galakei
4
2
  class Railtie < Rails::Railtie
5
3
  config.galakei = ActiveSupport::OrderedOptions.new
6
- initializer "galakei.extend.action_controller", :after => "docomo_css.extend.action_controller" do |app|
4
+ initializer "galakei.extend.action_controller" do |app|
7
5
  ActiveSupport.on_load :action_controller do
8
6
  include Galakei::HelperMethods
9
- docomo_filter
10
7
  filters = %w[Views ContentType]
11
8
  filters << :Haml if defined?(Haml)
12
9
  filters.each {|f| Galakei::Filter.const_get(f).inject(self) }
@@ -19,3 +16,4 @@ module Galakei
19
16
  end
20
17
 
21
18
  require 'galakei/session_id_parameter/railtie'
19
+ require 'galakei/docomo_css/railtie'
@@ -1,5 +1,3 @@
1
- require 'docomo_css'
2
-
3
1
  module Galakei
4
2
  module SessionIdParameter
5
3
  class Railtie < Rails::Railtie
@@ -1,3 +1,3 @@
1
1
  module Galakei
2
- VERSION = "0.3.8"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/galakei.rb CHANGED
@@ -1,9 +1,9 @@
1
1
  if defined?(Rails)
2
2
  require 'galakei/railtie'
3
3
  require 'galakei/use_rack_request_to_extract_sid'
4
- require 'docomo_css/railtie'
5
4
  end
6
5
  require 'galakei/request'
6
+ require 'galakei/docomo_css'
7
7
 
8
8
  module Galakei
9
9
  autoload :Email, "galakei/email"
@@ -1,4 +1,8 @@
1
+ !!! XML
2
+ !!! Mobile
1
3
  %html
2
4
  %head
5
+ %meta{"http-equiv" => "Content-Type", :content => "application/xhtml+xml;charset=UTF-8"}
6
+ = yield :head if content_for?(:head)
3
7
  %body
4
8
  = yield
@@ -0,0 +1,55 @@
1
+ # encoding: UTF-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/acceptance_helper')
3
+ require 'fakeweb'
4
+
5
+ class DocomoCssController < ApplicationController
6
+ def simple
7
+ html = <<-EOD
8
+ <% content_for(:head, stylesheet_link_tag("docomo_css/simple.css")) %>
9
+ <span>color</span>
10
+ EOD
11
+ render :inline => html, :layout => true
12
+ end
13
+
14
+ def external
15
+ html = <<-EOD
16
+ <% content_for(:head, stylesheet_link_tag("http://www.galakei.com/external.css")) %>
17
+ <span>color</span>
18
+ EOD
19
+ render :inline => html, :layout => true
20
+ end
21
+
22
+ def japanese
23
+ html = <<-EOD
24
+ <% content_for(:head, stylesheet_link_tag("docomo_css/simple.css")) %>
25
+ ほげ
26
+ EOD
27
+ render :inline => html, :layout => true
28
+ end
29
+ end
30
+
31
+ feature 'inlining of css' do
32
+ scenario 'requesting simple page for docomo', :driver => :docomo do
33
+ FakeWeb.register_uri(:get, 'http://www.example.com/stylesheets/docomo_css/simple.css', :body => "span { color: red }")
34
+ visit '/docomo_css/simple'
35
+ find("span")["style"].should == "color: red;"
36
+ page.should_not have_xpath("//link")
37
+ end
38
+ scenario 'requesting external page for docomo', :driver => :docomo do
39
+ FakeWeb.register_uri(:get, 'http://www.galakei.com/external.css', :body => "span { color: red }")
40
+ visit '/docomo_css/external'
41
+ find("span")["style"].should == "color: red;"
42
+ page.should_not have_xpath("//link")
43
+ end
44
+ %w[au softbank docomo_2_0].each do |carrier|
45
+ scenario "requesting simple page for #{carrier}", :driver => carrier.to_sym do
46
+ visit '/docomo_css/simple'
47
+ find("span")["style"].should be_nil
48
+ end
49
+ end
50
+
51
+ scenario 'response contains non-ascii', :driver => :docomo do
52
+ visit '/docomo_css/japanese'
53
+ page.body.should include("ほげ")
54
+ end
55
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: UTF-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/acceptance_helper')
3
+
4
+ class HamlController < ApplicationController
5
+ end
6
+
7
+ feature 'haml' do
8
+ %w[softbank au docomo].each do |s|
9
+ scenario "for #{s}", :driver => s.to_sym do
10
+ visit '/haml'
11
+ page.body.should include('<br />')
12
+ page.should have_css('br')
13
+ end
14
+ end
15
+ end
@@ -19,7 +19,6 @@ class HandsetDetectionController < ApplicationController
19
19
  end
20
20
  end
21
21
 
22
-
23
22
  feature 'handset detection' do
24
23
  scenario 'for docomo', :driver => :docomo do
25
24
  visit '/handset_detection'
@@ -13,6 +13,10 @@ Capybara.register_driver :docomo do |app|
13
13
  Capybara::Driver::RackTestWithUserAgent.new(app, "DoCoMo/2.0 P903i(c100;TB;W24H12)")
14
14
  end
15
15
 
16
+ Capybara.register_driver :docomo_2_0 do |app|
17
+ Capybara::Driver::RackTestWithUserAgent.new(app, "DoCoMo/2.0 SH06A3(c500;TB;W24H14)")
18
+ end
19
+
16
20
  Capybara.register_driver :au do |app|
17
21
  Capybara::Driver::RackTestWithUserAgent.new(app, "KDDI-CA32 UP.Browser/6.2.0.7.3.129 (GUI) MMP/2.0")
18
22
  end
@@ -0,0 +1,13 @@
1
+ # encoding: UTF-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/acceptance_helper')
3
+
4
+ class ViewsController < ApplicationController
5
+ def index
6
+ end
7
+ end
8
+
9
+ feature 'View path appending' do
10
+ scenario 'for docomo', :driver => :docomo do
11
+ visit '/views'
12
+ end
13
+ end
@@ -0,0 +1,140 @@
1
+ require 'spec_helper'
2
+ require "nokogiri"
3
+
4
+ describe Galakei::DocomoCss::Stylesheet do
5
+ context "simple stylesheet" do
6
+ before do
7
+ parser = CssParser::Parser.new
8
+ parser.add_block!(<<-EOD)
9
+ span {
10
+ color: red;
11
+ }
12
+ EOD
13
+ @stylesheet = described_class.new(parser)
14
+ end
15
+ it "should apply style to matching element" do
16
+ doc = Nokogiri::HTML.fragment("<span>foo</span>")
17
+ @stylesheet.apply(doc)
18
+ doc.to_s.should == %q{<span style="color: red;">foo</span>}
19
+ end
20
+ it "should not apply style to non-matching element" do
21
+ doc = Nokogiri::HTML.fragment("<p>foo</p>")
22
+ @stylesheet.apply(doc)
23
+ doc.to_s.should == %q{<p>foo</p>}
24
+ end
25
+ end
26
+
27
+ context "stylesheet with multiple styles" do
28
+ before do
29
+ parser = CssParser::Parser.new
30
+ parser.add_block!(<<-EOD)
31
+ div {
32
+ background-color: red;
33
+ }
34
+
35
+ .alC {
36
+ text-align: center
37
+ }
38
+ EOD
39
+ @stylesheet = described_class.new(parser)
40
+ end
41
+
42
+ it "should apply style to element that matches one style" do
43
+ doc = Nokogiri::HTML.fragment("<div class='alC'>foo</span>")
44
+ @stylesheet.apply(doc)
45
+ doc.to_s.should == %q{<div class="alC" style="background-color: red;text-align: center;">foo</div>}
46
+ end
47
+ end
48
+ context "stylesheet with pseudo style" do
49
+ before do
50
+ parser = CssParser::Parser.new
51
+ parser.add_block!(<<-EOD)
52
+ a:link { color: red; }
53
+ a:focus { color: green; }
54
+ a:visited { color: blue; }
55
+ EOD
56
+ @stylesheet = described_class.new(parser)
57
+ end
58
+
59
+ it "should add to head" do
60
+ doc = Nokogiri::HTML(<<-EOD)
61
+ <html>
62
+ <head></head>
63
+ <body><a href="/">foo</a></body>
64
+ </html>
65
+ EOD
66
+ @stylesheet.apply(doc)
67
+ doc.at("//a").to_s.should == %q{<a href="/">foo</a>}
68
+ expected = <<-EOD
69
+ <style type="text/css">
70
+ <![CDATA[
71
+ a:link { color: red; }
72
+ a:focus { color: green; }
73
+ a:visited { color: blue; }
74
+ ]]>
75
+ </style>
76
+ EOD
77
+ doc.at("/html/head/style").to_s.strip.should == expected.strip
78
+ end
79
+ end
80
+
81
+ ((1..6).map {|i| "h#{i}"} + %w[p]).each do |tag|
82
+ context "style applied to #{tag}" do
83
+ before do
84
+ parser = CssParser::Parser.new
85
+ parser.add_block!(<<-EOD)
86
+ #{tag}.color { color: red; }
87
+ #{tag}.fontsize { font-size: x-small; }
88
+ #{tag}.backgroundcolor { background-color: blue; }
89
+ EOD
90
+ @stylesheet = described_class.new(parser)
91
+ end
92
+
93
+ it "should wrap children in span for color" do
94
+ doc = Nokogiri::HTML("<#{tag} class='color'>foo</#{tag}>")
95
+ @stylesheet.apply(doc)
96
+ doc.at("//#{tag}").to_s.should == %Q{<#{tag} class="color"><span style="color: red;">foo</span></#{tag}>}
97
+ end
98
+
99
+ it "should wrap children in span for font-size" do
100
+ doc = Nokogiri::HTML("<#{tag} class='fontsize'>foo</#{tag}>")
101
+ @stylesheet.apply(doc)
102
+ doc.at("//#{tag}").to_s.should == %Q{<#{tag} class="fontsize"><span style="font-size: x-small;">foo</span></#{tag}>}
103
+ end
104
+
105
+ it "should wrap multiple children in single span" do
106
+ doc = Nokogiri::HTML("<#{tag} class='fontsize'>foo<br />bar</#{tag}>")
107
+ @stylesheet.apply(doc)
108
+ doc.at("//#{tag}").to_s.should == %Q{<#{tag} class="fontsize"><span style="font-size: x-small;">foo<br>bar</span></#{tag}>}
109
+ end
110
+
111
+ it "should wrap element in div for background-color" do
112
+ doc = Nokogiri::HTML("<#{tag} class='backgroundcolor'>foo</#{tag}>")
113
+ @stylesheet.apply(doc)
114
+ doc.at("//div").to_s.should == %Q{<div style="background-color: blue;"><#{tag} class="backgroundcolor">foo</#{tag}></div>}
115
+ end
116
+ end
117
+ end
118
+
119
+ context "style applied to child of h1" do
120
+ before do
121
+ parser = CssParser::Parser.new
122
+ parser.add_block!(<<-EOD)
123
+ h1 span { color: red; }
124
+ EOD
125
+ @stylesheet = described_class.new(parser)
126
+ end
127
+
128
+ it "should not apply style to single h1" do
129
+ doc = Nokogiri::HTML("<h1>foo</h1>")
130
+ @stylesheet.apply(doc)
131
+ doc.at("//h1").to_s.should == %q{<h1>foo</h1>}
132
+ end
133
+
134
+ it "should apply style to neseted element" do
135
+ doc = Nokogiri::HTML("<h1><span>foo</span></h1>")
136
+ @stylesheet.apply(doc)
137
+ doc.at("//h1").to_s.should == %q{<h1><span style="color: red;">foo</span></h1>}
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe Galakei::EmojiTable do
4
+ before do
5
+ @unicode, @docomo, @softbank, @au = %w[unicode docomo softbank au].map {|s| Galakei::EmojiTable.send(s)}
6
+ end
7
+
8
+ it "should handle single-character unicode" do
9
+ @unicode.black_sun_with_rays.should == "&#x2600;"
10
+ @docomo.black_sun_with_rays.should == "&#xE63E;"
11
+ @softbank.black_sun_with_rays.should == "&#xE04A;"
12
+ @au.black_sun_with_rays.should == "&#xE488;"
13
+ end
14
+
15
+ it "should handle multi-character unicode" do
16
+ @unicode.hash_key.should == "&#x0023;&#x20E3;"
17
+ @docomo.hash_key.should == "&#xE6E0;"
18
+ end
19
+ end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 3
8
- - 8
9
- version: 0.3.8
7
+ - 4
8
+ - 0
9
+ version: 0.4.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Paul McMahon
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-03-24 00:00:00 +09:00
18
+ date: 2011-04-05 00:00:00 +09:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -49,18 +49,16 @@ dependencies:
49
49
  type: :runtime
50
50
  version_requirements: *id002
51
51
  - !ruby/object:Gem::Dependency
52
- name: docomo_css
52
+ name: css_parser
53
53
  prerelease: false
54
54
  requirement: &id003 !ruby/object:Gem::Requirement
55
55
  none: false
56
56
  requirements:
57
- - - ~>
57
+ - - ">="
58
58
  - !ruby/object:Gem::Version
59
59
  segments:
60
60
  - 0
61
- - 4
62
- - 4
63
- version: 0.4.4
61
+ version: "0"
64
62
  type: :runtime
65
63
  version_requirements: *id003
66
64
  description: Japanese feature phones (a.k.a., keitai, galakei) have a number of restrictions over normal web browsers. This library adds support for them
@@ -79,6 +77,10 @@ files:
79
77
  - Rakefile
80
78
  - galakei.gemspec
81
79
  - lib/galakei.rb
80
+ - lib/galakei/docomo_css.rb
81
+ - lib/galakei/docomo_css/inline_stylesheet.rb
82
+ - lib/galakei/docomo_css/railtie.rb
83
+ - lib/galakei/docomo_css/stylesheet.rb
82
84
  - lib/galakei/email.rb
83
85
  - lib/galakei/emoji_table.rb
84
86
  - lib/galakei/filter/base.rb
@@ -97,13 +99,19 @@ files:
97
99
  - lib/galakei/version.rb
98
100
  - spec/acceptance/acceptance_helper.rb
99
101
  - spec/acceptance/app/.gitignore
102
+ - spec/acceptance/app/app/views/haml/index.html.haml
100
103
  - spec/acceptance/app/app/views/layouts/application.html.haml
101
104
  - spec/acceptance/app/fake.rb
105
+ - spec/acceptance/docomo_css_spec.rb
102
106
  - spec/acceptance/emoji_table_spec.rb
107
+ - spec/acceptance/haml_spec.rb
103
108
  - spec/acceptance/handset_detection_spec.rb
104
109
  - spec/acceptance/input_mode_spec.rb
105
110
  - spec/acceptance/session_spec.rb
106
111
  - spec/acceptance/support/handsets.rb
112
+ - spec/acceptance/views_spec.rb
113
+ - spec/galakei/docomo_css/stylesheet_spec.rb
114
+ - spec/galakei/emoji_table_spec.rb
107
115
  - spec/galakei/filter/content_type_spec.rb
108
116
  - spec/galakei/request_spec.rb
109
117
  - spec/spec_helper.rb