spontaneous 0.2.0.beta9 → 0.2.0.beta10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +61 -0
  3. data/LICENSE +18 -17
  4. data/Rakefile +1 -1
  5. data/application/css/core.css.scss +1 -1
  6. data/application/css/dialogue.css.scss +8 -20
  7. data/application/js/preview.js +28 -7
  8. data/application/js/publish.js +15 -4
  9. data/application/js/top_bar.js +0 -16
  10. data/application/js/views/piece_view.js +1 -1
  11. data/lib/spontaneous/asset/environment.rb +16 -1
  12. data/lib/spontaneous/box.rb +68 -0
  13. data/lib/spontaneous/capistrano/deploy.rb +7 -4
  14. data/lib/spontaneous/capistrano/sync.rb +2 -2
  15. data/lib/spontaneous/cli/init.rb +70 -19
  16. data/lib/spontaneous/cli/init/db.rb +34 -55
  17. data/lib/spontaneous/cli/init/mysql.rb +5 -5
  18. data/lib/spontaneous/cli/init/postgresql.rb +8 -9
  19. data/lib/spontaneous/cli/init/sqlite.rb +1 -2
  20. data/lib/spontaneous/cli/migrate.rb +0 -1
  21. data/lib/spontaneous/cli/site.rb +4 -0
  22. data/lib/spontaneous/collections/entry_set.rb +11 -0
  23. data/lib/spontaneous/data_mapper/content_model.rb +2 -0
  24. data/lib/spontaneous/data_mapper/content_model/serialization.rb +2 -2
  25. data/lib/spontaneous/extensions/array.rb +12 -2
  26. data/lib/spontaneous/field/base.rb +10 -0
  27. data/lib/spontaneous/field/file.rb +32 -2
  28. data/lib/spontaneous/field/image.rb +24 -2
  29. data/lib/spontaneous/field/select.rb +8 -0
  30. data/lib/spontaneous/field/webvideo.rb +8 -0
  31. data/lib/spontaneous/generators/site/config/initializers/fields.rb +55 -0
  32. data/lib/spontaneous/json.rb +3 -2
  33. data/lib/spontaneous/logger.rb +2 -2
  34. data/lib/spontaneous/media/file.rb +3 -3
  35. data/lib/spontaneous/media/image/attributes.rb +72 -6
  36. data/lib/spontaneous/media/image/renderable.rb +53 -20
  37. data/lib/spontaneous/media/store.rb +3 -3
  38. data/lib/spontaneous/media/store/backend.rb +16 -0
  39. data/lib/spontaneous/media/store/cloud.rb +52 -12
  40. data/lib/spontaneous/media/store/local.rb +6 -3
  41. data/lib/spontaneous/model.rb +3 -0
  42. data/lib/spontaneous/model/core/entries.rb +34 -13
  43. data/lib/spontaneous/model/core/entry.rb +3 -1
  44. data/lib/spontaneous/model/page/controllers.rb +1 -2
  45. data/lib/spontaneous/model/page/paths.rb +18 -7
  46. data/lib/spontaneous/output/context.rb +0 -8
  47. data/lib/spontaneous/output/template/renderer.rb +2 -0
  48. data/lib/spontaneous/plugins/application/state.rb +0 -4
  49. data/lib/spontaneous/prototypes/field_prototype.rb +4 -0
  50. data/lib/spontaneous/publishing/immediate.rb +0 -5
  51. data/lib/spontaneous/publishing/progress.rb +2 -2
  52. data/lib/spontaneous/publishing/rerender.rb +1 -4
  53. data/lib/spontaneous/publishing/simultaneous.rb +19 -17
  54. data/lib/spontaneous/publishing/steps.rb +12 -3
  55. data/lib/spontaneous/rack.rb +2 -0
  56. data/lib/spontaneous/rack/asset_server.rb +5 -2
  57. data/lib/spontaneous/rack/back.rb +9 -1
  58. data/lib/spontaneous/rack/back/base.rb +1 -0
  59. data/lib/spontaneous/rack/back/changes.rb +5 -0
  60. data/lib/spontaneous/rack/back/preview.rb +4 -4
  61. data/lib/spontaneous/rack/back/private.rb +11 -0
  62. data/lib/spontaneous/rack/middleware/scope.rb +16 -4
  63. data/lib/spontaneous/rack/page_controller.rb +2 -2
  64. data/lib/spontaneous/rack/public.rb +52 -4
  65. data/lib/spontaneous/sequel.rb +10 -13
  66. data/lib/spontaneous/site.rb +28 -8
  67. data/lib/spontaneous/site/publishing.rb +1 -1
  68. data/lib/spontaneous/site/storage.rb +7 -4
  69. data/lib/spontaneous/tasks/environment.rake +3 -0
  70. data/lib/spontaneous/utils/database/postgres_dumper.rb +23 -2
  71. data/lib/spontaneous/version.rb +1 -1
  72. data/spontaneous.gemspec +7 -12
  73. data/test/fixtures/assets/public1/css/data.css.scss +1 -1
  74. data/test/functional/test_application.rb +15 -0
  75. data/test/functional/test_cli.rb +109 -3
  76. data/test/functional/test_front.rb +108 -10
  77. data/test/test_helper.rb +3 -3
  78. data/test/unit/fields/test_boolean_fields.rb +80 -0
  79. data/test/unit/fields/test_date_fields.rb +47 -0
  80. data/test/unit/fields/test_file_field.rb +210 -0
  81. data/test/unit/{test_images.rb → fields/test_image_fields.rb} +133 -15
  82. data/test/unit/fields/test_location_fields.rb +41 -0
  83. data/test/unit/fields/test_option_fields.rb +61 -0
  84. data/test/unit/fields/test_tag_list_fields.rb +45 -0
  85. data/test/unit/fields/test_text_fields.rb +124 -0
  86. data/test/unit/fields/test_web_video_fields.rb +198 -0
  87. data/test/unit/test_assets.rb +22 -22
  88. data/test/unit/test_boxes.rb +34 -13
  89. data/test/unit/test_changesets.rb +1 -0
  90. data/test/unit/test_extensions.rb +17 -0
  91. data/test/unit/test_fields.rb +20 -643
  92. data/test/unit/test_media.rb +9 -9
  93. data/test/unit/test_page.rb +47 -0
  94. data/test/unit/test_publishing_pipeline.rb +2 -2
  95. data/test/unit/test_serialisation.rb +37 -0
  96. data/test/unit/test_storage.rb +42 -3
  97. metadata +37 -17
