formtastic 1.2.4 → 3.1.5

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 (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