sir_trevor_rails 0.5.0b1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +8 -0
  3. data/app/views/sir_trevor/blocks/_heading_block.html.erb +3 -1
  4. data/app/views/sir_trevor/blocks/_list_block.html.erb +9 -1
  5. data/app/views/sir_trevor/blocks/_quote_block.html.erb +7 -1
  6. data/app/views/sir_trevor/blocks/_text_block.html.erb +1 -1
  7. data/app/views/sir_trevor/blocks/_tweet_block.html.erb +3 -3
  8. data/lib/generators/sir_trevor_rails/block/block_generator.rb +16 -11
  9. data/lib/generators/sir_trevor_rails/block/templates/_block.html.erb +3 -3
  10. data/lib/generators/sir_trevor_rails/block/templates/{_block.js → _block.js.erb} +4 -6
  11. data/lib/generators/sir_trevor_rails/block/templates/_block.rb.erb +2 -0
  12. data/lib/sir_trevor_rails/block.rb +15 -5
  13. data/lib/sir_trevor_rails/block_array.rb +17 -5
  14. data/lib/sir_trevor_rails/blocks/tweet_block.rb +6 -4
  15. data/lib/sir_trevor_rails/helpers/view_helper.rb +21 -2
  16. data/lib/sir_trevor_rails/version.rb +1 -1
  17. data/lib/sir_trevor_rails/view_resolver.rb +3 -3
  18. data/sir_trevor_rails.gemspec +4 -2
  19. data/spec/fixtures/blocks/custom.json +7 -0
  20. data/spec/fixtures/blocks/heading_html.json +7 -0
  21. data/spec/fixtures/blocks/heading_markdown.json +6 -0
  22. data/spec/fixtures/blocks/image.json +26 -0
  23. data/spec/fixtures/blocks/list_html.json +10 -0
  24. data/spec/fixtures/blocks/list_markdown.json +6 -0
  25. data/spec/fixtures/blocks/quote_html.json +8 -0
  26. data/spec/fixtures/blocks/quote_markdown.json +7 -0
  27. data/spec/fixtures/blocks/text_html.json +7 -0
  28. data/spec/fixtures/blocks/text_markdown.json +6 -0
  29. data/spec/fixtures/blocks/tweet.json +21 -0
  30. data/spec/integration/editing_spec.rb +19 -0
  31. data/spec/integration/rendering_spec.rb +121 -0
  32. data/spec/internal/app/controllers/posts_controller.rb +33 -0
  33. data/spec/internal/app/models/post.rb +2 -0
  34. data/spec/internal/app/views/posts/_form.html.erb +3 -0
  35. data/spec/internal/app/views/posts/edit.html.erb +3 -0
  36. data/spec/internal/app/views/posts/new.html.erb +3 -0
  37. data/spec/internal/app/views/posts/show.html.erb +1 -0
  38. data/spec/internal/config/routes.rb +1 -1
  39. data/spec/spec_helper.rb +42 -1
  40. data/spec/unit/block_array_spec.rb +21 -11
  41. data/spec/unit/block_spec.rb +10 -4
  42. data/spec/unit/custom_block_spec.rb +10 -8
  43. data/spec/unit/has_sir_trevor_content_spec.rb +2 -8
  44. data/spec/unit/helpers_spec.rb +44 -0
  45. metadata +96 -10
  46. data/lib/generators/sir_trevor_rails/block/templates/_block.rb +0 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e0440542cac7ceda76800ef5d839ce166aea6a96
4
- data.tar.gz: 63bfbecf7f48260f978fd284bf773ec1cc8287c2
3
+ metadata.gz: 63883a02cdf49d08e2ecfc4b3ecd307396d4b9c8
4
+ data.tar.gz: 81b858f079c3632f141414bd4ed19b197edd1b05
5
5
  SHA512:
6
- metadata.gz: 41c835b01d6f5fd0bf4540ba2a3c65d3fcf70c43de7586ab3048fae21025bd05045c70c4afca5a9671dbbfe3a774e8e9f7a97dd9c3b97ff1840d7bf281135a3f
7
- data.tar.gz: 7621bdbeae052096553e9af079169a8f600aef8e8733acfdf02e0b3f91618455c8a44b3bcec27a0eb382fbb55fd52b1cfc0d32c9cd6e3f4d3cdf3c19821b9013
6
+ metadata.gz: d80735fb35626d3be73f46a844007c8b6541118b65f5d5db1b48401475dad94e9840b9564d236493457fb463e0eed2c9e35bb00d4043cd681b33021e9170cec2
7
+ data.tar.gz: 9e9d4e118c9c541d7afa0f2ce96152de64b1e136fb83561f3cc0f31c460e749515b96237b399c2534dfa04bdbd10dc1a46c5ffd79c613d570142f9f4827908ca
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ cache: bundler
3
+ rvm:
4
+ - 2.0.0
5
+ - 2.1.0
6
+ - 2.2.2
7
+
8
+ script: 'bundle exec rake test'
@@ -1 +1,3 @@
1
- <h2><%= heading_block.text %></h2>
1
+ <h2 class="st__content-block st__content-block--heading">
2
+ <%= without_p_wrap(sir_trevor_format(heading_block.text, format: heading_block.format)) %>
3
+ </h2>
@@ -1,3 +1,11 @@
1
1
  <div class="st__content-block st__content-block--list">
2
- <%= sir_trevor_markdown list_block.text %>
2
+ <% if list_block.format == :html %>
3
+ <ul>
4
+ <% list_block.listItems.each do |item| %>
5
+ <li><%= sir_trevor_format item[:content], format: list_block.format %></li>
6
+ <% end %>
7
+ </ul>
8
+ <% else %>
9
+ <%= sir_trevor_markdown list_block.text %>
10
+ <% end %>
3
11
  </div>