@@ -1,6 +1,6 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  module Spontaneous
4
- VERSION = "0.2.0.beta9"
4
+ VERSION = "0.2.0.beta10"
5
5
  GEM = false
6
6
  end
@@ -3,13 +3,14 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
  require 'spontaneous/version'
4
4
 
5
5
  Gem::Specification.new do |s|
6
- s.required_ruby_version = '>= 2.0.0'
7
-
8
6
  s.name = 'spontaneous'
9
7
  s.version = Spontaneous::VERSION
8
+ s.license = "MIT"
9
+
10
+ s.required_ruby_version = '>= 2.0.0'
10
11
 
11
- s.summary = "Spontaneous is a next-generation Ruby CMS"
12
- s.description = "Spontaneous is a next-generation Ruby CMS"
12
+ s.summary = "Next-generation Ruby CMS and web framework."
13
+ s.description = "Spontaneous is a content management framework that allows the easy development of sophisticated & beautiful websites with powerful developer tools & an elegant editing interface."
13
14
 
14
15
  s.authors = ['Garry Hill']
15
16
  s.email = 'garry@spontaneous.io'
@@ -20,12 +21,6 @@ Gem::Specification.new do |s|
20
21
  s.test_files = s.files.grep(%r{^test/})
21
22
  s.require_paths = %w[lib]
22
23
 
23
- s.rdoc_options = ['--charset=UTF-8']
24
- s.extra_rdoc_files = %w[LICENSE]
25
-
26
- # s.signing_key = '/Volumes/Keys/rubygems-garry-magnetised-net-private_key.pem'
27
- # s.cert_chain = ['gem-public_cert.pem']
28
-
29
24
  s.add_dependency('activesupport', ['~> 4.0'])
