ninetails 0.0.2 → 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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +27 -4
  3. data/Rakefile +3 -3
  4. data/app/components/ninetails/element.rb +5 -1
  5. data/app/components/ninetails/element_definition.rb +2 -2
  6. data/app/components/ninetails/property_type.rb +0 -2
  7. data/app/components/ninetails/{section_template.rb → section.rb} +6 -8
  8. data/app/controllers/ninetails/application_controller.rb +1 -0
  9. data/app/controllers/ninetails/page_revisions_controller.rb +2 -3
  10. data/app/controllers/ninetails/pages_controller.rb +20 -1
  11. data/app/controllers/ninetails/sections_controller.rb +35 -0
  12. data/app/models/ninetails/page.rb +3 -16
  13. data/app/models/ninetails/page_revision.rb +2 -8
  14. data/app/models/ninetails/page_revision_section.rb +1 -1
  15. data/app/models/ninetails/page_section.rb +40 -0
  16. data/app/views/ninetails/page_revisions/index.json.jbuilder +1 -0
  17. data/app/views/ninetails/pages/_page.json.jbuilder +13 -0
  18. data/app/views/ninetails/pages/index.json.jbuilder +1 -0
  19. data/app/views/ninetails/pages/show.json.jbuilder +1 -0
  20. data/app/views/ninetails/sections/_section.json.jbuilder +1 -0
  21. data/app/views/ninetails/sections/validate.json.jbuilder +3 -0
  22. data/config/routes.rb +9 -5
  23. data/config/spring.rb +1 -0
  24. data/db/migrate/20151120120538_rename_sections_to_page_sections.rb +5 -0
  25. data/lib/ninetails/config.rb +8 -0
  26. data/lib/ninetails/engine.rb +5 -0
  27. data/lib/ninetails/key_conversion.rb +28 -0
  28. data/lib/ninetails/version.rb +1 -1
  29. data/lib/tasks/ninetails_tasks.rake +2 -2
  30. data/spec/components/element_spec.rb +3 -3
  31. data/spec/components/property_spec.rb +2 -2
  32. data/spec/components/property_type_spec.rb +5 -5
  33. data/spec/components/section_spec.rb +2 -2
  34. data/spec/dummy/app/components/{section_template → section}/billboard.rb +2 -2
  35. data/spec/dummy/app/components/{section_template → section}/document_head.rb +2 -2
  36. data/spec/dummy/app/components/section/minimal_billboard.rb +9 -0
  37. data/spec/dummy/bin/rails +5 -0
  38. data/spec/dummy/bin/rake +5 -0
  39. data/spec/dummy/bin/rspec +8 -0
  40. data/spec/dummy/bin/spring +15 -0
  41. data/spec/dummy/config/application.rb +0 -10
  42. data/spec/dummy/config/initializers/ninetails.rb +1 -0
  43. data/spec/dummy/config/spring.rb +1 -0
  44. data/spec/dummy/db/schema.rb +10 -10
  45. data/spec/dummy/log/development.log +2499 -0
  46. data/spec/dummy/log/test.log +55690 -0
  47. data/spec/factories/page_sections.rb +15 -0
  48. data/spec/models/{section_spec.rb → page_section_spec.rb} +17 -36
  49. data/spec/models/page_spec.rb +9 -14
  50. data/spec/requests/middleware_spec.rb +80 -0
  51. data/spec/requests/page_revisions_spec.rb +41 -42
  52. data/spec/requests/pages_spec.rb +54 -1
  53. data/spec/requests/sections_spec.rb +32 -0
  54. data/spec/spec_helper.rb +4 -0
  55. data/spec/support/section_helpers.rb +27 -0
  56. metadata +74 -35
  57. data/app/controllers/ninetails/section_templates_controller.rb +0 -17
  58. data/app/models/ninetails/section.rb +0 -30
  59. data/db/schema.rb +0 -53
  60. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
  61. data/spec/dummy/config/initializers/inflections.rb +0 -16
  62. data/spec/dummy/config/initializers/mime_types.rb +0 -4
  63. data/spec/dummy/config/locales/en.yml +0 -23
  64. data/spec/dummy/public/404.html +0 -67
  65. data/spec/dummy/public/422.html +0 -67
  66. data/spec/dummy/public/500.html +0 -66
  67. data/spec/dummy/public/favicon.ico +0 -0
  68. data/spec/factories/sections.rb +0 -15
  69. data/spec/requests/section_templates_spec.rb +0 -17
