formtastic 1.2.4 → 3.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (189) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +46 -0
  5. data/.yardopts +1 -0
  6. data/Appraisals +43 -0
  7. data/CHANGELOG +54 -0
  8. data/DEPRECATIONS +52 -0
  9. data/Gemfile +3 -0
  10. data/README.md +629 -0
  11. data/RELEASE_PROCESS +6 -0
  12. data/Rakefile +35 -0
  13. data/app/assets/stylesheets/formtastic.css +289 -0
  14. data/app/assets/stylesheets/formtastic_ie6.css +33 -0
  15. data/app/assets/stylesheets/formtastic_ie7.css +23 -0
  16. data/formtastic.gemspec +42 -0
  17. data/gemfiles/rails_3.2.gemfile +9 -0
  18. data/gemfiles/rails_4.0.4.gemfile +8 -0
  19. data/gemfiles/rails_4.1.gemfile +8 -0
  20. data/gemfiles/rails_4.2.gemfile +8 -0
  21. data/gemfiles/rails_4.gemfile +8 -0
  22. data/gemfiles/rails_5.0.gemfile +8 -0
  23. data/gemfiles/rails_edge.gemfile +15 -0
  24. data/lib/formtastic.rb +40 -1945
  25. data/lib/formtastic/action_class_finder.rb +18 -0
  26. data/lib/formtastic/actions.rb +11 -0
  27. data/lib/formtastic/actions/base.rb +156 -0
  28. data/lib/formtastic/actions/button_action.rb +67 -0
  29. data/lib/formtastic/actions/buttonish.rb +17 -0
  30. data/lib/formtastic/actions/input_action.rb +70 -0
  31. data/lib/formtastic/actions/link_action.rb +88 -0
  32. data/lib/formtastic/deprecation.rb +42 -0
  33. data/lib/formtastic/engine.rb +11 -0
  34. data/lib/formtastic/form_builder.rb +124 -0
  35. data/lib/formtastic/helpers.rb +16 -0
  36. data/lib/formtastic/helpers/action_helper.rb +162 -0
  37. data/lib/formtastic/helpers/actions_helper.rb +168 -0
  38. data/lib/formtastic/helpers/enum.rb +13 -0
  39. data/lib/formtastic/helpers/errors_helper.rb +81 -0
  40. data/lib/formtastic/helpers/fieldset_wrapper.rb +80 -0
  41. data/lib/formtastic/helpers/file_column_detection.rb +16 -0
  42. data/lib/formtastic/helpers/form_helper.rb +203 -0
  43. data/lib/formtastic/helpers/input_helper.rb +407 -0
  44. data/lib/formtastic/helpers/inputs_helper.rb +411 -0
  45. data/lib/formtastic/helpers/reflection.rb +37 -0
  46. data/lib/formtastic/html_attributes.rb +32 -0
  47. data/lib/formtastic/i18n.rb +4 -2
  48. data/lib/formtastic/input_class_finder.rb +18 -0
  49. data/lib/formtastic/inputs.rb +39 -0
  50. data/lib/formtastic/inputs/base.rb +76 -0
  51. data/lib/formtastic/inputs/base/associations.rb +31 -0
  52. data/lib/formtastic/inputs/base/choices.rb +108 -0
  53. data/lib/formtastic/inputs/base/collections.rb +159 -0
  54. data/lib/formtastic/inputs/base/database.rb +22 -0
  55. data/lib/formtastic/inputs/base/datetime_pickerish.rb +85 -0
  56. data/lib/formtastic/inputs/base/errors.rb +58 -0
  57. data/lib/formtastic/inputs/base/fileish.rb +23 -0
  58. data/lib/formtastic/inputs/base/hints.rb +31 -0
  59. data/lib/formtastic/inputs/base/html.rb +53 -0
  60. data/lib/formtastic/inputs/base/labelling.rb +52 -0
  61. data/lib/formtastic/inputs/base/naming.rb +42 -0
  62. data/lib/formtastic/inputs/base/numeric.rb +50 -0
  63. data/lib/formtastic/inputs/base/options.rb +17 -0
  64. data/lib/formtastic/inputs/base/placeholder.rb +17 -0
  65. data/lib/formtastic/inputs/base/stringish.rb +38 -0
  66. data/lib/formtastic/inputs/base/timeish.rb +241 -0
  67. data/lib/formtastic/inputs/base/validations.rb +215 -0
  68. data/lib/formtastic/inputs/base/wrapping.rb +50 -0
  69. data/lib/formtastic/inputs/boolean_input.rb +118 -0
  70. data/lib/formtastic/inputs/check_boxes_input.rb +197 -0
  71. data/lib/formtastic/inputs/color_input.rb +42 -0
  72. data/lib/formtastic/inputs/country_input.rb +86 -0
  73. data/lib/formtastic/inputs/datalist_input.rb +41 -0
  74. data/lib/formtastic/inputs/date_picker_input.rb +93 -0
  75. data/lib/formtastic/inputs/date_select_input.rb +34 -0
  76. data/lib/formtastic/inputs/datetime_picker_input.rb +103 -0
  77. data/lib/formtastic/inputs/datetime_select_input.rb +12 -0
  78. data/lib/formtastic/inputs/email_input.rb +41 -0
  79. data/lib/formtastic/inputs/file_input.rb +42 -0
  80. data/lib/formtastic/inputs/hidden_input.rb +62 -0
  81. data/lib/formtastic/inputs/number_input.rb +88 -0
  82. data/lib/formtastic/inputs/password_input.rb +41 -0
  83. data/lib/formtastic/inputs/phone_input.rb +42 -0
  84. data/lib/formtastic/inputs/radio_input.rb +163 -0
  85. data/lib/formtastic/inputs/range_input.rb +95 -0
  86. data/lib/formtastic/inputs/search_input.rb +41 -0
  87. data/lib/formtastic/inputs/select_input.rb +235 -0
  88. data/lib/formtastic/inputs/string_input.rb +36 -0
  89. data/lib/formtastic/inputs/text_input.rb +48 -0
  90. data/lib/formtastic/inputs/time_picker_input.rb +99 -0
  91. data/lib/formtastic/inputs/time_select_input.rb +38 -0
  92. data/lib/formtastic/inputs/time_zone_input.rb +58 -0
  93. data/lib/formtastic/inputs/url_input.rb +41 -0
  94. data/lib/formtastic/localized_string.rb +17 -0
  95. data/lib/formtastic/localizer.rb +152 -0
  96. data/lib/formtastic/namespaced_class_finder.rb +99 -0
  97. data/lib/formtastic/util.rb +35 -16
  98. data/lib/formtastic/version.rb +3 -0
  99. data/lib/generators/formtastic/form/form_generator.rb +64 -37
  100. data/lib/generators/formtastic/input/input_generator.rb +46 -0
  101. data/lib/generators/formtastic/install/install_generator.rb +13 -5
  102. data/lib/generators/templates/_form.html.erb +10 -4
  103. data/lib/generators/templates/_form.html.haml +8 -4
  104. data/lib/generators/templates/_form.html.slim +8 -0
  105. data/lib/generators/templates/formtastic.rb +77 -44
  106. data/lib/generators/templates/input.rb +19 -0
  107. data/lib/locale/en.yml +3 -0
  108. data/sample/basic_inputs.html +224 -0
  109. data/sample/config.ru +69 -0
  110. data/sample/index.html +14 -0
  111. data/spec/action_class_finder_spec.rb +12 -0
  112. data/spec/actions/button_action_spec.rb +63 -0
  113. data/spec/actions/generic_action_spec.rb +521 -0
  114. data/spec/actions/input_action_spec.rb +59 -0
  115. data/spec/actions/link_action_spec.rb +92 -0
  116. data/spec/builder/custom_builder_spec.rb +116 -0
  117. data/spec/builder/error_proc_spec.rb +27 -0
  118. data/spec/builder/semantic_fields_for_spec.rb +142 -0
  119. data/spec/fast_spec_helper.rb +12 -0
  120. data/spec/generators/formtastic/form/form_generator_spec.rb +131 -0
  121. data/spec/generators/formtastic/input/input_generator_spec.rb +124 -0
  122. data/spec/generators/formtastic/install/install_generator_spec.rb +47 -0
  123. data/spec/helpers/action_helper_spec.rb +19 -0
  124. data/spec/helpers/actions_helper_spec.rb +143 -0
  125. data/spec/helpers/form_helper_spec.rb +218 -0
  126. data/spec/helpers/input_helper_spec.rb +6 -0
  127. data/spec/helpers/inputs_helper_spec.rb +655 -0
  128. data/spec/helpers/namespaced_action_helper_spec.rb +43 -0
  129. data/spec/helpers/namespaced_input_helper_spec.rb +36 -0
  130. data/spec/helpers/reflection_helper_spec.rb +32 -0
  131. data/spec/helpers/semantic_errors_helper_spec.rb +112 -0
  132. data/spec/i18n_spec.rb +210 -0
  133. data/spec/input_class_finder_spec.rb +10 -0
  134. data/spec/inputs/base/collections_spec.rb +76 -0
  135. data/spec/inputs/base/validations_spec.rb +342 -0
  136. data/spec/inputs/boolean_input_spec.rb +254 -0
  137. data/spec/inputs/check_boxes_input_spec.rb +546 -0
  138. data/spec/inputs/color_input_spec.rb +97 -0
  139. data/spec/inputs/country_input_spec.rb +133 -0
  140. data/spec/inputs/custom_input_spec.rb +55 -0
  141. data/spec/inputs/datalist_input_spec.rb +61 -0
  142. data/spec/inputs/date_picker_input_spec.rb +449 -0
  143. data/spec/inputs/date_select_input_spec.rb +235 -0
  144. data/spec/inputs/datetime_picker_input_spec.rb +490 -0
  145. data/spec/inputs/datetime_select_input_spec.rb +193 -0
  146. data/spec/inputs/email_input_spec.rb +85 -0
  147. data/spec/inputs/file_input_spec.rb +89 -0
  148. data/spec/inputs/hidden_input_spec.rb +135 -0
  149. data/spec/inputs/include_blank_spec.rb +78 -0
  150. data/spec/inputs/label_spec.rb +149 -0
  151. data/spec/inputs/number_input_spec.rb +815 -0
  152. data/spec/inputs/password_input_spec.rb +99 -0
  153. data/spec/inputs/phone_input_spec.rb +85 -0
  154. data/spec/inputs/placeholder_spec.rb +71 -0
  155. data/spec/inputs/radio_input_spec.rb +328 -0
  156. data/spec/inputs/range_input_spec.rb +505 -0
  157. data/spec/inputs/readonly_spec.rb +50 -0
  158. data/spec/inputs/search_input_spec.rb +84 -0
  159. data/spec/inputs/select_input_spec.rb +615 -0
  160. data/spec/inputs/string_input_spec.rb +260 -0
  161. data/spec/inputs/text_input_spec.rb +187 -0
  162. data/spec/inputs/time_picker_input_spec.rb +455 -0
  163. data/spec/inputs/time_select_input_spec.rb +248 -0
  164. data/spec/inputs/time_zone_input_spec.rb +143 -0
  165. data/spec/inputs/url_input_spec.rb +85 -0
  166. data/spec/inputs/with_options_spec.rb +43 -0
  167. data/spec/localizer_spec.rb +130 -0
  168. data/spec/namespaced_class_finder_spec.rb +79 -0
  169. data/spec/spec.opts +2 -0
  170. data/spec/spec_helper.rb +525 -0
  171. data/spec/support/custom_macros.rb +564 -0
  172. data/spec/support/deprecation.rb +6 -0
  173. data/spec/support/shared_examples.rb +1313 -0
  174. data/spec/support/specialized_class_finder_shared_example.rb +27 -0
  175. data/spec/support/test_environment.rb +31 -0
  176. data/spec/util_spec.rb +66 -0
  177. metadata +434 -161
  178. data/README.textile +0 -682
  179. data/generators/form/USAGE +0 -16
  180. data/generators/form/form_generator.rb +0 -111
  181. data/generators/formtastic/formtastic_generator.rb +0 -26
  182. data/init.rb +0 -5
  183. data/lib/formtastic/layout_helper.rb +0 -12
  184. data/lib/formtastic/railtie.rb +0 -14
  185. data/lib/generators/templates/formtastic.css +0 -145
  186. data/lib/generators/templates/formtastic_changes.css +0 -14
  187. data/lib/generators/templates/rails2/_form.html.erb +0 -5
  188. data/lib/generators/templates/rails2/_form.html.haml +0 -4
  189. data/rails/init.rb +0 -2
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe 'string input' do
5
+
6
+ include FormtasticSpecHelper
7
+
8
+ before do
9
+ @output_buffer = ''
10
+ mock_everything
11
+ end
12
+
13
+ describe "with_options and :wrapper_html" do
14
+ before do
15
+ concat(semantic_form_for(@new_post) do |builder|
16
+ builder.with_options :wrapper_html => { :class => ['extra'] } do |opt_builder|
17
+ concat(opt_builder.input(:title, :as => :string))
18
+ concat(opt_builder.input(:author, :as => :radio))
19
+ end
20
+ end)
21
+ end
22
+
23
+ it "should have extra class on title" do
24
+ output_buffer.should have_tag("form li#post_title_input.extra")
25
+ end
26
+ it "should have title as string" do
27
+ output_buffer.should have_tag("form li#post_title_input.string")
28
+ end
29
+ it "should not have title as radio" do
30
+ output_buffer.should_not have_tag("form li#post_title_input.radio")
31
+ end
32
+
33
+ it "should have extra class on author" do
34
+ output_buffer.should have_tag("form li#post_author_input.extra")
35
+ end
36
+ it "should not have author as string" do
37
+ output_buffer.should_not have_tag("form li#post_author_input.string")
38
+ end
39
+ it "should have author as radio" do
40
+ output_buffer.should have_tag("form li#post_author_input.radio")
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,130 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe 'Formtastic::Localizer' do
5
+ describe "Cache" do
6
+ before do
7
+ @cache = Formtastic::Localizer::Cache.new
8
+ @key = ['model', 'name']
9
+ @undefined_key = ['model', 'undefined']
10
+ @cache.set(@key, 'value')
11
+ end
12
+
13
+ it "should get value" do
14
+ @cache.get(@key).should == 'value'
15
+ @cache.get(@undefined_key).should be_nil
16
+ end
17
+
18
+ it "should check if key exists?" do
19
+ @cache.has_key?(@key).should be_truthy
20
+ @cache.has_key?(@undefined_key).should be_falsey
21
+ end
22
+
23
+ it "should set a key" do
24
+ @cache.set(['model', 'name2'], 'value2')
25
+ @cache.get(['model', 'name2']).should == 'value2'
26
+ end
27
+
28
+ it "should return hash" do
29
+ @cache.cache.should be_an_instance_of(Hash)
30
+ end
31
+
32
+ it "should clear the cache" do
33
+ @cache.clear!
34
+ @cache.get(@key).should be_nil
35
+ end
36
+ end
37
+
38
+ describe "Localizer" do
39
+ include FormtasticSpecHelper
40
+
41
+ before do
42
+ mock_everything
43
+
44
+ with_config :i18n_lookups_by_default, true do
45
+ semantic_form_for(@new_post) do |builder|
46
+ @localizer = Formtastic::Localizer.new(builder)
47
+ end
48
+ end
49
+ end
50
+
51
+ after do
52
+ ::I18n.backend.reload!
53
+ end
54
+
55
+ it "should be defined" do
56
+ lambda { Formtastic::Localizer }.should_not raise_error
57
+ end
58
+
59
+ it "should have a cache" do
60
+ Formtastic::Localizer.cache.should be_an_instance_of(Formtastic::Localizer::Cache)
61
+ end
62
+
63
+ describe "localize" do
64
+ def store_post_translations(value)
65
+ ::I18n.backend.store_translations :en, {:formtastic => {
66
+ :labels => {
67
+ :post => { :name => value }
68
+ }
69
+ }
70
+ }
71
+ end
72
+
73
+ before do
74
+ store_post_translations('POST.NAME')
75
+ end
76
+
77
+ it "should translate key with i18n" do
78
+ @localizer.localize(:name, :name, :label).should == 'POST.NAME'
79
+ end
80
+
81
+ describe "with caching" do
82
+ it "should not update translation when stored translations change" do
83
+ with_config :i18n_cache_lookups, true do
84
+ @localizer.localize(:name, :name, :label).should == 'POST.NAME'
85
+ store_post_translations('POST.NEW_NAME')
86
+
87
+ @localizer.localize(:name, :name, :label).should == 'POST.NAME'
88
+
89
+ Formtastic::Localizer.cache.clear!
90
+ @localizer.localize(:name, :name, :label).should == 'POST.NEW_NAME'
91
+ end
92
+ end
93
+ end
94
+
95
+ describe "without caching" do
96
+ it "should update translation when stored translations change" do
97
+ with_config :i18n_cache_lookups, false do
98
+ @localizer.localize(:name, :name, :label).should == 'POST.NAME'
99
+ store_post_translations('POST.NEW_NAME')
100
+ @localizer.localize(:name, :name, :label).should == 'POST.NEW_NAME'
101
+ end
102
+ end
103
+ end
104
+
105
+ describe "with custom resource name" do
106
+ before do
107
+ ::I18n.backend.store_translations :en, {:formtastic => {
108
+ :labels => {
109
+ :post => { :name => 'POST.NAME' },
110
+ :message => { :name => 'MESSAGE.NAME' }
111
+ }
112
+ }
113
+ }
114
+
115
+ with_config :i18n_lookups_by_default, true do
116
+ semantic_form_for(@new_post, :as => :message) do |builder|
117
+ @localizer = Formtastic::Localizer.new(builder)
118
+ end
119
+ end
120
+ end
121
+
122
+ it "should translate custom key with i18n" do
123
+ @localizer.localize(:name, :name, :label).should == 'MESSAGE.NAME'
124
+ end
125
+ end
126
+ end
127
+
128
+ end
129
+
130
+ end
@@ -0,0 +1,79 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+ require 'formtastic/namespaced_class_finder'
4
+
5
+ describe Formtastic::NamespacedClassFinder do
6
+ include FormtasticSpecHelper
7
+
8
+ before do
9
+ stub_const('SearchPath', Module.new)
10
+ end
11
+
12
+ let(:search_path) { [ SearchPath ] }
13
+ subject(:finder) { Formtastic::NamespacedClassFinder.new(search_path) }
14
+
15
+ shared_examples 'Namespaced Class Finder' do
16
+ subject(:found_class) { finder.find(:custom_class) }
17
+
18
+ context 'Input defined in the Object scope' do
19
+ before do
20
+ stub_const('CustomClass', Class.new)
21
+ end
22
+
23
+ it { expect(found_class).to be(CustomClass) }
24
+ end
25
+
26
+ context 'Input defined in the search path' do
27
+ before do
28
+ stub_const('SearchPath::CustomClass', Class.new)
29
+ end
30
+
31
+ it { expect(found_class).to be(SearchPath::CustomClass) }
32
+ end
33
+
34
+ context 'Input defined both in the Object scope and the search path' do
35
+ before do
36
+ stub_const('CustomClass', Class.new)
37
+ stub_const('SearchPath::CustomClass', Class.new)
38
+ end
39
+
40
+ it { expect(found_class).to be(SearchPath::CustomClass) }
41
+ end
42
+
43
+ context 'Input defined outside the search path' do
44
+ before do
45
+ stub_const('Foo', Module.new)
46
+ stub_const('Foo::CustomClass', Class.new)
47
+ end
48
+
49
+ let(:error) { Formtastic::NamespacedClassFinder::NotFoundError }
50
+
51
+ it { expect { found_class }.to raise_error(error) }
52
+ end
53
+ end
54
+
55
+ context '#finder' do
56
+ before do
57
+ Rails.application.config.stub(:cache_classes).and_return(cache_classes)
58
+ end
59
+
60
+ context 'when cache_classes is on' do
61
+ let(:cache_classes) { true }
62
+ it_behaves_like 'Namespaced Class Finder'
63
+ end
64
+
65
+ context 'when cache_classes is off' do
66
+ let(:cache_classes) { false }
67
+ it_behaves_like 'Namespaced Class Finder'
68
+ end
69
+ end
70
+
71
+ context '#find' do
72
+ it 'caches calls' do
73
+ expect(subject).to receive(:resolve).once.and_call_original
74
+ subject.find(:object)
75
+ subject.find(:object)
76
+ end
77
+ end
78
+
79
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format=progress
@@ -0,0 +1,525 @@
1
+ # encoding: utf-8
2
+ require 'rubygems'
3
+ require 'bundler/setup'
4
+ require 'active_support'
5
+ require 'action_pack'
6
+ require 'action_view'
7
+ require 'action_controller'
8
+ require 'action_dispatch'
9
+
10
+ require File.expand_path(File.join(File.dirname(__FILE__), '../lib/formtastic/util'))
11
+ require File.expand_path(File.join(File.dirname(__FILE__), '../lib/formtastic'))
12
+
13
+ #rspec-rails 2-14 assumes a full rails install
14
+ module Rails
15
+ module VERSION
16
+ include ActionPack::VERSION
17
+ end
18
+ end
19
+
20
+ require 'ammeter/init'
21
+
22
+ # Requires supporting files with custom matchers and macros, etc,
23
+ # in ./support/ and its subdirectories in alphabetic order.
24
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].sort.each {|f| require f}
25
+
26
+ I18n.enforce_available_locales = false if I18n.respond_to?(:enforce_available_locales)
27
+
28
+ module FakeHelpersModule
29
+ end
30
+
31
+ module FormtasticSpecHelper
32
+ include ActionPack
33
+ include ActionView::Context if defined?(ActionView::Context)
34
+ include ActionController::RecordIdentifier if defined?(ActionController::RecordIdentifier)
35
+ include ActionView::Helpers::FormHelper
36
+ include ActionView::Helpers::FormTagHelper
37
+ include ActionView::Helpers::FormOptionsHelper
38
+ include ActionView::Helpers::UrlHelper
39
+ include ActionView::Helpers::TagHelper
40
+ include ActionView::Helpers::TextHelper
41
+ include ActionView::Helpers::ActiveRecordHelper if defined?(ActionView::Helpers::ActiveRecordHelper)
42
+ include ActionView::Helpers::ActiveModelHelper if defined?(ActionView::Helpers::ActiveModelHelper)
43
+ include ActionView::Helpers::DateHelper
44
+ include ActionView::Helpers::CaptureHelper
45
+ include ActionView::Helpers::AssetTagHelper
46
+ include ActiveSupport
47
+ include ActionController::PolymorphicRoutes if defined?(ActionController::PolymorphicRoutes)
48
+ include ActionDispatch::Routing::PolymorphicRoutes
49
+ include AbstractController::UrlFor if defined?(AbstractController::UrlFor)
50
+ include ActionView::RecordIdentifier if defined?(ActionView::RecordIdentifier)
51
+
52
+ include Formtastic::Helpers::FormHelper
53
+
54
+ def default_input_type(column_type, column_name = :generic_column_name)
55
+ @new_post.stub(column_name)
56
+ @new_post.stub(:column_for_attribute).and_return(double('column', :type => column_type)) unless column_type.nil?
57
+
58
+ semantic_form_for(@new_post) do |builder|
59
+ @default_type = builder.send(:default_input_type, column_name)
60
+ end
61
+
62
+ return @default_type
63
+ end
64
+
65
+ def active_model_validator(kind, attributes, options = {})
66
+ validator = double("ActiveModel::Validations::#{kind.to_s.titlecase}Validator", :attributes => attributes, :options => options)
67
+ validator.stub(:kind).and_return(kind)
68
+ validator
69
+ end
70
+
71
+ def active_model_presence_validator(attributes, options = {})
72
+ active_model_validator(:presence, attributes, options)
73
+ end
74
+
75
+ def active_model_length_validator(attributes, options = {})
76
+ active_model_validator(:length, attributes, options)
77
+ end
78
+
79
+ def active_model_inclusion_validator(attributes, options = {})
80
+ active_model_validator(:inclusion, attributes, options)
81
+ end
82
+
83
+ def active_model_numericality_validator(attributes, options = {})
84
+ active_model_validator(:numericality, attributes, options)
85
+ end
86
+
87
+ class ::MongoPost
88
+ include MongoMapper::Document if defined?(MongoMapper::Document)
89
+
90
+ def id
91
+ end
92
+
93
+ def persisted?
94
+ end
95
+ end
96
+
97
+
98
+ class ::Post
99
+ extend ActiveModel::Naming if defined?(ActiveModel::Naming)
100
+ include ActiveModel::Conversion if defined?(ActiveModel::Conversion)
101
+
102
+ def id
103
+ end
104
+
105
+ def persisted?
106
+ end
107
+ end
108
+
109
+ module ::Namespaced
110
+ class Post
111
+ extend ActiveModel::Naming if defined?(ActiveModel::Naming)
112
+ include ActiveModel::Conversion if defined?(ActiveModel::Conversion)
113
+
114
+ def id
115
+ end
116
+
117
+ def persisted?
118
+ end
119
+ end
120
+ end
121
+
122
+ class ::Author
123
+ extend ActiveModel::Naming if defined?(ActiveModel::Naming)
124
+ include ActiveModel::Conversion if defined?(ActiveModel::Conversion)
125
+
126
+ def to_label
127
+ end
128
+
129
+ def persisted?
130
+ end
131
+ end
132
+
133
+ class ::HashBackedAuthor < Hash
134
+ extend ActiveModel::Naming if defined?(ActiveModel::Naming)
135
+ include ActiveModel::Conversion if defined?(ActiveModel::Conversion)
136
+ def persisted?; false; end
137
+ def name
138
+ 'hash backed author'
139
+ end
140
+ end
141
+
142
+ class ::Continent
143
+ extend ActiveModel::Naming if defined?(ActiveModel::Naming)
144
+ include ActiveModel::Conversion if defined?(ActiveModel::Conversion)
145
+ end
146
+
147
+ class ::PostModel
148
+ extend ActiveModel::Naming if defined?(ActiveModel::Naming)
149
+ include ActiveModel::Conversion if defined?(ActiveModel::Conversion)
150
+ end
151
+
152
+ ##
153
+ # We can't mock :respond_to?, so we need a concrete class override
154
+ class ::MongoidReflectionMock < RSpec::Mocks::Double
155
+ def initialize(name=nil, stubs_and_options={})
156
+ super name, stubs_and_options
157
+ end
158
+
159
+ def respond_to?(sym)
160
+ sym == :options ? false : super
161
+ end
162
+ end
163
+
164
+ # In Rails 3 Model.all returns an array. In Rails 4, it returns an
165
+ # association proxy, which quacks a lot like an array. We use this in stubs
166
+ # or mocks where we need to return the later.
167
+ #
168
+ # TODO try delegate?
169
+ # delegate :map, :size, :length, :first, :to_ary, :each, :include?, :to => :array
170
+ class MockScope
171
+ attr_reader :array
172
+
173
+ def initialize(the_array)
174
+ @array = the_array
175
+ end
176
+
177
+ def map(&block)
178
+ array.map(&block)
179
+ end
180
+
181
+ def where(*args)
182
+ # array
183
+ self
184
+ end
185
+
186
+ def includes(*args)
187
+ self
188
+ end
189
+
190
+ def size
191
+ array.size
192
+ end
193
+ alias_method :length, :size
194
+
195
+ def first
196
+ array.first
197
+ end
198
+
199
+ def to_ary
200
+ array
201
+ end
202
+
203
+ def each(&block)
204
+ array.each(&block)
205
+ end
206
+
207
+ def include?(*args)
208
+ array.include?(*args)
209
+ end
210
+ end
211
+
212
+ def _routes
213
+ url_helpers = double('url_helpers')
214
+ url_helpers.stub(:hash_for_posts_path).and_return({})
215
+ url_helpers.stub(:hash_for_post_path).and_return({})
216
+ url_helpers.stub(:hash_for_post_models_path).and_return({})
217
+ url_helpers.stub(:hash_for_authors_path).and_return({})
218
+
219
+ double('_routes',
220
+ :url_helpers => url_helpers,
221
+ :url_for => "/mock/path"
222
+ )
223
+ end
224
+
225
+ def controller
226
+ env = double('env', :[] => nil)
227
+ request = double('request', :env => env)
228
+ double('controller', :controller_path= => '', :params => {}, :request => request)
229
+ end
230
+
231
+ def default_url_options
232
+ {}
233
+ end
234
+
235
+ def mock_everything
236
+
237
+ # Resource-oriented styles like form_for(@post) will expect a path method for the object,
238
+ # so we're defining some here.
239
+ def post_models_path(*args); "/postmodels/1"; end
240
+
241
+ def post_path(*args); "/posts/1"; end
242
+ def posts_path(*args); "/posts"; end
243
+ def new_post_path(*args); "/posts/new"; end
244
+
245
+ def author_path(*args); "/authors/1"; end
246
+ def authors_path(*args); "/authors"; end
247
+ def new_author_path(*args); "/authors/new"; end
248
+
249
+ # Returns the array for Rails 3 and a thing that looks looks like an
250
+ # association proxy for Rails 4+
251
+ def author_array_or_scope(the_array = [@fred, @bob])
252
+ if ::Formtastic::Util.rails3?
253
+ the_array
254
+ else
255
+ MockScope.new(the_array)
256
+ end
257
+ end
258
+
259
+ @fred = ::Author.new
260
+ @fred.stub(:class).and_return(::Author)
261
+ @fred.stub(:to_label).and_return('Fred Smith')
262
+ @fred.stub(:login).and_return('fred_smith')
263
+ @fred.stub(:age).and_return(27)
264
+ @fred.stub(:id).and_return(37)
265
+ @fred.stub(:new_record?).and_return(false)
266
+ @fred.stub(:errors).and_return(double('errors', :[] => nil))
267
+ @fred.stub(:to_key).and_return(nil)
268
+ @fred.stub(:persisted?).and_return(nil)
269
+ @fred.stub(:name).and_return('Fred')
270
+
271
+ @bob = ::Author.new
272
+ @bob.stub(:to_label).and_return('Bob Rock')
273
+ @bob.stub(:login).and_return('bob')
274
+ @bob.stub(:age).and_return(43)
275
+ @bob.stub(:created_at)
276
+ @bob.stub(:id).and_return(42)
277
+ @bob.stub(:posts).and_return([])
278
+ @bob.stub(:post_ids).and_return([])
279
+ @bob.stub(:new_record?).and_return(false)
280
+ @bob.stub(:errors).and_return(double('errors', :[] => nil))
281
+ @bob.stub(:to_key).and_return(nil)
282
+ @bob.stub(:persisted?).and_return(nil)
283
+ @bob.stub(:name).and_return('Bob')
284
+
285
+ @james = ::Author.new
286
+ @james.stub(:to_label).and_return('James Shock')
287
+ @james.stub(:login).and_return('james')
288
+ @james.stub(:age).and_return(38)
289
+ @james.stub(:id).and_return(75)
290
+ @james.stub(:posts).and_return([])
291
+ @james.stub(:post_ids).and_return([])
292
+ @james.stub(:new_record?).and_return(false)
293
+ @james.stub(:errors).and_return(double('errors', :[] => nil))
294
+ @james.stub(:to_key).and_return(nil)
295
+ @james.stub(:persisted?).and_return(nil)
296
+ @james.stub(:name).and_return('James')
297
+
298
+
299
+ ::Author.stub(:scoped).and_return(::Author)
300
+ ::Author.stub(:find).and_return(author_array_or_scope)
301
+ ::Author.stub(:all).and_return(author_array_or_scope)
302
+ ::Author.stub(:where).and_return(author_array_or_scope)
303
+ ::Author.stub(:human_attribute_name) { |column_name| column_name.humanize }
304
+ ::Author.stub(:human_name).and_return('::Author')
305
+ ::Author.stub(:reflect_on_association) { |column_name| double('reflection', :options => {}, :klass => Post, :macro => :has_many) if column_name == :posts }
306
+ ::Author.stub(:content_columns).and_return([double('column', :name => 'login'), double('column', :name => 'created_at')])
307
+ ::Author.stub(:to_key).and_return(nil)
308
+ ::Author.stub(:persisted?).and_return(nil)
309
+
310
+ @hash_backed_author = HashBackedAuthor.new
311
+
312
+ # Sometimes we need a mock @post object and some Authors for belongs_to
313
+ @new_post = double('post')
314
+ @new_post.stub(:class).and_return(::Post)
315
+ @new_post.stub(:id).and_return(nil)
316
+ @new_post.stub(:new_record?).and_return(true)
317
+ @new_post.stub(:errors).and_return(double('errors', :[] => nil))
318
+ @new_post.stub(:author).and_return(nil)
319
+ @new_post.stub(:author_attributes=).and_return(nil)
320
+ @new_post.stub(:authors).and_return(author_array_or_scope([@fred]))
321
+ @new_post.stub(:authors_attributes=)
322
+ @new_post.stub(:reviewer).and_return(nil)
323
+ @new_post.stub(:main_post).and_return(nil)
324
+ @new_post.stub(:sub_posts).and_return([]) #TODO should be a mock with methods for adding sub posts
325
+ @new_post.stub(:to_key).and_return(nil)
326
+ @new_post.stub(:to_model).and_return(@new_post)
327
+ @new_post.stub(:persisted?).and_return(nil)
328
+ @new_post.stub(:model_name){ @new_post.class.model_name}
329
+
330
+ @freds_post = double('post')
331
+ @freds_post.stub(:to_ary)
332
+ @freds_post.stub(:class).and_return(::Post)
333
+ @freds_post.stub(:to_label).and_return('Fred Smith')
334
+ @freds_post.stub(:id).and_return(19)
335
+ @freds_post.stub(:title).and_return("Hello World")
336
+ @freds_post.stub(:author).and_return(@fred)
337
+ @freds_post.stub(:author_id).and_return(@fred.id)
338
+ @freds_post.stub(:authors).and_return([@fred])
339
+ @freds_post.stub(:author_ids).and_return([@fred.id])
340
+ @freds_post.stub(:new_record?).and_return(false)
341
+ @freds_post.stub(:errors).and_return(double('errors', :[] => nil))
342
+ @freds_post.stub(:to_key).and_return(nil)
343
+ @freds_post.stub(:persisted?).and_return(nil)
344
+ @freds_post.stub(:model_name){ @freds_post.class.model_name}
345
+ @freds_post.stub(:to_model).and_return(@freds_post)
346
+ @fred.stub(:posts).and_return(author_array_or_scope([@freds_post]))
347
+ @fred.stub(:post_ids).and_return([@freds_post.id])
348
+
349
+ ::Post.stub(:scoped).and_return(::Post)
350
+ ::Post.stub(:human_attribute_name) { |column_name| column_name.humanize }
351
+ ::Post.stub(:human_name).and_return('Post')
352
+ ::Post.stub(:reflect_on_all_validations).and_return([])
353
+ ::Post.stub(:reflect_on_validations_for).and_return([])
354
+ ::Post.stub(:reflections).and_return({})
355
+ ::Post.stub(:reflect_on_association) { |column_name|
356
+ case column_name
357
+ when :author, :author_status
358
+ mock = double('reflection', :options => {}, :klass => ::Author, :macro => :belongs_to)
359
+ mock.stub(:[]).with(:class_name).and_return("Author")
360
+ mock
361
+ when :reviewer
362
+ mock = double('reflection', :options => {:class_name => 'Author'}, :klass => ::Author, :macro => :belongs_to)
363
+ mock.stub(:[]).with(:class_name).and_return("Author")
364
+ mock
365
+ when :authors
366
+ double('reflection', :options => {}, :klass => ::Author, :macro => :has_and_belongs_to_many)
367
+ when :sub_posts
368
+ double('reflection', :options => {}, :klass => ::Post, :macro => :has_many)
369
+ when :main_post
370
+ double('reflection', :options => {}, :klass => ::Post, :macro => :belongs_to)
371
+ when :mongoid_reviewer
372
+ ::MongoidReflectionMock.new('reflection',
373
+ :options => Proc.new { raise NoMethodError, "Mongoid has no reflection.options" },
374
+ :klass => ::Author, :macro => :referenced_in, :foreign_key => "reviewer_id") # custom id
375
+ end
376
+ }
377
+ ::Post.stub(:find).and_return(author_array_or_scope([@freds_post]))
378
+ ::Post.stub(:all).and_return(author_array_or_scope([@freds_post]))
379
+ ::Post.stub(:where).and_return(author_array_or_scope([@freds_post]))
380
+ ::Post.stub(:content_columns).and_return([double('column', :name => 'title'), double('column', :name => 'body'), double('column', :name => 'created_at')])
381
+ ::Post.stub(:to_key).and_return(nil)
382
+ ::Post.stub(:persisted?).and_return(nil)
383
+ ::Post.stub(:to_ary)
384
+
385
+ ::MongoPost.stub(:human_attribute_name) { |column_name| column_name.humanize }
386
+ ::MongoPost.stub(:human_name).and_return('MongoPost')
387
+ ::MongoPost.stub(:associations).and_return({
388
+ :sub_posts => double('reflection', :options => {:polymorphic => true}, :klass => ::MongoPost, :macro => :has_many),
389
+ :options => []
390
+ })
391
+ ::MongoPost.stub(:find).and_return(author_array_or_scope([@freds_post]))
392
+ ::MongoPost.stub(:all).and_return(author_array_or_scope([@freds_post]))
393
+ ::MongoPost.stub(:where).and_return(author_array_or_scope([@freds_post]))
394
+ ::MongoPost.stub(:to_key).and_return(nil)
395
+ ::MongoPost.stub(:persisted?).and_return(nil)
396
+ ::MongoPost.stub(:to_ary)
397
+ ::MongoPost.stub(:model_name).and_return( double(:model_name_mock, :singular => "post", :plural => "posts", :param_key => "post", :route_key => "posts") )
398
+
399
+ @new_mm_post = double('mm_post')
400
+ @new_mm_post.stub(:class).and_return(::MongoPost)
401
+ @new_mm_post.stub(:id).and_return(nil)
402
+ @new_mm_post.stub(:new_record?).and_return(true)
403
+ @new_mm_post.stub(:errors).and_return(double('errors', :[] => nil))
404
+ @new_mm_post.stub(:title).and_return("Hello World")
405
+ @new_mm_post.stub(:sub_posts).and_return([]) #TODO should be a mock with methods for adding sub posts
406
+ @new_mm_post.stub(:to_key).and_return(nil)
407
+ @new_mm_post.stub(:to_model).and_return(@new_mm_post)
408
+ @new_mm_post.stub(:persisted?).and_return(nil)
409
+ @new_mm_post.stub(:model_name).and_return(::MongoPost.model_name)
410
+
411
+ @mock_file = double('file')
412
+ Formtastic::FormBuilder.file_methods.each do |method|
413
+ @mock_file.stub(method).and_return(true)
414
+ end
415
+
416
+ @new_post.stub(:title)
417
+ @new_post.stub(:email)
418
+ @new_post.stub(:url)
419
+ @new_post.stub(:phone)
420
+ @new_post.stub(:search)
421
+ @new_post.stub(:to_ary)
422
+ @new_post.stub(:body)
423
+ @new_post.stub(:published)
424
+ @new_post.stub(:publish_at)
425
+ @new_post.stub(:created_at)
426
+ @new_post.stub(:secret).and_return(1)
427
+ @new_post.stub(:url)
428
+ @new_post.stub(:email)
429
+ @new_post.stub(:color)
430
+ @new_post.stub(:search)
431
+ @new_post.stub(:phone)
432
+ @new_post.stub(:time_zone)
433
+ @new_post.stub(:category_name)
434
+ @new_post.stub(:allow_comments).and_return(true)
435
+ @new_post.stub(:answer_comments)
436
+ @new_post.stub(:country)
437
+ @new_post.stub(:country_subdivision)
438
+ @new_post.stub(:country_code)
439
+ @new_post.stub(:document).and_return(@mock_file)
440
+ @new_post.stub(:column_for_attribute).with(:meta_description).and_return(double('column', :type => :string, :limit => 255))
441
+ @new_post.stub(:column_for_attribute).with(:title).and_return(double('column', :type => :string, :limit => 50))
442
+ @new_post.stub(:column_for_attribute).with(:body).and_return(double('column', :type => :text))
443
+ @new_post.stub(:column_for_attribute).with(:published).and_return(double('column', :type => :boolean))
444
+ @new_post.stub(:column_for_attribute).with(:publish_at).and_return(double('column', :type => :date))
445
+ @new_post.stub(:column_for_attribute).with(:time_zone).and_return(double('column', :type => :string))
446
+ @new_post.stub(:column_for_attribute).with(:allow_comments).and_return(double('column', :type => :boolean))
447
+ @new_post.stub(:column_for_attribute).with(:author).and_return(double('column', :type => :integer))
448
+ @new_post.stub(:column_for_attribute).with(:country).and_return(double('column', :type => :string, :limit => 255))
449
+ @new_post.stub(:column_for_attribute).with(:country_subdivision).and_return(double('column', :type => :string, :limit => 255))
450
+ @new_post.stub(:column_for_attribute).with(:country_code).and_return(double('column', :type => :string, :limit => 255))
451
+ @new_post.stub(:column_for_attribute).with(:email).and_return(double('column', :type => :string, :limit => 255))
452
+ @new_post.stub(:column_for_attribute).with(:color).and_return(double('column', :type => :string, :limit => 255))
453
+ @new_post.stub(:column_for_attribute).with(:url).and_return(double('column', :type => :string, :limit => 255))
454
+ @new_post.stub(:column_for_attribute).with(:phone).and_return(double('column', :type => :string, :limit => 255))
455
+ @new_post.stub(:column_for_attribute).with(:search).and_return(double('column', :type => :string, :limit => 255))
456
+ @new_post.stub(:column_for_attribute).with(:document).and_return(nil)
457
+
458
+ @new_post.stub(:author).and_return(@bob)
459
+ @new_post.stub(:author_id).and_return(@bob.id)
460
+
461
+ @new_post.stub(:reviewer).and_return(@fred)
462
+ @new_post.stub(:reviewer_id).and_return(@fred.id)
463
+
464
+ # @new_post.should_receive(:publish_at=).at_least(:once)
465
+ @new_post.stub(:publish_at=)
466
+ # @new_post.should_receive(:title=).at_least(:once)
467
+ @new_post.stub(:title=)
468
+ @new_post.stub(:main_post_id).and_return(nil)
469
+
470
+ end
471
+
472
+ def self.included(base)
473
+ base.class_eval do
474
+
475
+ attr_accessor :output_buffer
476
+
477
+ def protect_against_forgery?
478
+ false
479
+ end
480
+
481
+ def _helpers
482
+ FakeHelpersModule
483
+ end
484
+
485
+ end
486
+ end
487
+
488
+ def with_config(config_method_name, value, &block)
489
+ old_value = Formtastic::FormBuilder.send(config_method_name)
490
+ Formtastic::FormBuilder.send(:"#{config_method_name}=", value)
491
+ yield
492
+ Formtastic::FormBuilder.send(:"#{config_method_name}=", old_value)
493
+ end
494
+
495
+ RSpec::Matchers.define :errors_matcher do |expected|
496
+ match { |actual| actual.to_s == expected.to_s }
497
+ end
498
+ end
499
+
500
+ class ::ActionView::Base
501
+ include Formtastic::Helpers::FormHelper
502
+ end
503
+
504
+ ::ActiveSupport::Deprecation.silenced = false
505
+
506
+ RSpec.configure do |config|
507
+ config.infer_spec_type_from_file_location!
508
+
509
+ config.filter_run focus: true
510
+ config.filter_run_excluding skip: true
511
+ config.run_all_when_everything_filtered = true
512
+
513
+ config.before(:each) do
514
+ Formtastic::Localizer.cache.clear!
515
+ end
516
+
517
+ config.before(:each) do
518
+ allow(Formtastic.deprecation).to receive(:deprecation_warning).and_call_original
519
+ # TODO: Remove this in Formtastic 4
520
+ [ :action_class, :standard_action_class_name, :custom_action_class_name,
521
+ :input_class, :standard_input_class_name, :custom_input_class_name ].each do |method|
522
+ allow(Formtastic.deprecation).to receive(:deprecation_warning).with(method, instance_of(String), instance_of(Array))
523
+ end
524
+ end
525
+ end