showcase 0.1.7 → 0.2.0.beta

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,61 @@
1
+ require 'showcase/helpers/config_object'
2
+ require 'active_support/core_ext/object/to_query'
3
+
4
+ module Showcase
5
+ module Traits
6
+ module Share
7
+ extend ActiveSupport::Concern
8
+ include LinkTo
9
+
10
+ PROVIDERS = {
11
+ twitter: {
12
+ url: "https://twitter.com/intent/tweet",
13
+ params: { url: :url, text: :text },
14
+ label: 'Tweet it!'
15
+ },
16
+ gplus: {
17
+ url: "https://plus.google.com/share",
18
+ params: { url: :url },
19
+ label: 'Share it on Google Plus!'
20
+ },
21
+ facebook: {
22
+ url: "http://www.facebook.com/sharer/sharer.php",
23
+ params: { u: :url },
24
+ label: 'Share it on Facebook!'
25
+ },
26
+ linkedin: {
27
+ url: "http://www.linkedin.com/shareArticle",
28
+ params: { url: :url, title: :text },
29
+ label: 'Share it on LinkedIn!'
30
+ },
31
+ pinterest: {
32
+ url: "http://www.pinterest.com/pin/create/button/",
33
+ params: { url: :url, media: :image_url, title: :text },
34
+ label: 'Pin it!'
35
+ }
36
+ }
37
+
38
+ module ClassMethods
39
+ def share(name = nil, &block)
40
+ PROVIDERS.each do |social, settings|
41
+ link_name = [ name, social, "share" ].map(&:presence).compact.join("_")
42
+
43
+ link_to link_name do |c|
44
+ meta = Helpers::ConfigObject.new(self, &block).to_struct
45
+ params = Hash[
46
+ settings[:params].map do |param, meta_key|
47
+ [ param, meta.send(meta_key) ]
48
+ end
49
+ ]
50
+ c.url "#{settings[:url]}?#{params.to_query}"
51
+ c.label settings[:label]
52
+ c.html_options = { target: :blank }
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ end
59
+ end
60
+ end
61
+
@@ -0,0 +1,11 @@
1
+ require 'showcase/traits/base'
2
+ require 'showcase/traits/record'
3
+ require 'showcase/traits/seo'
4
+ require 'showcase/traits/link_to'
5
+ require 'showcase/traits/share'
6
+
7
+ module Showcase
8
+ module Traits
9
+ end
10
+ end
11
+
@@ -1,4 +1,4 @@
1
1
  module Showcase
2
- VERSION = "0.1.7"
2
+ VERSION = "0.2.0.beta"
3
3
  end
4
4
 
data/lib/showcase.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  require 'showcase/version'
2
2
  require 'showcase/presenter'
3
+ require 'showcase/railtie' if defined?(Rails)
3
4
 
4
5
  module Showcase
5
6
  end
7
+
data/showcase.gemspec CHANGED
@@ -18,7 +18,10 @@ Gem::Specification.new do |gem|
18
18
  gem.require_paths = ['lib']
19
19
 
20
20
  gem.add_dependency 'activesupport'
21
+
21
22
  gem.add_development_dependency 'rspec'
22
23
  gem.add_development_dependency 'coveralls'
24
+ gem.add_development_dependency 'rails'
25
+ gem.add_development_dependency 'rspec-html-matchers'
23
26
  end
24
27
 
data/spec/fixtures.rb CHANGED
@@ -1,3 +1,19 @@
1
+ class RailsViewContext < ActionView::Base
2
+ include ActionView::Helpers::TagHelper
3
+
4
+ def capture(*args, &block)
5
+ block.call(*args)
6
+ end
7
+ end
8
+
9
+ class Model
10
+ extend ActiveModel::Naming
11
+
12
+ def to_key
13
+ Array('1')
14
+ end
15
+ end
16
+
1
17
  class Person < Struct.new(:name)
2
18
  end