@@ -0,0 +1,15 @@
1
+ FactoryGirl.define do
2
+
3
+ factory :document_head_section, class: Ninetails::PageSection do
4
+ type "DocumentHead"
5
+ tags Section::DocumentHead.new.tags
6
+ elements Section::DocumentHead.new.serialize_elements
7
+ end
8
+
9
+ factory :billboard_section, class: Ninetails::PageSection do
10
+ type "Billboard"
11
+ tags Section::Billboard.new.tags
12
+ elements Section::Billboard.new.serialize_elements
13
+ end
14
+
15
+ end
@@ -1,7 +1,7 @@
1
1
  require 'rails_helper'
2
2
 
3
- module SectionTemplate
4
- class TestSection < Ninetails::SectionTemplate
3
+ module Section
4
+ class TestSection < Ninetails::Section
5
5
  located_in :body
6
6
  has_element :headline, Element::Text
7
7
  has_element :button, Element::Button
@@ -9,7 +9,7 @@ module SectionTemplate
9
9
  end
10
10
  end
11
11
 
12
- RSpec.describe Ninetails::Section, type: :model do
12
+ RSpec.describe Ninetails::PageSection, type: :model do
13
13
 
14
14
  describe "deserializing a json blob" do
15
15
  let(:headline) { "Hello world!" }
@@ -48,42 +48,42 @@ RSpec.describe Ninetails::Section, type: :model do
48
48
  }
49
49
  end
50
50
 
51
- let(:section) { Ninetails::Section.new(json) }
51
+ let(:section) { Ninetails::PageSection.new(json) }
52
52
 
53
- it "should create a SectionTemplate instance" do
53
+ it "should create a Section instance" do
54
54
  section.deserialize
55
- expect(section.template).to be_a SectionTemplate::TestSection
55
+ expect(section.section).to be_a Section::TestSection
56
56
  end
57
57
 
58
58
  it "should have an array of elements_instances" do
59
59
  section.deserialize
60
- expect(section.template.elements_instances.size).to eq 3
60
+ expect(section.section.elements_instances.size).to eq 3
61
61
  end
62
62
 
63
63
  it "should include an element for 'text', 'button', and 'link'" do
64
64
  section.deserialize
65
- expect(section.template.elements_instances[0].name).to eq :headline
66
- expect(section.template.elements_instances[1].name).to eq :button
67
- expect(section.template.elements_instances[2].name).to eq :messages
65
+ expect(section.section.elements_instances[0].name).to eq :headline
66
+ expect(section.section.elements_instances[1].name).to eq :button
67
+ expect(section.section.elements_instances[2].name).to eq :messages
68
68
  end
69
69
 
70
70
  it "should be the correct type for 'text', 'button', and 'link'" do
71
71
  section.deserialize
72
- expect(section.template.elements_instances[0].type).to eq Element::Text
73
- expect(section.template.elements_instances[1].type).to eq Element::Button
74
- expect(section.template.elements_instances[2].type).to eq Element::Text
72
+ expect(section.section.elements_instances[0].type).to eq Element::Text
73
+ expect(section.section.elements_instances[1].type).to eq Element::Button
74
+ expect(section.section.elements_instances[2].type).to eq Element::Text
75
75
  end
76
76
 
77
77
  it "should call deserialize on each element" do
78
- expect(SectionTemplate::TestSection.find_element("headline")).to receive(:deserialize).with(json["elements"]["headline"])
79
- expect(SectionTemplate::TestSection.find_element("button")).to receive(:deserialize).with(json["elements"]["button"])
80
- expect(SectionTemplate::TestSection.find_element("messages")).to receive(:deserialize).with(json["elements"]["messages"])
78
+ expect(Section::TestSection.find_element("headline")).to receive(:deserialize).with(json["elements"]["headline"])
79
+ expect(Section::TestSection.find_element("button")).to receive(:deserialize).with(json["elements"]["button"])
80
+ expect(Section::TestSection.find_element("messages")).to receive(:deserialize).with(json["elements"]["messages"])
81
81
  section.deserialize