30
25
  s.add_dependency('coffee-script', ['~> 2.2'])
31
26
  s.add_dependency('bcrypt', ['~> 3.1'])
@@ -46,14 +41,14 @@ Gem::Specification.new do |s|
46
41
  s.add_dependency('sequel', ['~> 4.8'])
47
42
  s.add_dependency('simultaneous', ['~> 0.4.2'])
48
43
  s.add_dependency('sinatra', ['~> 1.3'])
49
- s.add_dependency('skeptick', ['~> 0.1'])
44
+ s.add_dependency('skeptick', ['~> 0.1.1'])
50
45
  s.add_dependency('sprockets', ['~> 2.9'])
51
46
  s.add_dependency('stringex', ['= 1.3'])
52
47
  s.add_dependency('thin', ['~> 1.2'])
53
48
  s.add_dependency('thor', ['~> 0.16'])
54
49
  s.add_dependency('uglifier', ['~> 1.3'])
55
50
  s.add_dependency('xapian-fu', ['~> 1.5'])
56
- s.add_dependency('yajl-ruby', ['~> 1.1'])
51
+ s.add_dependency('oj', ['~> 2.11'])
57
52
 
58
53
  s.add_development_dependency('minitest', ['~> 4.7.0'])
59
54
  s.add_development_dependency('minitest-colorize', ['~> 0.0.5'])
@@ -1,3 +1,3 @@
1
1
  div {
2
- background-image: url(asset-data-uri("i/y.png"));
2
+ background-image: asset-data-uri("i/y.png");
3
3
  }
@@ -196,4 +196,19 @@ describe "Application" do
196
196
  Spontaneous::Rack.port.must_equal 3002
197
197
  end
198
198
  end
199
+
200
+ describe 'ENV["DATABASE_URL"]' do
201
+ before do
202
+ ENV['DATABASE_URL'] = 'sqlite:///production.db'
203
+ @site = Spontaneous.init(:root => site_root, :mode => :front, :environment => :production)
204
+ end
205
+
206
+ after do
207
+ ENV.delete('DATABASE_URL')
208
+ end
209
+
210
+ it 'should override settings in database.yml' do
211
+ @site.config.db.must_equal 'sqlite:///production.db'
212
+ end
213
+ end
199
214
  end
@@ -16,18 +16,124 @@ describe "CLI" do
16
16
  end
17
17
  end
18
18
 
19
- def set_expectation(method, cmd = cmd)
19
+ def set_expectation(method, _cmd = cmd)
20
20
  quietly do
21
- cmd.any_instance.expects(method)
21
+ _cmd.any_instance.expects(method)
22
22
  end
23
23
  end
24
24
 
25
25
  describe "Init" do
26
26
  let(:cmd) { cli::Init }
27
+
28
+ after do
29
+ teardown_site
30
+ end
31
+
27
32
  it "maps 'spot init' to Init#init" do
28
- set_expectation(:initialize_size)
33
+ set_expectation(:initialize_site)
29
34
  run_command(["init"])
30
35
  end