3
19
 
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ module Showcase::Helpers
4
+ describe ConfigObject do
5
+
6
+ let(:context_class) {
7
+ Class.new do
8
+ def foo
9
+ 'foo'
10
+ end
11
+
12
+ def bar
13
+ 'bar'
14
+ end
15
+ end
16
+ }
17
+ let(:context) { context_class.new }
18
+
19
+ describe 'to_hash' do
20
+ it 'accepts attr=() notation' do
21
+ result = ConfigObject.new(context) do |c|
22
+ c.first = foo
23
+ c.second = bar
24
+ c.third = '3rd'
25
+ end.to_hash
26
+
27
+ hash = { first: 'foo', second: 'bar', third: '3rd' }
28
+ expect(result).to eq hash
29
+ end
30
+
31
+ it 'accepts attr() notation' do
32
+ result = ConfigObject.new(context) do |c|
33
+ c.first foo
34
+ c.second bar
35
+ c.third '3rd'
36
+ end.to_hash
37
+
38
+ hash = { first: 'foo', second: 'bar', third: '3rd' }
39
+ expect(result).to eq hash
40
+ end
41
+ end
42
+
43
+ end
44
+ end
45
+
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+
3
+ module Showcase::Helpers
4
+ describe SeoMetaBuilder do
5
+
6
+ let(:view) { RailsViewContext.new }
7
+ let(:builder) { double('SeoMetaBuilder') }
8
+
9
+ subject { SeoMetaBuilder.new(view) }
10
+
11
+ describe '#title' do
12
+ it 'produces a <title/> tag' do
13
+ expect(subject.title('foo')).to have_tag(:title, text: 'foo')
14
+ end
15
+
16
+ it 'produces a og:title meta tag' do
17
+ expect(subject.title('foo')).to have_tag(:meta, with: { property: 'og:title', content: 'foo' })
18
+ end
19
+
20
+ it 'produces a twitter:title meta tag' do
21
+ expect(subject.title('foo')).to have_tag(:meta, with: { name: 'twitter:title', content: 'foo' })
22
+ end
23
+
24
+ context 'with multiple values' do
25
+ it 'uses the first non-blank' do
26
+ expect(subject.title(['', nil, 'foo'])).to have_tag(:title, text: 'foo')
27
+ end
28
+ end
29
+
30
+ context 'with a :title_suffix option' do
31
+ it 'produces a <title/> tag with the suffix' do
32
+ expect(subject.title('foo', title_suffix: ' - bar')).to have_tag(:title, text: 'foo - bar')
33
+ end
34
+ it 'does not suffix meta tags' do
35
+ expect(subject.title('foo')).to have_tag(:meta, with: { property: 'og:title', content: 'foo' })
36
+ expect(subject.title('foo')).to have_tag(:meta, with: { name: 'twitter:title', content: 'foo' })
37
+ end
38
+ end
39
+ end
40
+
41
+ describe '#description' do
42
+ it 'produces a description meta tag' do
43
+ expect(subject.description('foo')).to have_tag(:meta, with: { name: 'description', content: 'foo' })
44
+ end
45
+
46
+ it 'produces a og:description meta tag' do
47
+ expect(subject.description('foo')).to have_tag(:meta, with: { property: 'og:description', content: 'foo' })
48
+ end
49
+
50
+ it 'produces a twitter:description meta tag' do
51
+ expect(subject.description('foo')).to have_tag(:meta, with: { name: 'twitter:description', content: 'foo' })
52
+ end
53
+
54
+ context 'with multiple values' do
55
+ it 'uses the first non-blank' do
56
+ expect(subject.description(['', nil, 'foo'])).to have_tag(:meta, with: { name: 'description', content: 'foo' })
57
+ end
58
+ end
59
+ end
60
+
61
+ describe '#image_url' do
62
+ it 'produces a og:image meta tag' do
63
+ expect(subject.image_url('foo')).to have_tag(:meta, with: { property: 'og:image', content: 'foo' })
64
+ end
65
+
66
+ it 'produces a twitter:image meta tag' do
67
+ expect(subject.image_url('foo')).to have_tag(:meta, with: { name: 'twitter:image', content: 'foo' })
68
+ end
69
+
70
+ context 'with multiple values' do
71
+ it 'uses the first non-blank' do
72
+ expect(subject.image_url(['', nil, 'foo'])).to have_tag(:meta, with: { property: 'og:image', content: 'foo' })
73
+ end
74
+ end
75
+ end
76
+
77
+ describe '#canonical_url' do
78
+ it 'produces a og:url meta tag' do
79
+ expect(subject.canonical_url('foo')).to have_tag(:meta, with: { property: 'og:url', content: 'foo' })
80
+ end
81
+
82
+ it 'produces a canonical link tag' do
83
+ expect(subject.canonical_url('foo')).to have_tag(:link, with: { rel: 'canonical', href: 'foo' })
84
+ end
85
+
86
+ context 'with multiple values' do
87
+ it 'uses the first non-blank' do
88
+ expect(subject.canonical_url(['', nil, 'foo'])).to have_tag(:meta, with: { property: 'og:url', content: 'foo' })
89
+ end
90
+ end
91
+ end
92
+
93
+ end
94
+ end
95
+
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Showcase::Helpers do
4
+ let(:object) { Person.new('Steve Ballmer') }
5
+ let(:context) { Context.new }
6
+
7
+ describe '.present' do
8
+ it 'instanciate a new presenter, inferring the class' do
9
+ PersonPresenter.stub(:new).with(object, context).and_return 'Presenter'
10
+ context.present(object, PersonPresenter).should == 'Presenter'
11
+ end
12
+ it 'the presenter class to use can be specified as the second parameter' do
13
+ ProjectPresenter.stub(:new).with(object, context).and_return 'Presenter'
14
+ context.present(object, ProjectPresenter).should == 'Presenter'
15
+ end
16
+ it 'the context to use can be specified as third parameter' do
17
+ different_context = double
18
+ context.present(object, ProjectPresenter, different_context).view_context.should == different_context
19
+ end
20
+ end
21
+
22
+ describe '.present_collection' do
23
+ it 'returns a presenter for each object in the collection' do
24
+ collection = [ Person.new('Mark'), Person.new('Luke') ]
25
+
26
+ PersonPresenter.stub(:new).with(collection[0], context).and_return 'foo'
27
+ PersonPresenter.stub(:new).with(collection[1], context).and_return 'bar'
28
+
29
+ presented_collection = context.present_collection(collection)
30
+ presented_collection.should == [ 'foo', 'bar' ]
31
+ end
32
+ end
33
+ end
34
+
@@ -1,8 +1,4 @@
1
- require 'coveralls'
2
- Coveralls.wear!
3
-
4
- require 'showcase'
5
- require_relative './fixtures'
1
+ require 'spec_helper'
6
2
 