82
82
  end
83
83
 
84
84
  it "should be possible to reserialize into json" do
85
85
  section.deserialize
86
- serialized = section.template.serialize.with_indifferent_access
86
+ serialized = section.section.serialize.with_indifferent_access
87
87
  expect(serialized[:elements][:headline][:content][:text]).to eq headline
88
88
  expect(serialized[:elements][:messages][0][:content][:text]).to eq first_message
89
89
  expect(serialized[:elements][:messages][1][:content][:text]).to eq second_message
@@ -91,23 +91,4 @@ RSpec.describe Ninetails::Section, type: :model do
91
91
 
92
92
  end
93
93
 
94
- describe "building json" do
95
-
96
- let(:section) { build :document_head_section }
97
- let(:json) do
98
- {
99
- id: section.id,
100
- name: section.name,
101
- type: section.type,
102
- tags: section.tags,
103
- elements: section.elements
104
- }.to_json
105
- end
106
-
107
- it "should build the section into json" do
108
- expect(section.to_builder.target!).to eq json
109
- end
110
-
111
- end
112
-
113
94
  end
@@ -2,23 +2,18 @@ require 'rails_helper'
2
2
 
3
3
  RSpec.describe Ninetails::Page, type: :model do
4
4
 
5
- describe "building json" do
5
+ describe "validations" do
6
6
 
7
- let(:page) { create :page }
8
- let(:json) do
9
- {
10
- page: {
11
- id: page.id,
12
- name: page.name,
13
- url: page.url,
14
- revisionId: page.revision.id,
15
- sections: page.sections_to_builder
16
- }
17
- }.to_json
7
+ it "should require a url" do
8
+ new_page = Ninetails::Page.new url: nil
9
+ expect(new_page.valid?).to be false
10
+ expect(new_page.errors[:url]).to eq ["can't be blank"]
18
11
  end
19
12
 
20
- it "should build the page into json" do
21
- expect(page.to_builder.target!).to eq json
13
+ it "should require a unique url" do
14
+ new_page = Ninetails::Page.new url: create(:page).url
15
+ expect(new_page.valid?).to be false
16
+ expect(new_page.errors[:url]).to eq ["has already been taken"]
22
17
  end
23
18
 
24
19
  end