36
+
37
+ describe 'DatabaseInitializer' do
38
+ let(:site) { setup_site }
39
+ let(:create_user) { false }
40
+ let(:env) { :development }
41
+ let(:cli) { mock }
42
+
43
+ def yaml_config(adapter)
44
+ { production: {adapter: adapter, database: 'spontaneous_db_production'}, development: {adapter: adapter, database: 'spontaneous_db'}, test: {adapter: adapter, database: 'spontaneous_db_test'} }
45
+ end
46
+
47
+ def with_yaml_config(adapter)
48
+ begin
49
+ site.stubs(:db_config_file).returns(yaml_config(adapter))
50
+ yield
51
+ ensure
52
+ site.unstub(:db_config_file)
53
+ end
54
+ end
55
+
56
+ def with_db_url(url)
57
+ begin
58
+ ENV['DATABASE_URL'] = url
59
+ yield
60
+ ensure
61
+ ENV.delete 'DATABASE_URL'
62
+ end
63
+ end
64
+
65
+ def init
66
+ Spontaneous::Cli::Init::DatabaseInitializer.new(cli, site)
67
+ end
68
+
69
+ it "runs a database initializer for development & test environments in development mode" do
70
+ init.database_environments(:development).must_equal [:development, :test]
71
+ end
72
+
73
+ it "runs a database initializer for only production in production mode" do
74
+ init.database_environments(:production).must_equal [:production]
75
+ end
76
+
77
+ describe 'DATABASE_URL' do
78
+ it "gets the db config from the ENV" do
79
+ with_db_url('mysql://localhost/something') do
80
+ init.database_config(:development).must_equal('mysql://localhost/something')
81
+ init.database_config(:test).must_equal('mysql://localhost/something')
82
+ end
83
+ end
84
+
85
+ it 'uses the right db initialization class for the adapter' do
86
+ with_db_url('mysql2://localhost/something') do
87
+ init.database_initializer(:development).must_be_instance_of Spontaneous::Cli::Init::MySQL
88
+ end
89
+ with_db_url('postgres://localhost/something') do
90
+ init.database_initializer(:development).must_be_instance_of Spontaneous::Cli::Init::Postgresql
91
+ end
92
+ with_db_url('sqlite://localhost/something') do
93
+ init.database_initializer(:development).must_be_instance_of Spontaneous::Cli::Init::Sqlite
94
+ end
95
+ end
96
+
97
+ it 'instantiates & calls #run on a db initializer for each env' do
98
+ with_db_url('sqlite://localhost/something') do
99
+ db_initializer = mock
100
+ db_initializer.expects(:run).once
101
+ Spontaneous::Cli::Init::Sqlite.expects(:new).with(cli, instance_of(Sequel::SQLite::Database)).returns(db_initializer).once
102
+ init.run(:development)
103
+ end
104
+ end
105
+ end
106
+
107
+ describe 'YAML' do
108
+ it "gets db settings from a config file if no ENV setting" do
109
+ with_yaml_config('postgres') do
110
+ init.database_config(:development).must_equal({adapter: 'postgres', database: 'spontaneous_db'})
111
+ init.database_config(:test).must_equal({adapter: 'postgres', database: 'spontaneous_db_test'})
112
+ end
113
+ end
114
+
115
+ it 'uses the right db initialization class for the adapter' do
116
+ with_yaml_config('mysql2') do
117
+ init.database_initializer(:development).must_be_instance_of Spontaneous::Cli::Init::MySQL
118
+ end
119
+ with_yaml_config('postgres') do
120
+ init.database_initializer(:development).must_be_instance_of Spontaneous::Cli::Init::Postgresql
121
+ end
122
+ with_yaml_config('sqlite') do
123
+ init.database_initializer(:development).must_be_instance_of Spontaneous::Cli::Init::Sqlite
124
+ end
125
+ end
126
+
127
+ it 'instantiates & calls #run on a db initializer for each env' do
128
+ with_yaml_config('mysql2') do
129
+ db_initializer = mock
130
+ db_initializer.expects(:run).once
131
+ Spontaneous::Cli::Init::MySQL.expects(:new).with(cli, instance_of(Sequel::Mysql2::Database)).returns(db_initializer).once
132
+ init.run(:production)
133
+ end
134
+ end
135
+ end
136
+ end
31
137
  end
32
138
 
33
139
  describe "Console" do
@@ -15,6 +15,17 @@ describe "Front" do
15
15
  Spontaneous::Output.write_compiled_scripts = true
16
16
 
17
17
 
18
+ class ::PageController < S::Rack::PageController
19
+ # Define a per-site base controller for all controller classes here so
20
+ # that we can test it's use later on.
21
+ # If I define it only in the test where it's used then it's too late as
22
+ # the controller hierarchy will have already been built upon some other
23
+ # base class.
24
+ #
25
+ # (Another argument for replacing these [start..finish] blocks with
26
+ # [before..after] ones)
27
+ end
28
+
18
29
  site = setup_site(site_root)