7
3
  describe Showcase::Presenter do
8
4
  let(:context) { Context.new }
@@ -73,35 +69,3 @@ describe Showcase::Presenter do
73
69
  end
74
70
  end
75
71
 
76
- describe Showcase::Helpers do
77
- let(:object) { Person.new('Steve Ballmer') }
78
- let(:context) { Context.new }
79
-
80
- describe '.present' do
81
- it 'instanciate a new presenter, inferring the class' do
82
- PersonPresenter.stub(:new).with(object, context).and_return 'Presenter'
83
- context.present(object, PersonPresenter).should == 'Presenter'
84
- end
85
- it 'the presenter class to use can be specified as the second parameter' do
86
- ProjectPresenter.stub(:new).with(object, context).and_return 'Presenter'
87
- context.present(object, ProjectPresenter).should == 'Presenter'
88
- end
89
- it 'the context to use can be specified as third parameter' do
90
- different_context = double
91
- context.present(object, ProjectPresenter, different_context).view_context.should == different_context
92
- end
93
- end
94
-
95
- describe '.present_collection' do
96
- it 'returns a presenter for each object in the collection' do
97
- collection = [ Person.new('Mark'), Person.new('Luke') ]
98
-
99
- PersonPresenter.stub(:new).with(collection[0], context).and_return 'foo'
100
- PersonPresenter.stub(:new).with(collection[1], context).and_return 'bar'
101
-
102
- presented_collection = context.present_collection(collection)
103
- presented_collection.should == [ 'foo', 'bar' ]
104
- end
105
- end
106
- end
107
-
@@ -0,0 +1,12 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
4
+ require 'rspec-html-matchers'
5
+
6
+ require 'rails'
7
+ require 'showcase'
8
+ require 'active_model'
9
+ require 'action_view'
10
+
11
+ require_relative './fixtures'
12
+
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ module Showcase::Traits
4
+ describe Base do
5
+ describe '.define_method?' do
6
+
7
+ it 'defines an instance method' do
8
+ klass = Class.new do
9
+ include Base
10
+ define_method? :foo do
11
+ true
12
+ end
13
+ end
14
+
15
+ expect(klass.new.foo).to be_true
16
+ end
17
+
18
+ context 'if the method is already present' do
19
+ it 'no-ops ' do
20
+ klass = Class.new do
21
+ include Base
22
+ def foo; false; end
23
+ end
24
+
25
+ expect(klass.new.foo).to be_false
26
+ end
27
+ end
28
+
29
+ context 'if method name is an array of chunks' do
30
+ it 'joins them in snake case' do
31
+ klass = Class.new do
32
+ include Base
33
+ define_method? [:foo, :bar] do
34
+ true
35
+ end
36
+ end
37
+
38
+ expect(klass.new.foo_bar).to be_true
39
+ end
40
+
41
+ it 'ignores blank chunks' do
42
+ klass = Class.new do
43
+ include Base
44
+ define_method? ["", :bar] do
45
+ true
46
+ end
47
+ end
48
+
49
+ expect(klass.new.bar).to be_true
50
+ end
51
+ end
52
+
53
+ end
54
+ end
55
+ end
56
+
@@ -0,0 +1,123 @@
1
+ require 'spec_helper'
2
+
3
+ module Showcase::Traits
4
+ describe LinkTo do
5
+
6
+ let(:view) { RailsViewContext.new }
7
+ let(:record) { Model.new }
8
+ subject { presenter.new(record, view) }
9
+
10
+ let(:active) { false }
11
+ before do
12
+ record.stub(:active).and_return(active)
13
+ end
14
+
15
+ let(:presenter) {
16
+ Class.new(Showcase::Presenter) do
17
+ include LinkTo
18
+
19
+ link_to do |c|
20
+ c.url = url
21
+ c.label = 'Label'
22
+ c.active = active
23
+ c.html_options role: 'label'
24
+ end
25
+
26
+ link_to :foo do |c|
27
+ c.url = '#foo'
28
+ c.active_class = 'current'
29
+ c.active = active
30
+ end
31
+
32
+ def url
33
+ '#bar'
34
+ end
35
+ end
36
+ }
37
+
38
+ describe '#link_to' do
39
+
40
+ context 'defines a link method' do
41
+ it 'pointing to the specified url' do
42
+ expect(subject.link).to have_tag(:a, with: { href: '#bar' })
43
+ end
44
+
45
+ it 'with the specified label' do
46
+ expect(subject.link).to have_tag(:a, text: 'Label')
47
+ end
48
+
49
+ it 'with the specified html attributes' do
50
+ expect(subject.link).to have_tag(:a, with: { role: 'label' })
51
+ end
52
+
53
+ context 'with a different label as parameter' do
54
+ it 'uses it' do
55
+ expect(subject.link('Foo')).to have_tag(:a, text: 'Foo')
56
+ end
57
+ end
58
+
59
+ context 'with additional options' do
60
+ it 'uses them as html options' do
61
+ end
62
+ end
63
+
64
+ context 'with a block' do
65
+ it 'uses it instead of the label' do
66
+ result = subject.link { 'Foo' }
67
+ expect(result).to have_tag(:a, text: 'Foo')
68
+ end
69
+ end
70
+
71
+ context 'if active' do
72
+ let(:active) { true }
73
+
74
+ it 'adds an active class to the link' do
75
+ expect(subject.link('label')).to have_tag(:a, with: { class: 'active' })
76
+ end
77
+
78
+ context 'with additional classes' do
79
+ it 'it sums them' do
80
+ expect(subject.link('label', class: 'extra')).to have_tag(:a, with: { class: 'extra active' })
81
+ end
82
+ end
83
+
84
+ context 'if a different CSS class was specified' do
85
+ it 'adds it to the link' do
86
+ expect(subject.foo_link('label')).to have_tag(:a, with: { class: 'current' })
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ context 'defines an link_active? method' do
93
+ let(:active) { double }
94
+
95
+ it 'returns the active flag' do
96
+ expect(subject.link_active?).to eq active
97
+ end
98
+ end
99
+
100
+ context 'defines an url method' do
101
+ it 'returns the url' do
102
+ expect(subject.url).to eq '#bar'
103
+ end
104
+ end
105
+
106
+ context 'with prefix' do
107
+ it 'prefixes link method' do
108
+ expect(subject).to respond_to :foo_link
109
+ end
110
+
111
+ it 'prefixes link_active? method' do
112
+ expect(subject).to respond_to :foo_link_active?
113
+ end
114
+
115
+ it 'prefixes url method' do
116
+ expect(subject).to respond_to :foo_url
117
+ end
118
+ end
119
+ end
120
+
121
+ end
122
+ end
123
+
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ module Showcase::Traits
4
+ describe Record do
5
+
6
+ let(:view) { RailsViewContext.new }
7
+ let(:record) { Model.new }
8
+ let(:presenter) {
9
+ Class.new(Showcase::Presenter) do
10
+ include Record
11
+
12
+ box do |c|
13
+ c.html_options role: 'actor', class: 'first'
14
+ end
15
+ end
16
+ }
17
+ subject { presenter.new(record, view) }
18
+
19
+ describe '#dom_id' do
20
+ it 'returns a CSS ID for the model' do
21
+ expect(subject.dom_id).to eq 'model_1'
22
+ end
23
+ end
24
+
25
+ describe '#dom_class' do
26
+ it 'returns a CSS class for model' do
27
+ expect(subject.dom_class).to eq 'model'
28
+ end
29
+ end
30
+
31
+ describe '#box' do
32
+ it 'wraps content inside an element that uniquely identifies the record' do
33
+ result = subject.box { "foo" }
34
+ expect(result).to have_tag(:div, with: { class: 'model', id: 'model_1' })
35
+ end
36
+
37
+ it 'adds attributes specified within box block' do
38
+ result = subject.box { "foo" }
39
+ expect(result).to have_tag(:div, with: { role: 'actor', class: 'first' })
40
+ end
41
+
42
+ context 'with a specified tag' do
43
+ it 'uses it' do
44
+ result = subject.box(:span) { "foo" }
45
+ expect(result).to have_tag(:span)
46
+ end
47
+ end
48
+
49
+ context 'with some class' do
50
+ it 'uses it' do
51
+ result = subject.box(class: 'bar') { "foo" }
52
+ expect(result).to have_tag(:div, with: { class: 'model bar'})
53
+ end
54
+ end
55
+ end
56
+
57
+ end
58
+ end
59
+
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ module Showcase::Traits
4
+ describe Seo do
5
+
6
+ let(:view) { RailsViewContext.new }
7
+ let(:record) { Model.new }
8
+ let(:builder) { double('SeoMetaBuilder') }
9
+ subject { presenter.new(record, view) }
10
+
11
+ let(:presenter) {
12
+ Class.new(Showcase::Presenter) do
13
+ include Seo
14
+
15
+ seo do |c|
16
+ c.title = 'foo'
17
+ c.description 'bar'
18
+ end
19
+ end
20
+ }
21
+
22
+ describe '#seo' do
23
+ let(:options) { double('options') }
24
+ before do
25
+ Showcase::Helpers::SeoMetaBuilder.stub(:new).with(view).and_return(builder)
26
+ builder.stub(:title).with('foo', options).and_return('<title>')
27
+ builder.stub(:description).with('bar', options).and_return('<description>')
28
+ end
29
+
30
+ it 'defines a seo_tags method that ouputs seo meta tags' do
31
+ expect(subject.seo_tags(options)).to eq '<title><description>'
32
+ end
33
+ end
34
+
35
+ end
36
+ end
37
+
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ module Showcase::Traits
4
+ describe Share do
5
+
6
+ let(:view) { RailsViewContext.new }
7
+ let(:record) { Model.new }
8
+ subject { presenter.new(record, view) }
9
+
10
+ let(:presenter) {
11
+ Class.new(Showcase::Presenter) do
12
+ include Share
13
+
14
+ share do |c|
15
+ c.url = 'url'
16
+ c.text = 'text'
17
+ c.image_url = 'image'
18
+ end
19
+
20
+ share :foo do |c|
21
+ c.url = 'url'
22
+ c.text = 'text'
23
+ c.image_url = 'image'
24
+ end
25
+
26
+ end
27
+ }
28
+
29
+ describe '#social_share' do
30
+ expected_urls = {
31
+ twitter: "https://twitter.com/intent/tweet?text=text&url=url",
32
+ facebook: "http://www.facebook.com/sharer/sharer.php?u=url",
33
+ gplus: "https://plus.google.com/share?url=url",
34
+ pinterest: "http://www.pinterest.com/pin/create/button/?media=image&title=text&url=url",
35
+ linkedin: "http://www.linkedin.com/shareArticle?title=text&url=url",
36
+ }
37
+
38
+ expected_urls.each do |provider, url|
39
+ it "produces a #{provider} share link" do
40
+ expect(subject.send("#{provider}_share_link")).to have_tag(:a)
41
+ end
42
+ it "produces a #{provider} share url" do
43
+ expect(subject.send("#{provider}_share_url")).to eq url
44
+ end
45
+
46
+ context 'with prefix' do
47
+ it 'prefixes link method' do
48
+ expect(subject).to respond_to "foo_#{provider}_share_link"
49
+ end
50
+
51
+ it 'prefixes link_active? method' do
52
+ expect(subject).to respond_to "foo_#{provider}_share_url"
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ end
59
+ end
60
+