@@ -0,0 +1,80 @@
1
+ require "rails_helper"
2
+
3
+ module Ninetails
4
+ class TestController < ApplicationController
5
+ def index
6
+ render json: {
7
+ foo_bar: "baz_box",
8
+ hello_world: {
9
+ this_is_dog_fort: "Checking in"
10
+ }
11
+ }
12
+ end
13
+
14
+ def show
15
+ render text: "foo_bar"
16
+ end
17
+ end
18
+ end
19
+
20
+ class DummyController < ActionController::Base
21
+ def index
22
+ render json: {
23
+ this_is_a_key: "thanks"
24
+ }
25
+ end
26
+ end
27
+
28
+ describe "Key conversion middleware" do
29
+
30
+ # Add the Ninetails::TestController and DummyController stuff to routes
31
+ before do
32
+ Ninetails::Engine.routes.send(:eval_block, -> {
33
+ get "/test", to: "test#index"
34
+ get "/test/foo", to: "test#show"
35
+ })
36
+
37
+ Dummy::Application.routes.send(:eval_block, -> {
38
+ get "/dummy", to: "dummy#index"
39
+ })
40
+ end
41
+
42
+ describe "when Ninetails::Config.key_style is camelcase" do
43
+ before do
44
+ Ninetails::Config.key_style = :camelcase
45
+ end
46
+
47
+ it "should convert all keys to camelcase" do
48
+ get "/test"
49
+ expect(json).to have_key "fooBar"
50
+ expect(json["helloWorld"]).to have_key "thisIsDogFort"
51
+ end
52
+
53
+ it "should not modify non-json responses" do
54
+ get "/test/foo"
55
+ expect(response.body).to eq "foo_bar"
56
+ end
57
+
58
+ it "should not modify responses from outside of the Ninetails engine" do
59
+ get "/dummy"
60
+ expect(json).to have_key "this_is_a_key"
61
+ end
62
+ end
63
+
64
+ describe "when Ninetails::Config.key_style is underscore" do
65
+ before do
66
+ Ninetails::Config.key_style = :underscore
67
+ end
68
+
69
+ it "should not modify any response from Ninetails" do
70
+ get "/test"
71
+ expect(json).to have_key "foo_bar"
72
+ end
73
+
74
+ it "should not modify any response from the parent app" do
75
+ get "/dummy"
76
+ expect(json).to have_key "this_is_a_key"
77
+ end
78
+ end
79
+
80
+ end
@@ -25,27 +25,7 @@ describe "Page Revisions API" do
25
25
  "page_revision": {
26
26
  "message": "",
27
27
  "sections":[
28
- {
29
- "name": "",
30
- "type": "DocumentHead",
31
- "tags": {
32
- "position": "head"
33
- },
34
- "elements": {
35
- "title": {
36
- "type": "Text",
37
- "content": {
38
- "text": "iZettle – Accept credit card payments with your iPhone, iPad or Android"
39
- }
40
- },
41
- "description": {
42
- "type": "Meta",
43
- "content": {
44
- "text": "Accept credit card payments on the go with iZettle. All you need is a smartphone or a tablet and our free app."
45
- }
46
- }
47
- }
48
- }
28
+ document_head_section
49
29
  ]
50
30
  }
51
31
  }
@@ -56,27 +36,7 @@ describe "Page Revisions API" do
56
36
  "page_revision": {
57
37
  "message": "",
58
38
  "sections":[
59
- {
60
- "name": "",
61
- "type": "DocumentHead",
62
- "tags": {
63
- "position": "head"
64
- },
65
- "elements": {
66
- "title": {
67
- "type": "Text",
68
- "content": {
69
- "text": ""
70
- }
71
- },
72
- "description": {
73
- "type": "Meta",
74
- "content": {
75
- "text": ""
76
- }
77
- }
78
- }
79
- }
39
+ document_head_section(title: "", description: "")
80
40
  ]
81
41
  }
82
42
  }
@@ -114,4 +74,43 @@ describe "Page Revisions API" do
114
74
  end
115
75
  end
116
76
 
77
+ describe "handling hash keys in camelcase" do
78
+
79
+ let(:camelcased_revision) do
80
+ {
81
+ page_revision: {
82
+ message: "",
83
+ sections: [
84
+ {
85
+ "name": "",
86
+ "type": "MinimalBillboard",
87
+ "tags": {
88
+ "position": "body"
89
+ },
90
+ "elements": {
91
+ "backgroundImage": {
92
+ "type": "Figure",
93
+ "image": {
94
+ "src": "/foobar.jpg",
95
+ "alt": "Hello world"
96
+ }
97
+ }
98
+ }
99
+ }
100
+ ]
101
+ }
102
+ }
103
+ end
104
+
105
+ it "should not cause problems" do
106
+ expect {
107
+ post "/pages/#{page.id}/revisions", camelcased_revision
108
+ }.to change { page.revisions.count }.by(1)
109
+
110
+ expect(response).to be_success
111
+ expect(json["page"]["revisionId"]).to_not be_nil
112
+ end
113
+
114
+ end
115
+
117
116
  end
@@ -5,13 +5,66 @@ describe "Pages API" do
5
5
  let(:page) { create :page }
6
6
  let(:page_url) { "/pages/#{CGI.escape(page.url)}" }
7
7
 
