apropos 0.1.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.
@@ -0,0 +1,24 @@
1
+ require_relative "../spec_helper.rb"
2
+
3
+ describe Apropos::MediaQuery do
4
+ it "wraps query in parens" do
5
+ css = described_class.new("min-width: 320px").to_css
6
+ css.should == "(min-width: 320px)"
7
+ end
8
+
9
+ it "combines media queries" do
10
+ combo = described_class.new("min-width: 320px").combine(described_class.new("max-width: 480px"))
11
+ combo.to_css.should == "(min-width: 320px) and (max-width: 480px)"
12
+ end
13
+
14
+ it "parses comma-separated media queries" do
15
+ query = described_class.new("min-width: 320px, min-resolution: 192dpi")
16
+ query.to_css.should == "(min-width: 320px), (min-resolution: 192dpi)"
17
+ end
18
+
19
+ it "combines complex media query strings" do
20
+ base = described_class.new("(-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi)")
21
+ combo = base.combine(described_class.new("min-width: 320px"))
22
+ combo.to_css.should == "(-webkit-min-device-pixel-ratio: 2) and (min-width: 320px), (min-resolution: 192dpi) and (min-width: 320px)"
23
+ end
24
+ end
@@ -0,0 +1,34 @@
1
+ require_relative "../spec_helper.rb"
2
+
3
+ describe Apropos::Set do
4
+ def subject
5
+ @subject ||= described_class.new("foo.jpg", "/dir")
6
+ end
7
+
8
+ it "detects paths with indicators before the base file extension" do
9
+ subject.variant_path_glob.should == Pathname.new("foo.*.jpg")
10
+ end
11
+
12
+ it "generates a list of variant paths and code fragments" do
13
+ paths = {
14
+ "ca" => "foo.ca.jpg",
15
+ "2x" => "foo.2x.jpg",
16
+ "large.fr" => "foo.large.fr.jpg",
17
+ "large.2x.ca" => "foo.large.2x.ca.jpg",
18
+ }
19
+ globbed = paths.values.map {|path| "/dir/#{path}"}
20
+ Dir.should_receive(:glob).with(Pathname.new("/dir/foo.*.jpg")).and_return(globbed)
21
+ subject.variant_paths.should == paths
22
+ end
23
+
24
+ it "creates Variants from variant paths and code fragments" do
25
+ Dir.should_receive(:glob).and_return(["/dir/foo.ca.jpg"])
26
+ Apropos::Variant.should_receive(:new).with("ca", "foo.ca.jpg")
27
+ subject.variants.length.should == 1
28
+ end
29
+
30
+ it "removes the basedir from paths" do
31
+ set = described_class.new("foo.jpg", "/foo/bar")
32
+ set.remove_basedir("/Users/bob/foo/bar/foo.fr.jpg").should == "foo.fr.jpg"
33
+ end
34
+ end
@@ -0,0 +1,74 @@
1
+ require_relative "../spec_helper.rb"
2
+ require 'ostruct'
3
+
4
+ describe Apropos::Variant do
5
+ let(:dummy_path) { "foo.jpg" }
6
+ let!(:dpi_selector) { OpenStruct.new(:sort_value => 0) }
7
+ let!(:breakpoint_selector) { OpenStruct.new(:sort_value => 1) }
8
+ let!(:class_selector) { OpenStruct.new(:sort_value => 2) }
9
+
10
+ def variant(code_fragment='')
11
+ described_class.new(code_fragment, dummy_path)
12
+ end
13
+
14
+ before :all do
15
+ Apropos::ExtensionParser.add_parser('2x') do |match|
16
+ dpi_selector
17
+ end
18
+ Apropos::ExtensionParser.add_parser('medium') do |match|
19
+ breakpoint_selector
20
+ end
21
+ Apropos::ExtensionParser.add_parser(/^([a-z]{2})$/) do |match|
22
+ class_selector
23
+ end
24
+ end
25
+
26
+ after :all do
27
+ Apropos::ExtensionParser.parsers.clear
28
+ end
29
+
30
+ it "extracts codes from code fragment" do
31
+ variant("2x.medium.fr").codes.should == %w[2x medium fr]
32
+ end
33
+
34
+ it "collects conditions parsed from code fragment" do
35
+ variant("2x").conditions.should == [dpi_selector]
36
+ variant("2x.medium.fr").conditions.should == [dpi_selector, breakpoint_selector, class_selector]
37
+ end
38
+
39
+ it "is invalid if no conditions apply" do
40
+ v = variant("1x")
41
+ v.conditions.should == []
42
+ v.should_not be_valid
43
+ v2 = variant("2x")
44
+ v2.should be_valid
45
+ end
46
+
47
+ it "combines conditions of the same type" do
48
+ v = variant
49
+ v.stub(:conditions) {
50
+ [
51
+ Apropos::MediaQuery.new("min-width: 320px"),
52
+ Apropos::MediaQuery.new("max-width: 640px")
53
+ ]
54
+ }
55
+ v.rule.should == ["media", "(min-width: 320px) and (max-width: 640px)", "foo.jpg"]
56
+ end
57
+
58
+ it "combines conditions of different types" do
59
+ v = variant
60
+ v.stub(:conditions) {
61
+ [
62
+ Apropos::MediaQuery.new("min-width: 320px"),
63
+ Apropos::ClassList.new([".foo"])
64
+ ]
65
+ }
66
+ v.rule.should == ["class+media", ".foo", "(min-width: 320px)", "foo.jpg"]
67
+ end
68
+
69
+ it "aggregates the sort value of the rules" do
70
+ variant("2x").aggregate_sort_value.should == 0
71
+ variant("2x.medium").aggregate_sort_value.should == 1
72
+ variant("2x.medium.fr").aggregate_sort_value.should == 3
73
+ end
74
+ end
@@ -0,0 +1,8 @@
1
+ require 'simplecov'
2
+ SimpleCov.start do
3
+ add_filter '/spec/'
4
+ add_filter '/lib/apropos/sass_functions.rb'
5
+ end
6
+
7
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
8
+ require 'apropos'
@@ -0,0 +1,114 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'stylesheets' do
4
+ let(:import_options) {
5
+ {style: :compact, load_paths: [Apropos::STYLESHEETS_DIR], syntax: :scss}
6
+ }
7
+ let(:css_file) {
8
+ Sass::Engine.new(@scss_file, import_options).render
9
+ }
10
+
11
+ def stub_files(*files)
12
+ Dir.stub(:glob).with(Pathname.new("hero.*.jpg")).and_return(files)
13
+ end
14
+
15
+ it "can be imported" do
16
+ @scss_file = %Q{
17
+ @import "apropos";
18
+ .foo {
19
+ @include apropos-bg-variants('hero.jpg');
20
+ }
21
+ }
22
+ css_file.strip.should == ".foo { background-image: url('/hero.jpg'); }"
23
+ end
24
+
25
+ describe "hidpi stylesheet" do
26
+ it "generates default hidpi rules" do
27
+ stub_files("./hero.2x.jpg")
28
+ @scss_file = %Q{
29
+ @import "apropos";
30
+ .foo {
31
+ @include apropos-bg-variants('hero.jpg');
32
+ }
33
+ }
34
+ css_file.should include('(-webkit-min-device-pixel-ratio: 1.75), (min-resolution: 168dpi)')
35
+ css_file.should include("'/hero.2x.jpg'")
36
+ end
37
+
38
+ it "allows customizing hidpi extension and query" do
39
+ stub_files("./hero.hidpi.jpg")
40
+ @scss_file = %Q{
41
+ $apropos-hidpi-extension: 'hidpi';
42
+ $apropos-hidpi-query: '(min-resolution: 300dpi)';
43
+ @import "apropos";
44
+ .foo {
45
+ @include apropos-bg-variants('hero.jpg');
46
+ }
47
+ }
48
+ css_file.should_not include('(-webkit-min-device-pixel-ratio: 1.75)')
49
+ css_file.should include('(min-resolution: 300dpi)')
50
+ css_file.should include("'/hero.hidpi.jpg'")
51
+ end
52
+ end
53
+
54
+ describe "breakpoints stylesheet" do
55
+ before :each do
56
+ stub_files("./hero.medium.jpg", "./hero.large.jpg")
57
+ end
58
+
59
+ it "doesn't generate any defaults" do
60
+ @scss_file = %Q{
61
+ @import "apropos";
62
+ .foo {
63
+ @include apropos-bg-variants('hero.jpg');
64
+ }
65
+ }
66
+ css_file.should_not include('/hero.medium.jpg')
67
+ css_file.should_not include('/hero.large.jpg')
68
+ end
69
+
70
+ it "allows setting breakpoints" do
71
+ stub_files("./hero.medium.jpg", "./hero.large.jpg")
72
+ @scss_file = %Q{
73
+ $apropos-breakpoints: (medium, 768px), (large, 1024px);
74
+ @import "apropos";
75
+ .foo {
76
+ @include apropos-bg-variants('hero.jpg');
77
+ }
78
+ }
79
+ css_file.should include("@media (min-width: 768px) { .foo { background-image: url('/hero.medium.jpg'); } }")
80
+ css_file.should include("@media (min-width: 1024px) { .foo { background-image: url('/hero.large.jpg'); } }")
81
+ end
82
+ end
83
+
84
+ describe "breakpoints and hidpi" do
85
+ it "can be combined" do
86
+ stub_files("./hero.large.2x.jpg", "./hero.medium.2x.jpg")
87
+ @scss_file = %Q{
88
+ $apropos-breakpoints: (medium, 768px), (large, 1024px);
89
+ @import "apropos";
90
+ .foo {
91
+ @include apropos-bg-variants('hero.jpg');
92
+ }
93
+ }
94
+ css_file.should include("@media (min-width: 768px) and (-webkit-min-device-pixel-ratio: 1.75), (min-width: 768px) and (min-resolution: 168dpi) { .foo { background-image: url('/hero.medium.2x.jpg'); } }")
95
+ css_file.should include("@media (min-width: 1024px) and (-webkit-min-device-pixel-ratio: 1.75), (min-width: 1024px) and (min-resolution: 168dpi) { .foo { background-image: url('/hero.large.2x.jpg'); } }")
96
+ end
97
+
98
+ it "sorts breakpoints vs. retina correctly" do
99
+ # filesystem sort order
100
+ files = %w[2x large.2x large medium.2x medium].map {|f| "./hero.#{f}.jpg" }
101
+ stub_files(*files)
102
+ @scss_file = %Q{
103
+ $apropos-breakpoints: (medium, 768px), (large, 1024px);
104
+ @import "apropos";
105
+ .foo {
106
+ @include apropos-bg-variants('hero.jpg');
107
+ }
108
+ }
109
+ images = css_file.scan(/hero.+jpg/)
110
+ sorted_images = %w[2x medium medium.2x large large.2x].map {|f| "hero.#{f}.jpg" }
111
+ images.should == ["hero.jpg"] + sorted_images
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,3 @@
1
+ @import "apropos/core"
2
+ @import "apropos/hidpi"
3
+ @import "apropos/breakpoints"
@@ -0,0 +1,6 @@
1
+ $apropos-breakpoints: () !default
2
+
3
+ $_count: 1
4
+ @each $_breakpoint in $apropos-breakpoints
5
+ $_: add_breakpoint_image_variant(nth($_breakpoint, 1), "(min-width: #{nth($_breakpoint, 2)})", $_count)
6
+ $_count: $_count + 1
@@ -0,0 +1,40 @@
1
+ // apropos-bg-variants automatically generates rules for applying image
2
+ // variants as CSS backgrounds.
3
+ //
4
+ // Arguments:
5
+ // $path: path to base image
6
+ // $generate-height: true to have height properties generated for all
7
+ // non-hidpi variants.
8
+ // ===========================================================================
9
+ =apropos-bg-variants($path, $generate-height: false)
10
+ +apropos-bg-image-with-height($path, $generate-height)
11
+ @each $_variant in image-variants($path)
12
+ $_type: nth-polyfill($_variant, 1)
13
+ $_path: nth-polyfill($_variant, -1)
14
+ $_class: nth-polyfill($_variant, 2)
15
+ $_query: nth-polyfill($_variant, -2)
16
+ @if $_type == "class+media"
17
+ @media #{$_query}
18
+ #{nest($_class, "&")}
19
+ +apropos-bg-image-with-height($_path, $generate-height, $_query)
20
+ @else if $_type == media
21
+ @media #{$_query}
22
+ +apropos-bg-image-with-height($_path, $generate-height, $_query)
23
+ @else if $_type == class
24
+ #{nest($_class, "&")}
25
+ background-image: image-url($_path)
26
+
27
+ // apropos-bg-image-with-height generates a background-image property and
28
+ // optionally generates a height property calculated from the image, if the
29
+ // query argument is not hidpi.
30
+ //
31
+ // Arguments:
32
+ // $path: path to base image
33
+ // $generate-height: true to have height properties generated
34
+ // $query: media query that this image will be used with (height will not be
35
+ // generated if the query is hidpi)
36
+ // ===========================================================================
37
+ =apropos-bg-image-with-height($path, $generate-height, $query: "")
38
+ background-image: image-url($path)
39
+ @if $generate-height and not (str-index($query, "dpi") > 0 or str-index($query, "pixel-ratio") > 0)
40
+ height: image-height($path)
@@ -0,0 +1,4 @@
1
+ $apropos-hidpi-extension: "2x" !default
2
+ $apropos-hidpi-query: "(-webkit-min-device-pixel-ratio: 1.75), (min-resolution: 168dpi)" !default
3
+
4
+ $_: add_dpi_image_variant($apropos-hidpi-extension, $apropos-hidpi-query, 0.5)
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: apropos
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Gabriel Gilder
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-08-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: compass
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.13'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '2.13'
69
+ - !ruby/object:Gem::Dependency
70
+ name: cane
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Apropos helps your site serve up the appropriate image for every visitor.
98
+ Serving multiple versions of an image in responsive and/or localized web sites can
99
+ be a chore, but Apropos simplifies and automates this task. Instead of manually
100
+ writing a lot of CSS rules to swap different images, Apropos generates CSS for you
101
+ based on a simple file naming convention.
102
+ email:
103
+ - gabriel@squareup.com
104
+ executables: []
105
+ extensions: []
106
+ extra_rdoc_files: []
107
+ files:
108
+ - .gitignore
109
+ - CHANGELOG.md
110
+ - Gemfile
111
+ - LICENSE.txt
112
+ - README.md
113
+ - Rakefile
114
+ - apropos.gemspec
115
+ - doc-src/customization.md
116
+ - lib/apropos.rb
117
+ - lib/apropos/class_list.rb
118
+ - lib/apropos/extension_parser.rb
119
+ - lib/apropos/functions.rb
120
+ - lib/apropos/media_query.rb
121
+ - lib/apropos/sass_functions.rb
122
+ - lib/apropos/set.rb
123
+ - lib/apropos/variant.rb
124
+ - lib/apropos/version.rb
125
+ - spec/apropos/class_list_spec.rb
126
+ - spec/apropos/extension_parser_spec.rb
127
+ - spec/apropos/functions_spec.rb
128
+ - spec/apropos/media_query_spec.rb
129
+ - spec/apropos/set_spec.rb
130
+ - spec/apropos/variant_spec.rb
131
+ - spec/spec_helper.rb
132
+ - spec/stylesheets_spec.rb
133
+ - stylesheets/_apropos.sass
134
+ - stylesheets/apropos/_breakpoints.sass
135
+ - stylesheets/apropos/_core.sass
136
+ - stylesheets/apropos/_hidpi.sass
137
+ homepage: https://github.com/square/apropos
138
+ licenses:
139
+ - Apache 2.0
140
+ metadata: {}
141
+ post_install_message:
142
+ rdoc_options: []
143
+ require_paths:
144
+ - lib
145
+ required_ruby_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - '>='
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ requirements: []
156
+ rubyforge_project:
157
+ rubygems_version: 2.0.2
158
+ signing_key:
159
+ specification_version: 4
160
+ summary: Apropos helps your site serve up the appropriate image for every visitor.
161
+ test_files:
162
+ - spec/apropos/class_list_spec.rb
163
+ - spec/apropos/extension_parser_spec.rb
164
+ - spec/apropos/functions_spec.rb
165
+ - spec/apropos/media_query_spec.rb
166
+ - spec/apropos/set_spec.rb
167
+ - spec/apropos/variant_spec.rb
168
+ - spec/spec_helper.rb
169
+ - spec/stylesheets_spec.rb