@@ -1,7 +1,13 @@
1
1
  <div class="st__content-block st__content-block--quote">
2
2
  <div class="quote">
3
3
  <div class="quote__content">
4
- <%= sir_trevor_markdown quote_block.text %>
4
+ <% if quote_block.format == :html %>
5
+ <blockquote>
6
+ <%= sir_trevor_format quote_block.text, format: quote_block.format %>
7
+ </blockquote>
8
+ <% else %>
9
+ <%= sir_trevor_markdown quote_block.text %>
10
+ <% end %>
5
11
  </div>
6
12
 
7
13
  <% if quote_block.cite.present? %>
@@ -1,3 +1,3 @@
1
1
  <div class="st__content-block st__content-block--text">
2
- <%= sir_trevor_markdown text_block.text %>
2
+ <%= sir_trevor_format text_block.text, format: text_block.format%>
3
3
  </div>
@@ -1,9 +1,9 @@
1
1
  <div class="st__content-block st__content-block--tweet">
2
- <%= link_to image_tag(tweet_block.profile_image_url, class: 'img'), tweet_block.screen_name %>
2
+ <%= link_to image_tag(tweet_block.profile_image_url, class: 'img'), tweet_block.profile_url %>
3
3
  <p>
4
4
  <%= tweet_block.render_tweet_body %>
5
5
  </p>
6
- <cite>From <%= link_to tweet_block.at_name, tweet_block.screen_name %> on Twitter:</cite>
7
- <time datetime="<%= tweet_block.created_at %>">(<%= link_to Time.parse(tweet_block.created_at), tweet_block.status_url %>)</time>
6
+ <cite>From <%= link_to tweet_block.at_name, tweet_block.profile_url %> on Twitter:</cite>
7
+ <time datetime="<%= tweet_block.created_at %>">(<%= link_to Time.zone.parse(tweet_block.created_at), tweet_block.status_url %>)</time>
8
8
  </div>
9
9
 
@@ -11,23 +11,28 @@ module SirTrevorRails
11
11
  def create_block
12
12
 
13
13
  # Copy the JS
14
- copy_file "_block.js", "app/assets/javascripts/sir_trevor/blocks/#{name}.js"
15
-
16
- gsub_file "app/assets/javascripts/sir_trevor/blocks/#{name}.js", /SirTrevor\.Blocks\.Example/, "SirTrevor.Blocks.#{name.capitalize}"
17
- gsub_file "app/assets/javascripts/sir_trevor/blocks/#{name}.js", /return "Example"/, "return: '#{name.capitalize}'"
18
- gsub_file "app/assets/javascripts/sir_trevor/blocks/#{name}.js", /type: 'example'/, "type: '#{name.downcase}'"
14
+ template "_block.js.erb", "app/assets/javascripts/sir_trevor/blocks/#{file_name}.js"
19
15
 
20
16
  # Copy the HTML
21
- copy_file "_block.html.erb", "app/views/sir_trevor/blocks/_#{name}_block.html.erb"
22
- gsub_file "app/views/sir_trevor/blocks/_#{name}_block.html.erb", /\s(-block)/, " #{name}-block"
23
- gsub_file "app/views/sir_trevor/blocks/_#{name}_block.html.erb", /\s(_block)/, " #{name}_block"
17
+ template "_block.html.erb", "app/views/sir_trevor/blocks/_#{file_name}_block.html.erb"
24
18
 
25
19
  # Copy the BlockDecorator
26
- copy_file "_block.rb", "app/sir_trevor_blocks/#{name}_block.rb"
27
- gsub_file "app/sir_trevor_blocks/#{name}_block.rb", /ExampleBlock/, " #{name}Block"
20
+ template "_block.rb.erb", "app/sir_trevor_blocks/#{file_name}_block.rb"
21
+ end
22
+
23
+ private
28
24
 
25
+ def file_name
26
+ name.underscore
29
27
  end
30
28
 
29
+ def block_name
30
+ file_name.camelize
31
+ end
32
+
33
+ def css_class
34
+ file_name.dasherize
35
+ end
31
36
  end
32
37
  end
33
- end
38
+ end
@@ -1,3 +1,3 @@
1
- <div class="content-block example-block">
2
- <%= example_block %>
3
- </div>
1
+ <div class="content-block <%= css_class %>-block">
2
+ <%%= <%= file_name %>_block %>
3
+ </div>
@@ -4,20 +4,18 @@
4
4
  Author: C J Bell @ madebymany
5
5
  */
6
6
 