8
+ describe "listing pages" do
9
+ before do
10
+ create_list :page, 5
11
+ end
12
+
13
+ it "should return the correct number of pages" do
14
+ get "/pages"
15
+ expect(response).to be_success
16
+ expect(json["pages"].size).to eq Ninetails::Page.count
17
+ end
18
+
19
+ it "should include the id and url for each page" do
20
+ get "/pages"
21
+
22
+ json["pages"].each do |page|
23
+ expect(page).to have_key "id"
24
+ expect(page).to have_key "url"
25
+ end
26
+ end
27
+ end
28
+
29
+ describe "creating a page" do
30
+
31
+ let(:valid_page_params) do
32
+ {
33
+ page: {
34
+ url: "/foobar"
35
+ }
36
+ }
37
+ end
38
+
39
+ let(:existing_page_url_params) do
40
+ {
41
+ page: {
42
+ url: page.url
43
+ }
44
+ }
45
+ end
46
+
47
+ it "should save the page if it is valid" do
48
+ expect {
49
+ post "/pages", valid_page_params
50
+ }.to change{ Ninetails::Page.count }.by(1)
51
+ end
52
+
53
+ it "should show errors if the url is taken" do
54
+ post "/pages", existing_page_url_params
55
+
56
+ expect(response).to_not be_success
57
+ expect(json["page"]["errors"]["url"]).not_to be_empty
58
+ end
59
+
60
+ end
61
+
8
62
  describe "when not specifying revision" do
9
63
  describe "when the page exists" do
10
64
  it "should return the current revision of a page" do
11
65
  get page_url
12
66
 
13
67
  expect(response).to be_success
14
- expect(response.body).to eq page.to_builder.target!
15
68
  expect(json["page"]["revisionId"]).to eq page.current_revision.id
16
69
  end
17
70
  end
@@ -0,0 +1,32 @@
1
+ require 'rails_helper'
2
+
3
+ describe "Sections API" do
4
+
5
+ it "should list the available sections" do
6
+ get "/sections"
7
+ expect(response).to be_success
8
+ billboard_json = json["sections"].find { |s| s["type"] == "Billboard" }.to_json
9
+ expect(billboard_json).to eq Section::Billboard.new.serialize.to_json
10
+ end
11
+
12
+ it "should be possible to get a section's structure" do
13
+ get "/sections/Billboard"
14
+ expect(response).to be_success
15
+ expect(response.body).to eq Section::Billboard.new.serialize.to_json
16
+ end
17
+
18
+ describe "validating" do
19
+ it "should return no errors when the section is valid" do
20
+ post "/sections/validate", section: document_head_section
21
+ expect(response).to be_success
22
+ expect(json["section"]["elements"]["title"]["content"]).not_to have_key "errors"
23
+ end
24
+
25
+ it "should return errors when the section is invalid" do
26
+ post "/sections/validate", section: document_head_section(title: "")
27
+ expect(response).not_to be_success
28
+ expect(json["section"]["elements"]["title"]["content"]).to have_key "errors"
29
+ end
30
+ end
31
+
32
+ end
@@ -1,5 +1,8 @@
1
1
  ENV["RAILS_ENV"] = "test"
2
2
 
3
+ require "codeclimate-test-reporter"
4
+ CodeClimate::TestReporter.start
5
+
3
6
  Dir["./spec/support/**/*.rb"].sort.each { |f| require f}
4
7
 
5
8
  # This file was generated by the `rails generate rspec:install` command. Conventionally, all
@@ -95,4 +98,5 @@ RSpec.configure do |config|
95
98
  =end
96
99
 
97
100
  config.include Requests::JsonHelpers, type: :request
101
+ config.include SectionHelpers
98
102
  end
@@ -0,0 +1,27 @@
1
+ module SectionHelpers
2
+
3
+ def document_head_section(title: "Title text", description: "Description text")
4
+ {
5
+ "name": "",
6
+ "type": "DocumentHead",
7
+ "tags": {
8
+ "position": "head"
9
+ },
10
+ "elements": {
11
+ "title": {
12
+ "type": "Text",
13
+ "content": {
14
+ "text": title
15
+ }
16
+ },
17
+ "description": {
18
+ "type": "Meta",
19
+ "content": {
20
+ "text": description
21
+ }
22
+ }
23
+ }
24
+ }
25
+ end
26
+
27
+ end