19
30
  let(:site) { site }
20
31
  S::State.delete
@@ -75,6 +86,11 @@ describe "Front" do
75
86
  layout { "${ path }.${ __format }" }
76
87
  end
77
88
 
89
+ class ::TakeItPage < ::Page
90
+ layout(:html) { "take it ${id} {{ splat }}" }
91
+ box :pages
92
+ end
93
+
78
94
  root = ::SitePage.create
79
95
  about = ::SitePage.create(:slug => "about", :uid => "about")
80
96
  feed = ::FeedPage.create(:slug => "feed", :uid => "feed")
@@ -83,6 +99,8 @@ describe "Front" do
83
99
  dynamic_request_params = ::DynamicRequestParams.create(slug: "dynamic-request-params", uid: "dynamic_request_params")
84
100
  dynamic_render_params = ::DynamicRenderParams.create(slug: "dynamic-render-params", uid: "dynamic_render_params")
85
101
  commentable = ::CommentablePage.create(slug:"commentable", uid: "commentable")
102
+ take_it = TakeItPage.create(slug: 'takeit', uid: 'takeit')
103
+ take_it_again = TakeItPage.create(slug: 'again', uid: 'again')
86
104
  root.pages << about
87
105
  root.pages << feed
88
106
  root.pages << news
@@ -90,12 +108,17 @@ describe "Front" do
90
108
  root.pages << dynamic_render_params
91
109
  root.pages << static
92
110
  root.pages << commentable
111
+ root.pages << take_it
112
+ take_it.pages << take_it_again
93
113
  root.save
114
+ take_it.save
94
115
 
95
116
  let(:root_id) { root.id }
96
117
  let(:about_id) { about.id }
97
118
  let(:feed_id) { feed.id }
98
119
  let(:news_id) { news.id }
120
+ let(:take_it_id) { take_it.id }
121
+ let(:take_it_again_id) { take_it_again.id }
99
122
  let(:dynamic_request_params_id) { dynamic_request_params.id }
100
123
  let(:dynamic_render_params_id) { dynamic_render_params.id }
101
124
  let(:static_id) { static.id }
@@ -111,7 +134,7 @@ describe "Front" do
111
134
  end
112
135
 
113
136
  finish do
114
- [:SitePage, :StaticPage, :DynamicRequestParams, :DynamicRenderParams, :CommentablePage, :FeedPage].each do |const|
137
+ [:SitePage, :StaticPage, :DynamicRequestParams, :DynamicRenderParams, :CommentablePage, :FeedPage, :TakeItPage, :PageController].each do |const|
115
138
  Object.send(:remove_const, const) rescue nil
116
139
  end
117
140
  if defined?(Content)
@@ -128,6 +151,8 @@ describe "Front" do
128
151
  let(:about) { Content[about_id] }
129
152
  let(:feed) { Content[feed_id] }
130
153
  let(:news) { Content[news_id] }
154
+ let(:take_it) { Content[take_it_id] }
155
+ let(:take_it_again) { Content[take_it_again_id] }
131
156
  let(:dynamic_request_params) { Content[dynamic_request_params_id] }
132
157
  let(:dynamic_render_params) { Content[dynamic_render_params_id] }
133
158
  let(:static) { Content[static_id] }
@@ -596,7 +621,7 @@ describe "Front" do
596
621
 
597
622
  after do
598
623
  Object.send(:remove_const, :SubPage) rescue nil
599
- CommentablePage.instance_variable_set(:@request_blocks, {})
624
+ CommentablePage.instance_variable_set(:@controllers, nil)
600
625
  CommentablePage.send(:remove_const, :StatusController) rescue nil
601
626
  CommentablePage.send(:remove_const, :TestController) rescue nil
602
627
  CommentablePage.send(:remove_const, :Test2Controller) rescue nil
@@ -711,10 +736,8 @@ describe "Front" do
711
736
 
712
737
  describe "overriding base controller class" do
713
738
  before do
