ninetails 0.0.1

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 (93) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +17 -0
  4. data/Rakefile +19 -0
  5. data/app/components/ninetails/element.rb +55 -0
  6. data/app/components/ninetails/element_definition.rb +67 -0
  7. data/app/components/ninetails/property.rb +11 -0
  8. data/app/components/ninetails/property_store.rb +22 -0
  9. data/app/components/ninetails/property_type.rb +27 -0
  10. data/app/components/ninetails/section_template.rb +66 -0
  11. data/app/controllers/ninetails/application_controller.rb +4 -0
  12. data/app/controllers/ninetails/page_revisions_controller.rb +31 -0
  13. data/app/controllers/ninetails/pages_controller.rb +14 -0
  14. data/app/controllers/ninetails/section_templates_controller.rb +17 -0
  15. data/app/models/ninetails/page.rb +37 -0
  16. data/app/models/ninetails/page_revision.rb +28 -0
  17. data/app/models/ninetails/page_revision_section.rb +6 -0
  18. data/app/models/ninetails/section.rb +30 -0
  19. data/config/initializers/error_classes.rb +4 -0
  20. data/config/initializers/json_stuff.rb +9 -0
  21. data/config/routes.rb +9 -0
  22. data/db/migrate/20151006093040_create_pages.rb +10 -0
  23. data/db/migrate/20151006093559_create_page_revisions.rb +8 -0
  24. data/db/migrate/20151006093754_create_sections.rb +11 -0
  25. data/db/migrate/20151006094337_create_page_revision_sections.rb +8 -0
  26. data/db/migrate/20151111085201_add_message_to_page_revisions.rb +5 -0
  27. data/db/schema.rb +53 -0
  28. data/lib/ninetails.rb +4 -0
  29. data/lib/ninetails/engine.rb +37 -0
  30. data/lib/ninetails/version.rb +3 -0
  31. data/lib/tasks/ninetails_tasks.rake +8 -0
  32. data/spec/components/element_definition_spec.rb +104 -0
  33. data/spec/components/element_spec.rb +92 -0
  34. data/spec/components/property_spec.rb +34 -0
  35. data/spec/components/property_store_spec.rb +64 -0
  36. data/spec/components/property_type_spec.rb +69 -0
  37. data/spec/components/section_spec.rb +72 -0
  38. data/spec/dummy/Rakefile +6 -0
  39. data/spec/dummy/app/components/element/button.rb +7 -0
  40. data/spec/dummy/app/components/element/button_icon.rb +8 -0
  41. data/spec/dummy/app/components/element/figure.rb +7 -0
  42. data/spec/dummy/app/components/element/meta.rb +7 -0
  43. data/spec/dummy/app/components/element/text.rb +7 -0
  44. data/spec/dummy/app/components/property/icon.rb +8 -0
  45. data/spec/dummy/app/components/property/image.rb +10 -0
  46. data/spec/dummy/app/components/property/link.rb +11 -0
  47. data/spec/dummy/app/components/property/text.rb +8 -0
  48. data/spec/dummy/app/components/section_template/billboard.rb +14 -0
  49. data/spec/dummy/app/components/section_template/document_head.rb +10 -0
  50. data/spec/dummy/bin/bundle +3 -0
  51. data/spec/dummy/bin/rails +4 -0
  52. data/spec/dummy/bin/rake +4 -0
  53. data/spec/dummy/bin/setup +29 -0
  54. data/spec/dummy/config.ru +4 -0
  55. data/spec/dummy/config/application.rb +35 -0
  56. data/spec/dummy/config/boot.rb +5 -0
  57. data/spec/dummy/config/database.yml +12 -0
  58. data/spec/dummy/config/database.yml.travis +4 -0
  59. data/spec/dummy/config/environment.rb +5 -0
  60. data/spec/dummy/config/environments/development.rb +41 -0
  61. data/spec/dummy/config/environments/production.rb +79 -0
  62. data/spec/dummy/config/environments/test.rb +42 -0
  63. data/spec/dummy/config/initializers/assets.rb +11 -0
  64. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  65. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  66. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  67. data/spec/dummy/config/initializers/inflections.rb +16 -0
  68. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  69. data/spec/dummy/config/initializers/session_store.rb +3 -0
  70. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  71. data/spec/dummy/config/locales/en.yml +23 -0
  72. data/spec/dummy/config/routes.rb +3 -0
  73. data/spec/dummy/config/secrets.yml +22 -0
  74. data/spec/dummy/db/schema.rb +53 -0
  75. data/spec/dummy/public/404.html +67 -0
  76. data/spec/dummy/public/422.html +67 -0
  77. data/spec/dummy/public/500.html +66 -0
  78. data/spec/dummy/public/favicon.ico +0 -0
  79. data/spec/factories/page_revisions.rb +8 -0
  80. data/spec/factories/pages.rb +25 -0
  81. data/spec/factories/sections.rb +15 -0
  82. data/spec/models/page_revision_spec.rb +5 -0
  83. data/spec/models/page_spec.rb +26 -0
  84. data/spec/models/section_spec.rb +113 -0
  85. data/spec/rails_helper.rb +60 -0
  86. data/spec/requests/page_revisions_spec.rb +117 -0
  87. data/spec/requests/pages_spec.rb +68 -0
  88. data/spec/requests/section_templates_spec.rb +17 -0
  89. data/spec/spec_helper.rb +98 -0
  90. data/spec/support/factory_girl.rb +5 -0
  91. data/spec/support/request_helpers.rb +9 -0
  92. data/spec/support/shared_examples.rb +34 -0
  93. metadata +378 -0