7
- SirTrevor.Blocks.Example = (function(){
8
-
7
+ SirTrevor.Blocks.<%= block_name %> = (function(){
9
8
  return SirTrevor.Block.extend({
10
-
11
9
  // String; Names the block
12
10
  // Note – please use underscores when naming
13
11
  // Eg example_block should be ExampleBlock
14
- type: 'example',
12
+ type: '<%= file_name %>',
15
13
 
16
14
  // Function; the title displayed in the toolbar
17
15
  // Can return a translated string (if required)
18
16
  title: function() {
19
17
  // return i18n.t('blocks:example:title');
20
- return "Example";
18
+ return "<%= block_name %>";
21
19
  },
22
20
 
23
21
  // Boolean; show this blockType of the toolbar
@@ -200,4 +198,4 @@ SirTrevor.Blocks.Example = (function(){
200
198
  }
201
199
  });
202
200
 
203
- })();
201
+ })();
@@ -0,0 +1,2 @@
1
+ class <%= block_name %>Block < SirTrevorRails::Block
2
+ end
@@ -2,26 +2,37 @@ require 'ostruct'
2
2
 
3
3
  module SirTrevorRails
4
4
  class Block < OpenStruct
5
+ DEFAULT_FORMAT = :markdown
5
6
 
6
7
  def self.from_hash(hash, parent)
7
- hash = hash.deep_dup
8
+ hash = hash.deep_dup.with_indifferent_access
8
9
  self.type_klass(hash).new(hash, parent)
9
10
  end
10
11
 
12
+ def format
13
+ send(:[], :format).present? ? send(:[], :format).to_sym : DEFAULT_FORMAT
14
+ end
15
+
11
16
  def initialize(hash, parent)
12
- @as_json = hash
17
+ @raw_data = hash
13
18
  @parent = parent
14
19
  @type = hash[:type].to_sym
15
-
16
20
  super(hash[:data])
17
21
  end
18
22
 
19
- attr_reader :parent, :type, :as_json
23
+ attr_reader :parent, :type
20
24
 
21
25
  def to_partial_path
22
26
  "sir_trevor/blocks/" << self.class.name.demodulize.underscore
23
27
  end
24
28
 
29
+ def as_json(*attrs)
30
+ {
31
+ type: @type.to_s,
32
+ data: marshal_dump
33
+ }
34
+ end
35
+
25
36
  private
26
37
 
27
38
  # Infers the block class.
@@ -58,6 +69,5 @@ module SirTrevorRails
58
69
  self
59
70
  end
60
71
  end
61
-
62
72
  end
63
73
  end
@@ -1,11 +1,23 @@
1
1
  module SirTrevorRails
2
2
  class BlockArray < Array
3
3
 
4
- def self.from_json(str, parent = nil)
5
- blocks = MultiJson.load(str, symbolize_keys: true)
6
- blocks = blocks[:data] if blocks.is_a?(Hash)
7
- blocks.map! { |block_obj| SirTrevorRails::Block.from_hash(block_obj, parent) }
8
- new blocks
4
+ def self.from_json(blocks, parent = nil)
5
+ if blocks.is_a?(String)
6
+ blocks = MultiJson.load(blocks, symbolize_keys: true)
7
+ end
8
+
9
+ if blocks.is_a?(Hash)
10
+ blocks = blocks[:data] || blocks['data'] or
11
+ raise IndexError, "No block data found"
12
+ end
13
+
14
+ new blocks.map { |obj|
15
+ SirTrevorRails::Block.from_hash(obj.deep_symbolize_keys, parent)
16
+ }
17
+ end
18
+
19
+ def to_s
20
+ {data: as_json}.to_json
9
21
  end
10
22
 
11
23
  def has_block_of_type?(type)
@@ -25,11 +25,13 @@ module SirTrevorRails
25
25
  "@" << screen_name
26
26
  end
27
27
 
28
- def profile_image_url(size="bigger")
29
- "//twitter.com/api/users/profile_image/" << self.user[:screen_name] <<
30
- "?size=" << size
28
+ def profile_image_url
29
+ # TODO: add support for different size images: https://dev.twitter.com/overview/general/user-profile-images-and-banners
30
+
31
+ # Split the URL to omit the protocol and let the browser define the context (note: assumes asset is available over both HTTP and HTTPS)
32
+ "//" << self.user[:profile_image_url].split("://")[1]
31
33
  end
32
34
 
33
35
  end
34
36
  end
35
- end
37
+ end
@@ -2,15 +2,34 @@ module SirTrevorRails
2
2
  module Helpers
3
3
  module ViewHelper
4
4
  extend ActiveSupport::Concern
5
+ included do
6
+ include ActionView::Helpers::SanitizeHelper
7
+ end
8
+
9
+ def sir_trevor_format(text, format: :markdown)
10
+ if format.to_s.to_sym == :html
11
+ sir_trevor_html(text)
12
+ else
13
+ sir_trevor_markdown(text)
14
+ end
15
+ end
16
+
17
+ def sir_trevor_html(text)
18
+ sanitize(text, tags: %w(b i a br p))
19
+ end
5
20
 
6
21
  def sir_trevor_markdown(text)
7
- Redcarpet::Render::HTML.new(hard_wrap: true, filter_html: true,
22
+ rndr = CustomMarkdownFormatter.new(hard_wrap: true, filter_html: true,
8
23
  autolink: true, no_intraemphasis: true,
9
24
  fenced_code: true)
10
25
 
11
- markdown = Redcarpet::Markdown.new(CustomMarkdownFormatter)
26
+ markdown = Redcarpet::Markdown.new(rndr)
12
27
  markdown.render(text).html_safe
13
28
  end
29
+
30
+ def without_p_wrap(html)
31
+ Regexp.new('^<p>(.*)<\/p>$').match(html)[1].html_safe rescue html
32
+ end
14
33
  end
15
34
  end
16
35
  end
@@ -1,3 +1,3 @@
1
1
  module SirTrevorRails
2
- VERSION = "0.5.0b1"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -5,9 +5,9 @@ module SirTrevorRails
5
5
  super("app/views")
6
6
  end
7
7
 
8
- def find_templates(name, prefix, partial, details)
9
- super(name, prefix.gsub(/^(.)+(sir_trevor)/, '\2'), partial, details)
8
+ def find_templates(name, prefix, *args)
9
+ super(name, prefix.gsub(/^(.)+(sir_trevor)/, '\2'), *args)
10
10
  end
11
11
 
12
12
  end
13
- end
13
+ end
@@ -31,9 +31,11 @@ Gem::Specification.new do |spec|
31
31
  spec.add_development_dependency "pry-rails"
32
32
  spec.add_development_dependency "combustion"
33
33
  spec.add_development_dependency "sqlite3"
34
+ spec.add_development_dependency "capybara"
35
+ spec.add_development_dependency "launchy"
34
36
 
35
- spec.add_dependency "rails"
37
+ spec.add_dependency "rails", ">= 3", "< 5"
36
38
  spec.add_dependency "redcarpet", ">= 2.0.1", "< 4"
37
39
  spec.add_dependency "twitter-text", "~> 1.4"
38
-
40
+ spec.add_dependency 'multi_json', '~> 1.0'
39
41
  end
@@ -0,0 +1,7 @@
1
+ {
2
+ "type": "custom",
3
+ "data": {
4
+ "text": "Body",
5
+ "title": "Name"
6
+ }
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "type": "heading",
3
+ "data": {
4
+ "text": "Heading <i>1</i>",
5
+ "format": "html"
6
+ }
7
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "type": "heading",
3
+ "data": {
4
+ "text": "Heading _1_"
5
+ }
6
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "type":"image",
3
+ "data":{
4
+ "id":1,
5
+ "updated_at":"2015-01-01T01:00:00+01:00",
6
+ "created_at":"2015-01-01T01:00:00+01:00",
7
+ "file":{
8
+ "url":"http://placeimg.com/1000/600/any",
9
+ "thumb":{
10
+ "url":"http://placeimg.com/100/100/any"
11
+ },
12
+ "large":{
13
+ "url":"http://placeimg.com/800/400/any"
14
+ },
15
+ "xlarge":{
16
+ "url":"http://placeimg.com/1000/600/any"
17
+ },
18
+ "medium":{
19
+ "url":"http://placeimg.com/600/300/any"
20
+ },
21
+ "mobile":{
22
+ "url":"http://placeimg.com/800/400/any"
23
+ }
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "type": "list",
3
+ "data": {
4
+ "listItems": [
5
+ { "content": "List Item 1" },
6
+ { "content": "<b>List Item 2</b>" }
7
+ ],
8
+ "format": "html"
9
+ }
10
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "type": "list",
3
+ "data": {
4
+ "text": " - List Item 1\n - **List Item 2**\n"
5
+ }
6
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "type": "quote",
3
+ "data": {
4
+ "text": "This is quoted text",
5
+ "cite": "Author",
6
+ "format": "html"
7
+ }
8
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "type": "quote",
3
+ "data": {
4
+ "text": "> This is quoted text",
5
+ "cite": "Author"
6
+ }
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "type": "text",
3
+ "data": {
4
+ "text": "One <i>two</i> <b>three</b> <a href='http://example.com'>four</a>",
5
+ "format": "html"
6
+ }
7
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "type": "text",
3
+ "data": {
4
+ "text": "One _two_ **three** [four](http://example.com)"
5
+ }
6
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "type":"tweet",
3
+ "data":{
4
+ "user":{
5
+ "profile_image_url":"http://pbs.twimg.com/profile_images/613336689893896192/5nQjxG2o_normal.jpg",
6
+ "profile_image_url_https":"https://pbs.twimg.com/profile_images/613336689893896192/5nQjxG2o_normal.jpg",
7
+ "screen_name":"SachseInTheCity",
8
+ "name":"Nelson Sachse"
9
+ },
10
+ "id":"615846875078504448",
11
+ "text":"There's no pain without some gain !\n\nlet's run \\o/",
12
+ "created_at":"Tue Jun 30 11:38:31 +0000 2015",
13
+ "entities":{
14
+ "hashtags":[],
15
+ "symbols":[],
16
+ "user_mentions":[],
17
+ "urls":[]
18
+ },
19
+ "status_url":"https://twitter.com/SachseInTheCity/status/615846875078504448"
20
+ }
21
+ }
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ class EditingSpec < ActionDispatch::IntegrationTest
4
+ describe 'Edit form' do
5
+ let(:post) { Post.create(body: blocks_json(:text_html)) }
6
+
7
+ it 'serializes BlockArray to correct json' do
8
+ visit edit_post_path(post)
9
+ expect { find_field('Body').value == {data: post.body.as_json}.to_json }
10
+ end
11
+
12
+ it 'serializes empty BlockArray to empty array json' do
13
+ post = Post.create()
14
+
15
+ visit edit_post_path(post)
16
+ expect { find_field('Body').value == "{\"data\":[]}" }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,121 @@
1
+ require 'spec_helper'
2
+
3
+ class RenderingSpec < ActionDispatch::IntegrationTest
4
+ describe 'SirTrevorContent rendering' do
5
+ it 'renders all blocks as separate divs' do
6
+ post = Post.create(body: blocks_json(:list_html, :text_html))
7
+ visit post_path(post)
8
+
9
+ expect { all('.st__content-block').length == 2 }
10
+ end
11
+ end
12
+
13
+ describe 'Text block' do
14
+ it 'renders markdown format' do
15
+ create_and_visit(blocks_json(:text_markdown))
16
+
17
+ expect { find_block(:text).text == "One two three four" }
18
+ expect { find_block(:text).find('em').text == "two" }
19
+ expect { find_block(:text).find('strong').text == "three" }
20
+ expect { find_block(:text).find('a').text == "four" }
21
+ end
22
+
23
+ it 'renders html format' do
24
+ create_and_visit(blocks_json(:text_html))
25
+
26
+ expect { find_block(:text).text == "One two three four" }
27
+ expect { find_block(:text).find('i').text == "two" }
28
+ expect { find_block(:text).find('b').text == "three" }
29
+ expect { find_block(:text).find('a').text == "four" }
30
+ end
31
+ end
32
+
33
+ describe 'List block' do
34
+ it 'renders markdown format' do
35
+ create_and_visit(blocks_json(:list_markdown))
36
+
37
+ expect { find_block(:list).text == "List Item 1 List Item 2" }
38
+ expect { find_block(:list).all('li').first.text == "List Item 1" }
39
+ expect { find_block(:list).all('li').last.text == "List Item 2" }
40
+ expect { find_block(:list).first('strong').text == "List Item 2" }
41
+ end
42
+
43
+ it 'renders html format' do
44
+ create_and_visit(blocks_json(:list_html))
45
+
46
+ expect { find_block(:list).text == "List Item 1 List Item 2" }
47
+ expect { find_block(:list).all('li').first.text == "List Item 1" }
48
+ expect { find_block(:list).all('li').last.text == "List Item 2" }
49
+ expect { find_block(:list).first('b').text == "List Item 2" }
50
+ end
51
+ end
52
+
53
+ describe 'Image block' do
54
+ it 'renders correctly' do
55
+ create_and_visit(blocks_json(:image))
56
+ expect { find_block(:image).find("img")[:src] == "http://placeimg.com/1000/600/any" }
57
+ end
58
+ end
59
+
60
+ describe 'Heading block' do
61
+ it 'renders markdown format' do
62
+ create_and_visit(blocks_json(:heading_markdown))
63
+
64
+ expect { find_block(:heading).text == "Heading 1" }
65
+ expect { find_block(:heading).find('em').text == "1" }
66
+ end
67
+
68
+ it 'renders html format' do
69
+ create_and_visit(blocks_json(:heading_html))
70
+
71
+ expect { find_block(:heading).text == "Heading 1" }
72
+ expect { find_block(:heading).find('i').text == "1" }
73
+ end
74
+ end
75
+
76
+ describe 'Quote block' do
77
+ it 'renders markdown format' do
78
+ create_and_visit(blocks_json(:quote_markdown))
79
+
80
+ expect { find_block(:quote).find('blockquote').text == "This is quoted text" }
81
+ expect { find_block(:quote).find('cite').text == "– Author" }
82
+ end
83
+
84
+ it 'renders html format' do
85
+ create_and_visit(blocks_json(:quote_html))
86
+
87
+ expect { find_block(:quote).find('blockquote').text == "This is quoted text" }
88
+ expect { find_block(:quote).find('cite').text == "– Author" }
89
+ end
90
+ end
91
+
92
+ describe 'Tweet block' do
93
+ it 'renders correctly' do
94
+ create_and_visit(blocks_json(:tweet))
95
+ expect { find_block(:tweet).all('a')[0][:href] == "//twitter.com/SachseInTheCity" }
96
+ expect { find_block(:tweet).find('img')[:src] == "//pbs.twimg.com/profile_images/613336689893896192/5nQjxG2o_normal.jpg" }
97
+
98
+ expect { find_block(:tweet).find('p').text == "There's no pain without some gain ! let's run \\o/" }
99
+
100
+ expect { find_block(:tweet).find('cite').text == "From @SachseInTheCity on Twitter:" }
101
+
102
+ expect { find_block(:tweet).all('a')[1][:href] == "//twitter.com/SachseInTheCity" }
103
+ expect { find_block(:tweet).all('a')[1].text == "@SachseInTheCity" }
104
+
105
+ expect { find_block(:tweet).find('time')[:datetime] == "Tue Jun 30 11:38:31 +0000 2015" }
106
+ expect { find_block(:tweet).find('time').text == "(#{Time.zone.parse("Tue Jun 30 11:38:31 +0000 2015")})" }
107
+
108
+ expect { find_block(:tweet).all('a')[2][:href] == "https://twitter.com/SachseInTheCity/status/615846875078504448" }
109
+ expect { find_block(:tweet).all('a')[2].text == Time.zone.parse("Tue Jun 30 11:38:31 +0000 2015").to_s }
110
+ end
111
+ end
112
+
113
+ def create_and_visit(json)
114
+ post = Post.create(body: json)
115
+ visit post_path(post)
116
+ end
117
+
118
+ def find_block(type)
119
+ first(".st__content-block--#{type}")
120
+ end
121
+ end
@@ -0,0 +1,33 @@
1
+ class PostsController < ActionController::Base
2
+ def new
3
+ @post = Post.new
4
+ end
5
+
6
+ def edit
7
+ @post = Post.find(params[:id])
8
+ end
9
+
10
+ def create
11
+ @post = Post.new(post_attributes)
12
+
13
+ if @post.save
14
+ redirect_to post_show_path(@post)
15
+ else
16
+ render :new
17
+ end
18
+ end
19
+
20
+ def update
21
+ @post = Post.find(params[:id])
22
+
23
+ if @post.update_attributes(post_attributes)
24
+ redirect_to post_show_path(@post)
25
+ else
26
+ render :edit
27
+ end
28
+ end
29
+
30
+ def show
31
+ @post = Post.find(params[:id])
32
+ end
33
+ end
@@ -1,2 +1,4 @@
1
1
  class Post < ActiveRecord::Base
2
+ include SirTrevorRails::HasSirTrevorContent
3
+ sir_trevor_content :body
2
4
  end
@@ -0,0 +1,3 @@
1
+ <%= form.label :body %>
2
+ <%= form.text_area :body %>
3
+ <%= form.submit :save %>
@@ -0,0 +1,3 @@
1
+ <%= form_for @post do |form| %>
2
+ <%= render form %>
3
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <%= form_for @post do |form| %>
2
+ <%= render form %>
3
+ <% end %>
@@ -0,0 +1 @@
1
+ <%= render @post.body %>
@@ -1,3 +1,3 @@
1
1
  Rails.application.routes.draw do
2
- #
2
+ resources :posts
3
3
  end
data/spec/spec_helper.rb CHANGED
@@ -1,12 +1,18 @@
1
1
  require 'sir_trevor_rails'
2
2
  require 'combustion'
3
+
4
+ Combustion.initialize! :active_record, :action_view do
5
+ config.active_support.test_order = :random
6
+ end
7
+
8
+
9
+ require 'capybara/rails'
3
10
  require 'minitest/autorun'
4
11
  require 'minitest/spec'
5
12
  require 'minitest/pride'
6
13
  require 'wrong/adapters/minitest'
7
14
  require 'pry'
8
15
 
9
- Combustion.initialize! :active_record, :action_view
10
16
 
11
17
  Wrong.config.alias_assert :expect, override: true
12
18
 
@@ -14,3 +20,38 @@ MiniTest::Spec.class_eval do
14
20
  include Wrong::Assert
15
21
  include Wrong::Helpers
16
22
  end
23
+
24
+ module SirTrevorRails::Fixtures
25
+ def blocks_json(*names)
26
+ "[" + names.map { |name| block_json(name) }.join(',') + "]"
27
+ end
28
+
29
+ def block_json(name)
30
+ filename = File.join('fixtures', 'blocks', name.to_s + '.json')
31
+ path = File.expand_path(filename, File.dirname(__FILE__))
32
+
33
+ File.read(path)
34
+ end
35
+
36
+ def block_hash(name)
37
+ MultiJson.load(block_json(name))
38
+ end
39
+
40
+ def blocks_hash(*names)
41
+ MultiJson.load(blocks_json(*names))
42
+ end
43
+ end
44
+
45
+ class ActiveSupport::TestCase
46
+ extend MiniTest::Spec::DSL
47
+ include Wrong::Assert
48
+ include Wrong::Helpers
49
+
50
+ include SirTrevorRails::Fixtures
51
+ include SirTrevorRails # bad, but we don't have to use full class names
52
+ end
53
+
54
+ class ActionDispatch::IntegrationTest
55
+ # Make the Capybara DSL available in all integration tests
56
+ include Capybara::DSL
57
+ end
@@ -1,11 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
- module SirTrevorRails
3
+ class BlockArraySpec < ActiveSupport::TestCase
4
4
  describe BlockArray do
5
5
  describe 'intialization' do
6
6
  subject { BlockArray }
7
- let(:text_block) { {type: 'text', data: {text: 'Lorem ipsum'}} }
8
- let(:heading_block) { {type: 'heading', data: {text: 'Heading 1'}} }
9
7
 
10
8
  it 'can be initialized with an empty JSON array' do
11
9
  array = subject.from_json('[]')
@@ -13,25 +11,37 @@ module SirTrevorRails
13
11
  end
14
12
 
15
13
  it 'can be initialized with multiple block hashes' do
16
- json = [text_block, heading_block].to_json
17
- array = subject.from_json(json)
14
+ array = subject.from_json(blocks_json(:text_html, :heading_html))
18
15
 
19
16
  expect { array.is_a? BlockArray }
20
17
  expect { array.size == 2 }
21
18
  end
19
+
20
+ it 'can be initialized with a hash' do
21
+ ar = subject.from_json({data: blocks_hash(:text_html, :image)})
22
+
23
+ expect { ar.is_a? BlockArray }
24
+ expect { ar.first.type == :text }
25
+ expect { ar.last.type == :image }
26
+ end
27
+
28
+ it 'can be initialized with an array' do
29
+ ar = subject.from_json(blocks_hash(:text_html, :image))
30
+
31
+ expect { ar.is_a? BlockArray }
32
+ expect { ar.first.type == :text }
33
+ expect { ar.last.type == :image }
34
+ end
22
35
  end
23
36
 
24
37
  describe 'instance methods' do
25
- let(:text_block) { {type: 'text', data: {text: 'First text block'}} }
26
- let(:other_text_block) { {type: 'text', data: {text: 'Other text block'}} }
27
- let(:image_block) { {type: 'image', data: {file: {url: 'http://example.com'}}} }
28
- let(:heading_block) { {type: 'heading', data: {text: 'Heading 1'}} }
29
- let(:json) { [image_block, text_block, heading_block, other_text_block].to_json }
38
+ let(:json) { blocks_json :image, :text_html, :heading_html, :text_markdown }
30
39
 
31
40
  subject { BlockArray.from_json(json) }
32
41
 
33
42
  describe 'has_block_of_type?' do
34
43
  it 'can be called with nonexistent block type' do
44
+ # no assert needed, if anything was wrong it would raise an exception
35
45
  subject.has_block_of_type? :nonexistent
36
46
  end
37
47
 
@@ -47,7 +57,7 @@ module SirTrevorRails
47
57
  describe 'first_block_of_type' do
48
58
  it 'returns first block of given type in the array' do
49
59
  block = subject.first_block_of_type(:text)
50
- assert { block.text == 'First text block' }
60
+ assert { block.text.starts_with? "One <i>two</i>" }
51
61
  end
52
62
 
53
63
  it 'returns nil if called with block type not in the array' do
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- module SirTrevorRails
3
+ class BlockSpec < ActiveSupport::TestCase
4
4
  describe Block do
5
5
  describe '.block_class' do
6
6
  subject { Block }
@@ -46,11 +46,17 @@ module SirTrevorRails
46
46
  end
47
47
 
48
48
  describe 'JSON representation' do
49
- let(:source_hash) { { type: 'test', data: {} } }
50
- let(:block) { Block.from_hash(source_hash, nil) }
49
+ let(:empty_source_hash) { { type: 'test', data: {} } }
50
+ let(:empty_block) { Block.from_hash(empty_source_hash, nil) }
51
+ let(:data_source_hash) { { type: 'test', data: { one: 2, three: 4 } } }
52
+ let(:data_block) { Block.from_hash(data_source_hash, nil) }
51
53
 
52
54
  it 'returns source hash when #as_json is called' do
53
- expect { block.as_json == source_hash }
55
+ expect { empty_block.as_json == empty_source_hash }
56
+ end
57
+
58
+ it 'serializes block data' do
59
+ expect { data_block.as_json == data_source_hash }
54
60
  end
55
61
  end
56
62
 
@@ -4,16 +4,18 @@ require 'spec_helper'
4
4
  # `spec/internal/app/models/custom_block.rb`, just like it would in real
5
5
  # Rails app.
6
6
 
7
- describe CustomBlock do
8
- describe "SirTrevorRails::Block" do
9
- let(:custom_block_hash) { {type: 'custom', data: {text: 'Body', title: 'name'}} }
7
+ class CustomBlockTest < ActiveSupport::TestCase
8
+ describe CustomBlock do
9
+ describe "SirTrevorRails::Block" do
10
+ let(:custom_block_hash) { {type: 'custom', data: {text: 'Body', title: 'name'}} }
10
11
 
11
- it 'initialises class defined in the file' do
12
- block = SirTrevorRails::Block.from_hash(custom_block_hash, nil)
12
+ it 'initialises class defined in the file' do
13
+ block = SirTrevorRails::Block.from_hash(block_hash(:custom), nil)
13
14
 
14
- assert { block.is_a? CustomBlock }
15
- assert { block.respond_to? :upcase_title }
16
- assert { block.upcase_title == "NAME" }
15
+ assert { block.is_a? CustomBlock }
16
+ assert { block.respond_to? :upcase_title }
17
+ assert { block.upcase_title == "NAME" }
18
+ end
17
19
  end
18
20
  end
19
21
  end
@@ -1,15 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
- module SirTrevorRails
3
+ class HasSirTrevorContentSpec < ActiveSupport::TestCase
4
4
  describe 'HasSirTrevorContent' do
5
5
  describe 'initialized for body field' do
6
- before do
7
- Post.send(:include, HasSirTrevorContent)
8
- Post.send(:sir_trevor_content, :body)
9
- end
10
-
11
6
  let(:post) { Post.new }
12
- let(:text_block) { {type: 'text', data: {text: 'Lorem ipsum'}} }
13
7
 
14
8
  it 'returns BlockArray for initialised field' do
15
9
  assert { post.body.is_a? BlockArray }
@@ -28,7 +22,7 @@ module SirTrevorRails
28
22
  end
29
23
 
30
24
  it 'returns blocks if field is SirTrevor JSON' do
31
- post.body = [text_block].to_json
25
+ post.body = blocks_json(:text_html)
32
26
 
33
27
  assert { post.body.size == 1 }
34
28
  assert { post.body.first.is_a? Block }
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ module SirTrevorRails
4
+ describe Helpers do
5
+ let(:subject) { Class.new.send(:include, Helpers::ViewHelper).new }
6
+
7
+ describe 'sir_trevor_format' do
8
+ it 'is callable' do
9
+ expect { subject.respond_to? :sir_trevor_format }
10
+ end
11
+
12
+ it 'calls sir_trevor_markdown if format is not provided' do
13
+ subject.stub :sir_trevor_markdown, 'markdown-called' do
14
+ expect { subject.sir_trevor_format('test') == 'markdown-called' }
15
+ end
16
+ end
17
+
18
+ it 'calls sir_trevor_markdown if format == markdown' do
19
+ subject.stub :sir_trevor_markdown, 'markdown-called' do
20
+ expect { subject.sir_trevor_format('test', format: :markdown) == 'markdown-called' }
21
+ end
22
+ end
23
+
24
+ it 'calls sir_trevor_html if format == html' do
25
+ subject.stub :sir_trevor_html, 'html-called' do
26
+ expect { subject.sir_trevor_format('test', format: :html) == 'html-called' }
27
+ end
28
+ end
29
+ end
30
+
31
+ describe 'sir_trevor_html' do
32
+ it 'removes not allowed html tags' do
33
+ html = '<strong><i>Hello<br><a href="http://example.com/">World</a>!</i></strong>'
34
+ escaped_html = "<i>Hello<br><a href=\"http://example.com/\">World</a>!</i>"
35
+ expect { subject.sir_trevor_html(html) != html }
36
+ expect { subject.sir_trevor_html(html) == escaped_html }
37
+ end
38
+ it 'retains formatting html tags' do
39
+ html = '<b><i>Hello<br><a href="http://example.com/">World</a>!</i></b>'
40
+ expect { subject.sir_trevor_html(html) == html }
41
+ end
42
+ end
43
+ end
44
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sir_trevor_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0b1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Sprinz
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2015-02-25 00:00:00.000000000 Z
14
+ date: 2016-01-28 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler
@@ -112,19 +112,53 @@ dependencies:
112
112
  - !ruby/object:Gem::Version
113
113
  version: '0'
114
114
  - !ruby/object:Gem::Dependency
115
- name: rails
115
+ name: capybara
116
116
  requirement: !ruby/object:Gem::Requirement
117
117
  requirements:
118
118
  - - ">="
119
119
  - !ruby/object:Gem::Version
120
120
  version: '0'
121
- type: :runtime
121
+ type: :development
122
122
  prerelease: false
123
123
  version_requirements: !ruby/object:Gem::Requirement
124
124
  requirements:
125
125
  - - ">="
126
126
  - !ruby/object:Gem::Version
127
127
  version: '0'
128
+ - !ruby/object:Gem::Dependency
129
+ name: launchy
130
+ requirement: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ type: :development
136
+ prerelease: false
137
+ version_requirements: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: rails
144
+ requirement: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '3'
149
+ - - "<"
150
+ - !ruby/object:Gem::Version
151
+ version: '5'
152
+ type: :runtime
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '3'
159
+ - - "<"
160
+ - !ruby/object:Gem::Version
161
+ version: '5'
128
162
  - !ruby/object:Gem::Dependency
129
163
  name: redcarpet
130
164
  requirement: !ruby/object:Gem::Requirement
@@ -159,6 +193,20 @@ dependencies:
159
193
  - - "~>"
160
194
  - !ruby/object:Gem::Version
161
195
  version: '1.4'
196
+ - !ruby/object:Gem::Dependency
197
+ name: multi_json
198
+ requirement: !ruby/object:Gem::Requirement
199
+ requirements:
200
+ - - "~>"
201
+ - !ruby/object:Gem::Version
202
+ version: '1.0'
203
+ type: :runtime
204
+ prerelease: false
205
+ version_requirements: !ruby/object:Gem::Requirement
206
+ requirements:
207
+ - - "~>"
208
+ - !ruby/object:Gem::Version
209
+ version: '1.0'
162
210
  description:
163
211
  email:
164
212
  - andrew@madebymany.co.uk
@@ -170,6 +218,7 @@ extensions: []
170
218
  extra_rdoc_files: []
171
219
  files:
172
220
  - ".gitignore"
221
+ - ".travis.yml"
173
222
  - CHANGELOG
174
223
  - Gemfile
175
224
  - LICENSE.txt
@@ -188,8 +237,8 @@ files:
188
237
  - config/initializers/validators.rb
189
238
  - lib/generators/sir_trevor_rails/block/block_generator.rb
190
239
  - lib/generators/sir_trevor_rails/block/templates/_block.html.erb
191
- - lib/generators/sir_trevor_rails/block/templates/_block.js
192
- - lib/generators/sir_trevor_rails/block/templates/_block.rb
240
+ - lib/generators/sir_trevor_rails/block/templates/_block.js.erb
241
+ - lib/generators/sir_trevor_rails/block/templates/_block.rb.erb
193
242
  - lib/generators/sir_trevor_rails/views/views_generator.rb
194
243
  - lib/sir_trevor_rails.rb
195
244
  - lib/sir_trevor_rails/block.rb
@@ -202,8 +251,26 @@ files:
202
251
  - lib/sir_trevor_rails/version.rb
203
252
  - lib/sir_trevor_rails/view_resolver.rb
204
253
  - sir_trevor_rails.gemspec
254
+ - spec/fixtures/blocks/custom.json
255
+ - spec/fixtures/blocks/heading_html.json
256
+ - spec/fixtures/blocks/heading_markdown.json
257
+ - spec/fixtures/blocks/image.json
258
+ - spec/fixtures/blocks/list_html.json
259
+ - spec/fixtures/blocks/list_markdown.json
260
+ - spec/fixtures/blocks/quote_html.json
261
+ - spec/fixtures/blocks/quote_markdown.json
262
+ - spec/fixtures/blocks/text_html.json
263
+ - spec/fixtures/blocks/text_markdown.json
264
+ - spec/fixtures/blocks/tweet.json
265
+ - spec/integration/editing_spec.rb
266
+ - spec/integration/rendering_spec.rb
267
+ - spec/internal/app/controllers/posts_controller.rb
205
268
  - spec/internal/app/models/custom_block.rb
206
269
  - spec/internal/app/models/post.rb
270
+ - spec/internal/app/views/posts/_form.html.erb
271
+ - spec/internal/app/views/posts/edit.html.erb
272
+ - spec/internal/app/views/posts/new.html.erb
273
+ - spec/internal/app/views/posts/show.html.erb
207
274
  - spec/internal/config/database.yml
208
275
  - spec/internal/config/routes.rb
209
276
  - spec/internal/db/schema.rb
@@ -214,6 +281,7 @@ files:
214
281
  - spec/unit/block_spec.rb
215
282
  - spec/unit/custom_block_spec.rb
216
283
  - spec/unit/has_sir_trevor_content_spec.rb
284
+ - spec/unit/helpers_spec.rb
217
285
  homepage: https://github.com/madebymany/sir-trevor-rails
218
286
  licenses:
219
287
  - MIT
@@ -229,18 +297,36 @@ required_ruby_version: !ruby/object:Gem::Requirement
229
297
  version: '0'
230
298
  required_rubygems_version: !ruby/object:Gem::Requirement
231
299
  requirements:
232
- - - ">"
300
+ - - ">="
233
301
  - !ruby/object:Gem::Version
234
- version: 1.3.1
302
+ version: '0'
235
303
  requirements: []
236
304
  rubyforge_project:
237
- rubygems_version: 2.2.2
305
+ rubygems_version: 2.4.5.1
238
306
  signing_key:
239
307
  specification_version: 4
240
308
  summary: A ruby wrapper for sir trevor content
241
309
  test_files:
310
+ - spec/fixtures/blocks/custom.json
311
+ - spec/fixtures/blocks/heading_html.json
312
+ - spec/fixtures/blocks/heading_markdown.json
313
+ - spec/fixtures/blocks/image.json
314
+ - spec/fixtures/blocks/list_html.json
315
+ - spec/fixtures/blocks/list_markdown.json
316
+ - spec/fixtures/blocks/quote_html.json
317
+ - spec/fixtures/blocks/quote_markdown.json
318
+ - spec/fixtures/blocks/text_html.json
319
+ - spec/fixtures/blocks/text_markdown.json
320
+ - spec/fixtures/blocks/tweet.json
321
+ - spec/integration/editing_spec.rb
322
+ - spec/integration/rendering_spec.rb
323
+ - spec/internal/app/controllers/posts_controller.rb
242
324
  - spec/internal/app/models/custom_block.rb
243
325
  - spec/internal/app/models/post.rb
326
+ - spec/internal/app/views/posts/_form.html.erb
327
+ - spec/internal/app/views/posts/edit.html.erb
328
+ - spec/internal/app/views/posts/new.html.erb
329
+ - spec/internal/app/views/posts/show.html.erb
244
330
  - spec/internal/config/database.yml
245
331
  - spec/internal/config/routes.rb
246
332
  - spec/internal/db/schema.rb
@@ -251,4 +337,4 @@ test_files:
251
337
  - spec/unit/block_spec.rb
252
338
  - spec/unit/custom_block_spec.rb
253
339
  - spec/unit/has_sir_trevor_content_spec.rb
254
- has_rdoc:
340
+ - spec/unit/helpers_spec.rb
@@ -1,2 +0,0 @@
1
- class ExampleBlock < SirTrevorRails::Block
2
- end