714
- class ::PageController < S::Rack::PageController
715
- get '/nothing' do
716
- 'Something'
717
- end
739
+ ::PageController.get '/nothing' do
740
+ 'Something'
718
741
  end
719
742
 
720
743
  CommentablePage.controller :drummer do
@@ -724,10 +747,6 @@ describe "Front" do
724
747
  end
725
748
  end
726
749
 
727
- after do
728
- Object.send(:remove_const, :PageController)
729
- end
730
-
731
750
  it "affect all controller actions" do
732
751
  get "/commentable/@drummer/nothing"
733
752
  assert last_response.ok?, "Expected 200 got #{last_response.status}"
@@ -736,6 +755,85 @@ describe "Front" do
736
755
  end
737
756
  end
738
757
 
758
+ describe 'wildcard paths' do
759
+ let(:page) { take_it }
760
+ let(:again) { take_it_again }
761
+
762
+ after do
763
+ root.class.instance_variable_set(:@controllers, nil)
764
+ TakeItPage.instance_variable_set(:@controllers, nil)
765
+ end
766
+
767
+ it 'renders a url that resolves to a page accepting the path' do
768
+ TakeItPage.controller do
769
+ get '*' do
770
+ render splat: params[:splat].first
771
+ end
772
+ end
773
+ [
774
+ ["/something", page],
775
+ ["/something/else", page],
776
+ ["/really/something/else/entirely", page],
777
+ ["/something/else/entirely", again]
778
+ ].each do |path, expected|
779
+ get "#{expected.path}#{path}"
780
+ assert last_response.ok?, "Expected 200 got #{last_response.status}"
781
+ last_response.body.must_equal "take it #{expected.id} #{path}"
782
+ end
783
+ end
784
+
785
+ it 'returns 404 if the requested path doesn’t match the controller’s route' do
786
+ TakeItPage.controller do
787
+ get '/womble/?:where?' do
788
+ render splat: params[:where]
789
+ end
790
+ end
791
+ get "#{page.path}/womble/around"
792
+ assert last_response.ok?, "Expected 200 got #{last_response.status}"
793
+ last_response.body.must_equal "take it #{page.id} around"
794
+
795
+ get "#{page.path}/womble"
796
+ assert last_response.ok?, "Expected 200 got #{last_response.status}"
797
+ last_response.body.must_equal "take it #{page.id} "
798
+
799
+ get "#{page.path}/wimble/around"
800
+ assert last_response.status == 404
801
+ end
802
+
803
+ it 'returns 404 if the controller is only configured to match the root' do
804
+ TakeItPage.controller do
805
+ get do
806
+ render splat: 'root'
807
+ end
808
+ end
809
+ get page.path
810
+ assert last_response.ok?, "Expected 200 got #{last_response.status}"
811
+ last_response.body.must_equal "take it #{page.id} root"
812
+
813
+ get "#{page.path}/womble"
814
+ assert last_response.status == 404, "Expected 404 but got #{last_response.status}"
815
+ end
816
+
817
+ it 'can fall back to controllers defined on the site homepage' do
818
+ root.class.controller do
819
+ get '/' do
820
+ "ow"
821
+ end
822
+ get '/slimy/:who' do
823
+ "<#{params[:who]}>"
824
+ end
825
+ end
826
+
827
+ get '/slimy/monster'
828
+ assert last_response.ok?, "Expected 200 got #{last_response.status}"
829
+ last_response.body.must_equal "<monster>"
830
+
831
+ get '/'
832
+ assert last_response.ok?, "Expected 200 got #{last_response.status}"
833
+ last_response.body.must_equal "ow"
834
+ end
835
+ end
836
+
739
837
  describe "Static files" do
740
838
  before do
741
839
  @revision_dir = Spontaneous.instance.revision_dir(1)
@@ -35,6 +35,9 @@ else
35
35
  false
36
36
  end
37
37
 
38
+ require 'mysql2'
39
+ require 'pg'
40
+ require 'sqlite3'
38
41
 
39
42
  connection_string = \
40
43
  case ENV["SPOT_ADAPTER"]