@@ -0,0 +1,66 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/500.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>We're sorry, but something went wrong.</h1>
62
+ </div>
63
+ <p>If you are the application owner check the logs for more information.</p>
64
+ </div>
65
+ </body>
66
+ </html>
File without changes
@@ -0,0 +1,8 @@
1
+ FactoryGirl.define do
2
+
3
+ # Default page revision factory has no sections... They're complicated to mock and make valid
4
+ # and all the tests which currently use them, build them manually anyway...
5
+ factory :page_revision, class: Ninetails::PageRevision do
6
+ end
7
+
8
+ end
@@ -0,0 +1,25 @@
1
+ FactoryGirl.define do
2
+
3
+ sequence :url do |n|
4
+ "/foo/#{n}"
5
+ end
6
+
7
+ factory :page, class: Ninetails::Page do
8
+ association :current_revision, factory: :page_revision
9
+ name "Home"
10
+ url
11
+
12
+ factory :page_with_revisions do
13
+ transient do
14
+ revisions_count 5
15
+ end
16
+
17
+ after :create do |page, evaluator|
18
+ # -1 because the page is created with a 'current_revision', so when you say revisions_count: 10,
19
+ # you'll only end up with 10 revisions instead of 11..
20
+ create_list :page_revision, evaluator.revisions_count - 1, page: page
21
+ end
22
+ end
23
+ end
24
+
25
+ end
@@ -0,0 +1,15 @@
1
+ FactoryGirl.define do
2
+
3
+ factory :document_head_section, class: Ninetails::Section do
4
+ type "DocumentHead"
5
+ tags SectionTemplate::DocumentHead.new.tags
6
+ elements SectionTemplate::DocumentHead.new.serialize_elements
7
+ end
8
+
9
+ factory :billboard_section, class: Ninetails::Section do
10
+ type "Billboard"
11
+ tags SectionTemplate::Billboard.new.tags
12
+ elements SectionTemplate::Billboard.new.serialize_elements
13
+ end
14
+
15
+ end
@@ -0,0 +1,5 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe Ninetails::PageRevision, type: :model do
4
+
5
+ end
@@ -0,0 +1,26 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe Ninetails::Page, type: :model do
4
+
5
+ describe "building json" do
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
18
+ end
19
+
20
+ it "should build the page into json" do
21
+ expect(page.to_builder.target!).to eq json
22
+ end
23
+
24
+ end
25
+
26
+ end
@@ -0,0 +1,113 @@
1
+ require 'rails_helper'
2
+
3
+ module SectionTemplate
4
+ class TestSection < Ninetails::SectionTemplate
5
+ located_in :body
6
+ has_element :headline, Element::Text
7
+ has_element :button, Element::Button
8
+ has_many_elements :messages, Element::Text
9
+ end
10
+ end
11
+
12
+ RSpec.describe Ninetails::Section, type: :model do
13
+
14
+ describe "deserializing a json blob" do
15
+ let(:headline) { "Hello world!" }
16
+ let(:first_message) { "this is the first message" }
17
+ let(:second_message) { "and this is the second" }
18
+
19
+ let(:json) do
20
+ {
21
+ "name" => "",
22
+ "type" => "TestSection",
23
+ "tags" => { "position" => "body" },
24
+ "elements" => {
25
+ "headline" => {
26
+ "type" => "Text",
27
+ "content" => { "text" => headline }
28
+ },
29
+ "button" => {
30
+ "type" => "Button",
31
+ "link" => {
32
+ "url" => "/foo",
33
+ "title" => "Hello",
34
+ "text" => "world"
35
+ }
36
+ },
37
+ "messages" => [
38
+ {
39
+ "type" => "Text",
40
+ "content" => { "text" => first_message }
41
+ },
42
+ {
43
+ "type" => "Text",
44
+ "content" => { "text" => second_message }
45
+ }
46
+ ]
47
+ }
48
+ }
49
+ end
50
+
51
+ let(:section) { Ninetails::Section.new(json) }
52
+
53
+ it "should create a SectionTemplate instance" do
54
+ section.deserialize
55
+ expect(section.template).to be_a SectionTemplate::TestSection
56
+ end
57
+
58
+ it "should have an array of elements_instances" do
59
+ section.deserialize
60
+ expect(section.template.elements_instances.size).to eq 3
61
+ end
62
+
63
+ it "should include an element for 'text', 'button', and 'link'" do
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
68
+ end
69
+
70
+ it "should be the correct type for 'text', 'button', and 'link'" do
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
75
+ end
76
+
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"])
81
+ section.deserialize
82
+ end
83
+
84
+ it "should be possible to reserialize into json" do
85
+ section.deserialize
86
+ serialized = section.template.serialize.with_indifferent_access
87
+ expect(serialized[:elements][:headline][:content][:text]).to eq headline
88
+ expect(serialized[:elements][:messages][0][:content][:text]).to eq first_message
89
+ expect(serialized[:elements][:messages][1][:content][:text]).to eq second_message
90
+ end
91
+
92
+ end
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
+ end
@@ -0,0 +1,60 @@
1
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
2
+ ENV['RAILS_ENV'] ||= 'test'
3
+ # require File.expand_path('../../config/environment', __FILE__)
4
+
5
+ require File.expand_path("./../dummy/config/environment.rb", __FILE__)
6
+ ActiveRecord::Migrator.migrations_paths = [File.expand_path("./../dummy/db/migrate", __FILE__)]
7
+ ActiveRecord::Migrator.migrations_paths << [File.expand_path('./../../db/migrate', __FILE__)]
8
+
9
+ # Prevent database truncation if the environment is production
10
+ abort("The Rails environment is running in production mode!") if Rails.env.production?
11
+ require 'spec_helper'
12
+ require 'rspec/rails'
13
+ # Add additional requires below this line. Rails is not loaded until this point!
14
+
15
+ require 'factory_girl'
16
+ FactoryGirl.find_definitions
17
+
18
+ # Requires supporting ruby files with custom matchers and macros, etc, in
19
+ # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
20
+ # run as spec files by default. This means that files in spec/support that end
21
+ # in _spec.rb will both be required and run as specs, causing the specs to be
22
+ # run twice. It is recommended that you do not name files matching this glob to
23
+ # end with _spec.rb. You can configure this pattern with the --pattern
24
+ # option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
25
+ #
26
+ # The following line is provided for convenience purposes. It has the downside
27
+ # of increasing the boot-up time by auto-requiring all files in the support
28
+ # directory. Alternatively, in the individual `*_spec.rb` files, manually
29
+ # require only the support files necessary.
30
+ #
31
+ # Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
32
+
33
+ # Checks for pending migrations before tests are run.
34
+ # If you are not using ActiveRecord, you can remove this line.
35
+ ActiveRecord::Migration.maintain_test_schema!
36
+
37
+ RSpec.configure do |config|
38
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
39
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
40
+
41
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
42
+ # examples within a transaction, remove the following line or assign false
43
+ # instead of true.
44
+ config.use_transactional_fixtures = true
45
+
46
+ # RSpec Rails can automatically mix in different behaviours to your tests
47
+ # based on their file location, for example enabling you to call `get` and
48
+ # `post` in specs under `spec/controllers`.
49
+ #
50
+ # You can disable this behaviour by removing the line below, and instead
51
+ # explicitly tag your specs with their type, e.g.:
52
+ #
53
+ # RSpec.describe UsersController, :type => :controller do
54
+ # # ...
55
+ # end
56
+ #
57
+ # The different available types are documented in the features, such as in
58
+ # https://relishapp.com/rspec/rspec-rails/docs
59
+ config.infer_spec_type_from_file_location!
60
+ end
@@ -0,0 +1,117 @@
1
+ require 'rails_helper'
2
+
3
+ describe "Page Revisions API" do
4
+
5
+ let(:page) { create :page_with_revisions, revisions_count: 5 }
6
+
7
+ describe "listing revisions" do
8
+ describe "with a valid page_id" do
9
+
10
+ before do
11
+ get "/pages/#{page.id}/revisions"
12
+ end
13
+
14
+ it "should list the revisions which belong to that page" do
15
+ expect(response).to be_success
16
+ expect(json["revisions"].size).to eq 5
17
+ end
18
+
19
+ end
20
+ end
21
+
22
+ describe "creating a revision" do
23
+ let(:valid_revision_params) do
24
+ {
25
+ "page_revision": {
26
+ "message": "",
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
+ }
49
+ ]
50
+ }
51
+ }
52
+ end
53
+
54
+ let(:invalid_revision_params) do
55
+ {
56
+ "page_revision": {
57
+ "message": "",
58
+ "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
+ }
80
+ ]
81
+ }
82
+ }
83
+ end
84
+
85
+ describe "with valid sections" do
86
+ it "should create a revision" do
87
+ expect {
88
+ post "/pages/#{page.id}/revisions", valid_revision_params
89
+ }.to change { page.revisions.count }.by(1)
90
+
91
+ expect(response).to be_success
92
+ expect(json["page"]["revisionId"]).to_not be_nil
93
+ end
94
+
95
+ it "should have created the correct number of sections" do
96
+ post "/pages/#{page.id}/revisions", valid_revision_params
97
+ expect(page.revisions.last.sections.size).to eq 1
98
+ end
99
+ end
100
+
101
+ describe "with invalid sections" do
102
+ it "should not create a revision" do
103
+ expect {
104
+ post "/pages/#{page.id}/revisions", invalid_revision_params
105
+ }.not_to change { page.revisions.count }
106
+ end
107
+
108
+ it "should show error messages in the revision params" do
109
+ post "/pages/#{page.id}/revisions", invalid_revision_params
110
+
111
+ errors = json["page"]["sections"][0]["elements"]["title"]["content"]["errors"]
112
+ expect(errors).to eq({ "text"=>["can't be blank"] })
113
+ end
114
+ end
115
+ end
116
+
117
+ end