@@ -44,7 +47,6 @@ when "postgres"
44
47
  Jdbc::Postgres.load_driver
45
48
  "jdbc:postgresql:///spontaneous2_test"
46
49
  else
47
- require 'pg'
48
50
  "postgres:///spontaneous2_test"
49
51
  end
50
52
  when "mysql"
@@ -53,7 +55,6 @@ when "mysql"
53
55
  Jdbc::MySQL.load_driver
54
56
  "jdbc:mysql://localhost/spontaneous2_test?user=root"
55
57
  else
56
- require 'mysql2'
57
58
  "mysql2://root@localhost/spontaneous2_test"
58
59
  end
59
60
  when "sqlite"
@@ -62,7 +63,6 @@ when "sqlite"
62
63
  Jdbc::SQLite3.load_driver
63
64
  "jdbc:sqlite::memory:"
64
65
  else
65
- require 'sqlite3'
66
66
  "sqlite:/" # in-memory
67
67
  end
68
68
  end
@@ -0,0 +1,80 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path('../../../test_helper', __FILE__)
4
+
5
+ describe "Boolean fields" do
6
+
7
+ before do
8
+ @site = setup_site
9
+ @now = Time.now
10
+ stub_time(@now)
11
+ Spontaneous::State.delete
12
+ @site.background_mode = :immediate
13
+ @content_class = Class.new(::Piece)
14
+ @prototype = @content_class.field :switch
15
+ @content_class.stubs(:name).returns("ContentClass")
16
+ @instance = @content_class.create
17
+ @field = @instance.switch
18
+ end
19
+
20
+ it "has a distinct editor class" do
21
+ @prototype.instance_class.editor_class.must_equal "Spontaneous.Field.Boolean"
22
+ end
23
+
24
+ it "adopts any field called 'switch'" do
25
+ assert @field.is_a?(Spontaneous::Field::Boolean), "Field should be an instance of Boolean but instead has the following ancestors #{ @prototype.instance_class.ancestors }"
26
+ end
27
+
28
+ it "defaults to true" do
29
+ @field.value.must_equal true
30
+ @field.value(:html).must_equal "Yes"
31
+ @field.value(:string).must_equal "Yes"
32
+ end
33
+
34
+ it "changes string value to 'No'" do
35
+ @field.value = false
36
+ @field.value(:string).must_equal "No"
37
+ end
38
+
39
+ it "flags itself as 'empty' if false" do # I think...
40
+ @field.empty?.must_equal false
41
+ @field.value = false
42
+ @field.empty?.must_equal true
43
+ end
44
+
45
+ it "uses the given state labels" do
46
+ prototype = @content_class.field :boolean, true: "Enabled", false: "Disabled"
47
+ field = prototype.to_field(@instance)
48
+ field.value.must_equal true
49
+ field.value(:string).must_equal "Enabled"
50
+ field.value = false
51
+ field.value(:string).must_equal "Disabled"
52
+ field.value(:html).must_equal "Disabled"
53
+ end
54
+
55
+ it "uses the given default" do
56
+ prototype = @content_class.field :boolean, default: false, true: "On", false: "Off"
57
+ field = prototype.to_field(@instance)
58
+ field.value.must_equal false
59
+ field.value(:string).must_equal "Off"
60
+ end
61
+
62
+ it "returns the string value from #to_s" do
63
+ prototype = @content_class.field :boolean, default: false, true: "On", false: "Off"
64
+ field = prototype.to_field(@instance)
65
+ field.to_s.must_equal "Off"
66
+ end
67
+
68
+ it "has shortcut accessors" do
69
+ state = @field.value(:boolean)
70
+ @field.on?.must_equal state
71
+ @field.checked?.must_equal state
72
+ @field.enabled?.must_equal state
73
+ end
74
+
75
+ it "exports the labels to the interface" do
76
+ prototype = @content_class.field :boolean, default: false, true: "Yes Please", false: "No Thanks"
77
+ exported = prototype.instance_class.export(nil)
78
+ exported.must_equal({:labels=>{:true=>"Yes Please", :false=>"No Thanks"}})
79
+ end
80
+ end