aasm 5.0.1 → 5.0.6

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +27 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  4. data/.travis.yml +41 -13
  5. data/Appraisals +18 -13
  6. data/CHANGELOG.md +37 -0
  7. data/Gemfile +1 -1
  8. data/README.md +132 -92
  9. data/aasm.gemspec +2 -0
  10. data/gemfiles/norails.gemfile +10 -0
  11. data/gemfiles/rails_3.2.gemfile +2 -1
  12. data/gemfiles/rails_4.2.gemfile +1 -1
  13. data/gemfiles/rails_4.2_mongoid_5.gemfile +1 -1
  14. data/gemfiles/rails_4.2_nobrainer.gemfile +1 -1
  15. data/gemfiles/rails_5.0.gemfile +2 -2
  16. data/gemfiles/rails_5.0_nobrainer.gemfile +1 -1
  17. data/gemfiles/rails_5.1.gemfile +2 -2
  18. data/gemfiles/rails_5.2.gemfile +13 -0
  19. data/lib/aasm/aasm.rb +29 -27
  20. data/lib/aasm/base.rb +25 -7
  21. data/lib/aasm/core/event.rb +9 -4
  22. data/lib/aasm/errors.rb +2 -1
  23. data/lib/aasm/instance_base.rb +4 -3
  24. data/lib/aasm/persistence/active_record_persistence.rb +2 -1
  25. data/lib/aasm/persistence/base.rb +1 -1
  26. data/lib/aasm/persistence/core_data_query_persistence.rb +2 -1
  27. data/lib/aasm/persistence/dynamoid_persistence.rb +1 -1
  28. data/lib/aasm/persistence/mongoid_persistence.rb +1 -1
  29. data/lib/aasm/persistence/no_brainer_persistence.rb +1 -1
  30. data/lib/aasm/persistence/redis_persistence.rb +1 -1
  31. data/lib/aasm/version.rb +1 -1
  32. data/lib/generators/aasm/orm_helpers.rb +6 -0
  33. data/lib/generators/active_record/aasm_generator.rb +3 -1
  34. data/spec/database.rb +12 -0
  35. data/spec/generators/active_record_generator_spec.rb +6 -0
  36. data/spec/models/active_record/instance_level_skip_validation_example.rb +19 -0
  37. data/spec/models/active_record/person.rb +23 -0
  38. data/spec/models/active_record/work.rb +3 -0
  39. data/spec/models/callbacks/with_state_arg.rb +5 -1
  40. data/spec/models/callbacks/with_state_arg_multiple.rb +4 -1
  41. data/spec/spec_helper.rb +10 -0
  42. data/spec/unit/abstract_class_spec.rb +27 -0
  43. data/spec/unit/callback_multiple_spec.rb +4 -0
  44. data/spec/unit/callbacks_spec.rb +30 -0
  45. data/spec/unit/complex_example_spec.rb +0 -1
  46. data/spec/unit/event_spec.rb +13 -0
  47. data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +4 -4
  48. data/spec/unit/persistence/active_record_persistence_spec.rb +26 -4
  49. metadata +42 -3
  50. data/gemfiles/rails_4.0.gemfile +0 -15
@@ -23,6 +23,8 @@ Gem::Specification.new do |s|
23
23
  s.add_development_dependency 'rspec', ">= 3"
24
24
  s.add_development_dependency 'generator_spec'
25
25
  s.add_development_dependency 'appraisal'
26
+ s.add_development_dependency "simplecov"
27
+ s.add_development_dependency "codecov", ">= 0.1.10"
26
28
 
27
29
  # debugging
28
30
  # s.add_development_dependency 'debugger'
@@ -0,0 +1,10 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sqlite3", "~> 1.3.5", platforms: :ruby
6
+ gem "rails", install_if: false
7
+ gem "sequel"
8
+ gem "redis-objects"
9
+
10
+ gemspec path: "../"
@@ -2,12 +2,13 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "sqlite3", platforms: :ruby
5
+ gem "sqlite3", "~> 1.3.5", platforms: :ruby
6
6
  gem "rails", "~> 3.2.22"
7
7
  gem "mongoid", "~> 3.1"
8
8
  gem "sequel"
9
9
  gem "bson_ext", platforms: :ruby
10
10
  gem "test-unit", "~> 3.0"
11
+ gem "minitest"
11
12
  gem "activerecord-jdbcsqlite3-adapter", "1.3.24", platforms: :jruby
12
13
 
13
14
  gemspec path: "../"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "sqlite3", platforms: :ruby
5
+ gem "sqlite3", "~> 1.3.5", platforms: :ruby
6
6
  gem "rails", "4.2.5"
7
7
  gem "nokogiri", "1.6.8.1", platforms: [:ruby_19]
8
8
  gem "mime-types", "~> 2", platforms: [:ruby_19, :jruby]
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "sqlite3", platforms: :ruby
5
+ gem "sqlite3", "~> 1.3.5", platforms: :ruby
6
6
  gem "rails", "4.2.5"
7
7
  gem "mime-types", "~> 2", platforms: [:ruby_19, :jruby]
8
8
  gem "mongoid", "~> 5.0"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "sqlite3", platforms: :ruby
5
+ gem "sqlite3", "~> 1.3.5", platforms: :ruby
6
6
  gem "rails", "4.2.5"
7
7
  gem "nobrainer", "~> 0.33.0"
8
8
 
@@ -2,11 +2,11 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "sqlite3", platforms: :ruby
5
+ gem "sqlite3", "~> 1.3.5", platforms: :ruby
6
6
  gem "rails", "5.0.0"
7
7
  gem "mongoid", "~> 6.0"
8
8
  gem "sequel"
9
- gem "dynamoid", "~> 1", platforms: :ruby
9
+ gem "dynamoid", "~> 1.3", platforms: :ruby
10
10
  gem "aws-sdk", "~> 2", platforms: :ruby
11
11
  gem "redis-objects"
12
12
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "sqlite3", platforms: :ruby
5
+ gem "sqlite3", "~> 1.3.5", platforms: :ruby
6
6
  gem "rails", "5.0.0"
7
7
  gem "nobrainer", "~> 0.33.0"
8
8
 
@@ -2,11 +2,11 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "sqlite3", platforms: :ruby
5
+ gem "sqlite3", "~> 1.3.5", platforms: :ruby
6
6
  gem "rails", "5.1"
7
7
  gem "mongoid", "~>6.0"
8
8
  gem "sequel"
9
- gem "dynamoid", "~> 1", platforms: :ruby
9
+ gem "dynamoid", "~> 1.3", platforms: :ruby
10
10
  gem "aws-sdk", "~>2", platforms: :ruby
11
11
  gem "redis-objects"
12
12
 
@@ -0,0 +1,13 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sqlite3", "~> 1.3.5", platforms: :ruby
6
+ gem "rails", "5.2"
7
+ gem "mongoid", "~>6.0"
8
+ gem "sequel"
9
+ gem "dynamoid", "~>2.2", platforms: :ruby
10
+ gem "aws-sdk", "~>2", platforms: :ruby
11
+ gem "redis-objects"
12
+
13
+ gemspec path: "../"
@@ -99,25 +99,10 @@ private
99
99
  begin
100
100
  old_state = aasm(state_machine_name).state_object_for_name(aasm(state_machine_name).current_state)
101
101
 
102
- event.fire_global_callbacks(
103
- :before_all_events,
104
- self,
105
- *process_args(event, aasm(state_machine_name).current_state, *args)
106
- )
107
-
108
- # new event before callback
109
- event.fire_callbacks(
110
- :before,
111
- self,
112
- *process_args(event, aasm(state_machine_name).current_state, *args)
113
- )
102
+ fire_default_callbacks(event, *process_args(event, aasm(state_machine_name).current_state, *args))
114
103
 
115
104
  if may_fire_to = event.may_fire?(self, *args)
116
- old_state.fire_callbacks(:before_exit, self,
117
- *process_args(event, aasm(state_machine_name).current_state, *args))
118
- old_state.fire_callbacks(:exit, self,
119
- *process_args(event, aasm(state_machine_name).current_state, *args))
120
-
105
+ fire_exit_callbacks(old_state, *process_args(event, aasm(state_machine_name).current_state, *args))
121
106
  if new_state_name = event.fire(self, {:may_fire => may_fire_to}, *args)
122
107
  aasm_fired(state_machine_name, event, old_state, new_state_name, options, *args, &block)
123
108
  else
@@ -137,25 +122,44 @@ private
137
122
  end
138
123
  end
139
124
 
125
+ def fire_default_callbacks(event, *processed_args)
126
+ event.fire_global_callbacks(
127
+ :before_all_events,
128
+ self,
129
+ *processed_args
130
+ )
131
+
132
+ # new event before callback
133
+ event.fire_callbacks(
134
+ :before,
135
+ self,
136
+ *processed_args
137
+ )
138
+ end
139
+
140
+ def fire_exit_callbacks(old_state, *processed_args)
141
+ old_state.fire_callbacks(:before_exit, self, *processed_args)
142
+ old_state.fire_callbacks(:exit, self, *processed_args)
143
+ end
144
+
140
145
  def aasm_fired(state_machine_name, event, old_state, new_state_name, options, *args)
141
146
  persist = options[:persist]
142
147
 
143
148
  new_state = aasm(state_machine_name).state_object_for_name(new_state_name)
149
+ callback_args = process_args(event, aasm(state_machine_name).current_state, *args)
144
150
 
145
- new_state.fire_callbacks(:before_enter, self,
146
- *process_args(event, aasm(state_machine_name).current_state, *args))
151
+ new_state.fire_callbacks(:before_enter, self, *callback_args)
147
152
 
148
- new_state.fire_callbacks(:enter, self,
149
- *process_args(event, aasm(state_machine_name).current_state, *args)) # TODO: remove for AASM 4?
153
+ new_state.fire_callbacks(:enter, self, *callback_args) # TODO: remove for AASM 4?
150
154
 
151
155
  persist_successful = true
152
156
  if persist
153
157
  persist_successful = aasm(state_machine_name).set_current_state_with_persistence(new_state_name)
154
158
  if persist_successful
155
159
  yield if block_given?
156
- event.fire_callbacks(:before_success, self)
160
+ event.fire_callbacks(:before_success, self, *callback_args)
157
161
  event.fire_transition_callbacks(self, *process_args(event, old_state.name, *args))
158
- event.fire_callbacks(:success, self)
162
+ event.fire_callbacks(:success, self, *callback_args)
159
163
  end
160
164
  else
161
165
  aasm(state_machine_name).current_state = new_state_name
@@ -168,10 +172,8 @@ private
168
172
  end
169
173
 
170
174
  if persist_successful
171
- old_state.fire_callbacks(:after_exit, self,
172
- *process_args(event, aasm(state_machine_name).current_state, *args))
173
- new_state.fire_callbacks(:after_enter, self,
174
- *process_args(event, aasm(state_machine_name).current_state, *args))
175
+ old_state.fire_callbacks(:after_exit, self, *callback_args)
176
+ new_state.fire_callbacks(:after_enter, self, *callback_args)
175
177
  event.fire_callbacks(
176
178
  :after,
177
179
  self,
@@ -54,13 +54,14 @@ module AASM
54
54
  # make sure to raise an error if no_direct_assignment is enabled
55
55
  # and attribute is directly assigned though
56
56
  aasm_name = @name
57
- klass.send :define_method, "#{@state_machine.config.column}=", ->(state_name) do
58
- if self.class.aasm(:"#{aasm_name}").state_machine.config.no_direct_assignment
59
- raise AASM::NoDirectAssignmentError.new(
60
- 'direct assignment of AASM column has been disabled (see AASM configuration for this class)'
61
- )
62
- else
63
- super(state_name)
57
+
58
+ if @state_machine.config.no_direct_assignment
59
+ @klass.send(:define_method, "#{@state_machine.config.column}=") do |state_name|
60
+ if self.class.aasm(:"#{aasm_name}").state_machine.config.no_direct_assignment
61
+ raise AASM::NoDirectAssignmentError.new('direct assignment of AASM column has been disabled (see AASM configuration for this class)')
62
+ else
63
+ super(state_name)
64
+ end
64
65
  end
65
66
  end
66
67
  end
@@ -134,6 +135,8 @@ module AASM
134
135
  aasm_fire_event(aasm_name, event, {:persist => false}, *args, &block)
135
136
  end
136
137
 
138
+ skip_instance_level_validation(event, name, aasm_name, klass)
139
+
137
140
  # Create aliases for the event methods. Keep the old names to maintain backwards compatibility.
138
141
  if namespace?
139
142
  klass.send(:alias_method, "may_#{name}_#{namespace}?", "may_#{name}?")
@@ -249,5 +252,20 @@ module AASM
249
252
  end
250
253
  end
251
254
 
255
+ def skip_instance_level_validation(event, name, aasm_name, klass)
256
+ # Overrides the skip_validation config for an instance (If skip validation is set to false in original config) and
257
+ # restores it back to the original value after the event is fired.
258
+ safely_define_method klass, "#{name}_without_validation!", ->(*args, &block) do
259
+ original_config = AASM::StateMachineStore.fetch(self.class, true).machine(aasm_name).config.skip_validation_on_save
260
+ begin
261
+ AASM::StateMachineStore.fetch(self.class, true).machine(aasm_name).config.skip_validation_on_save = true unless original_config
262
+ aasm(aasm_name).current_event = :"#{name}!"
263
+ aasm_fire_event(aasm_name, event, {:persist => true}, *args, &block)
264
+ ensure
265
+ AASM::StateMachineStore.fetch(self.class, true).machine(aasm_name).config.skip_validation_on_save = original_config
266
+ end
267
+ end
268
+ end
269
+
252
270
  end
253
271
  end
@@ -125,21 +125,21 @@ module AASM::Core
125
125
 
126
126
  def _fire(obj, options={}, to_state=::AASM::NO_VALUE, *args)
127
127
  result = options[:test_only] ? false : nil
128
+ clear_failed_callbacks
128
129
  transitions = @transitions.select { |t| t.from == obj.aasm(state_machine.name).current_state || t.from == nil}
129
130
  return result if transitions.size == 0
130
131
 
131
132
  if to_state == ::AASM::NO_VALUE
132
133
  to_state = nil
133
- elsif to_state.respond_to?(:to_sym) && transitions.map(&:to).flatten.include?(to_state.to_sym)
134
- # nop, to_state is a valid to-state
135
- else
134
+ elsif !(to_state.respond_to?(:to_sym) && transitions.map(&:to).flatten.include?(to_state.to_sym))
136
135
  # to_state is an argument
137
136
  args.unshift(to_state)
138
137
  to_state = nil
139
138
  end
139
+
140
+ # nop, to_state is a valid to-state
140
141
 
141
142
  transitions.each do |transition|
142
- transition.failures.clear #https://github.com/aasm/aasm/issues/383
143
143
  next if to_state and !Array(transition.to).include?(to_state)
144
144
  if (options.key?(:may_fire) && transition.eql?(options[:may_fire])) ||
145
145
  (!options.key?(:may_fire) && transition.allowed?(obj, *args))
@@ -158,6 +158,11 @@ module AASM::Core
158
158
  result
159
159
  end
160
160
 
161
+ def clear_failed_callbacks
162
+ # https://github.com/aasm/aasm/issues/383, https://github.com/aasm/aasm/issues/599
163
+ transitions.each { |transition| transition.failures.clear }
164
+ end
165
+
161
166
  def invoke_callbacks(code, record, args)
162
167
  Invoker.new(code, record, args)
163
168
  .with_default_return_value(false)
@@ -3,10 +3,11 @@ module AASM
3
3
  class UnknownStateMachineError < RuntimeError; end
4
4
 
5
5
  class InvalidTransition < RuntimeError
6
- attr_reader :object, :event_name, :originating_state, :failures
6
+ attr_reader :object, :event_name, :originating_state, :failures, :state_machine_name
7
7
 
8
8
  def initialize(object, event_name, state_machine_name, failures = [])
9
9
  @object, @event_name, @originating_state, @failures = object, event_name, object.aasm(state_machine_name).current_state, failures
10
+ @state_machine_name = state_machine_name
10
11
  super("Event '#{event_name}' cannot transition from '#{originating_state}'.#{reasoning}")
11
12
  end
12
13
 
@@ -91,7 +91,7 @@ module AASM
91
91
  when Proc
92
92
  state.call(@instance)
93
93
  else
94
- raise NotImplementedError, "Unrecognized state-type given. Expected Symbol, String, or Proc."
94
+ raise NotImplementedError, "Unrecognized state-type given. Expected Symbol, String, or Proc."
95
95
  end
96
96
  end
97
97
 
@@ -104,11 +104,12 @@ module AASM
104
104
  end
105
105
 
106
106
  def fire(event_name, *args, &block)
107
- @instance.send(:aasm_fire_event, @name, event_name, {persist: false}, *args, &block)
107
+ @instance.send(event_name, *args, &block)
108
108
  end
109
109
 
110
110
  def fire!(event_name, *args, &block)
111
- @instance.send(:aasm_fire_event, @name, event_name, {persist: true}, *args, &block)
111
+ event_name = event_name.to_s.+("!").to_sym
112
+ @instance.send(event_name, *args, &block)
112
113
  end
113
114
 
114
115
  def set_current_state_with_persistence(state)
@@ -141,7 +141,8 @@ module AASM
141
141
 
142
142
  def aasm_column_is_blank?(state_machine_name)
143
143
  attribute_name = self.class.aasm(state_machine_name).attribute_name
144
- attribute_names.include?(attribute_name.to_s) && send(attribute_name).blank?
144
+ attribute_names.include?(attribute_name.to_s) &&
145
+ (send(attribute_name).respond_to?(:empty?) ? !!send(attribute_name).empty? : !send(attribute_name))
145
146
  end
146
147
 
147
148
  def aasm_validate_states
@@ -34,7 +34,7 @@ module AASM
34
34
  # This allows for nil aasm states - be sure to add validation to your model
35
35
  def aasm_read_state(name=:default)
36
36
  state = send(self.class.aasm(name).attribute_name)
37
- if state.blank?
37
+ if !state || state.empty?
38
38
  aasm_new_record? ? aasm(name).determine_state_name(self.class.aasm(name).initial_state) : nil
39
39
  else
40
40
  state.to_sym
@@ -77,7 +77,8 @@ module AASM
77
77
  #
78
78
  def aasm_ensure_initial_state
79
79
  AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
80
- send("#{self.class.aasm(state_machine_name).attribute_name}=", aasm(state_machine_name).enter_initial_state.to_s) if send(self.class.aasm(state_machine_name).attribute_name).blank?
80
+ next if !send(self.class.aasm(state_machine_name).attribute_name) || send(self.class.aasm(state_machine_name).attribute_name).empty?
81
+ send("#{self.class.aasm(state_machine_name).attribute_name}=", aasm(state_machine_name).enter_initial_state.to_s)
81
82
  end
82
83
  end
83
84
  end # InstanceMethods
@@ -83,7 +83,7 @@ module AASM
83
83
  #
84
84
  def aasm_ensure_initial_state
85
85
  AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
86
- aasm(state_machine_name).enter_initial_state if send(self.class.aasm(state_machine_name).attribute_name).blank?
86
+ aasm(state_machine_name).enter_initial_state if !send(self.class.aasm(state_machine_name).attribute_name) || send(self.class.aasm(state_machine_name).attribute_name).empty?
87
87
  end
88
88
  end
89
89
  end # InstanceMethods
@@ -106,7 +106,7 @@ module AASM
106
106
  # mongoid has_many relationship does not load child object attributes when
107
107
  # only ids are loaded, for example parent.child_ids will not load child object attributes.
108
108
  # This feature is introduced in mongoid > 4.
109
- if attribute_names.include?(attribute_name) && attributes[attribute_name].blank?
109
+ if attribute_names.include?(attribute_name) && !attributes[attribute_name] || attributes[attribute_name].empty?
110
110
  # attribute_missing? is defined in mongoid > 4
111
111
  return if Mongoid::VERSION.to_f >= 4 && attribute_missing?(attribute_name)
112
112
  send("#{self.class.aasm(state_machine_name).attribute_name}=", aasm(state_machine_name).enter_initial_state.to_s)
@@ -96,7 +96,7 @@ module AASM
96
96
  def aasm_ensure_initial_state
97
97
  AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |name|
98
98
  aasm_column = self.class.aasm(name).attribute_name
99
- aasm(name).enter_initial_state if read_attribute(aasm_column).blank?
99
+ aasm(name).enter_initial_state if !read_attribute(aasm_column) || read_attribute(aasm_column).empty?
100
100
  end
101
101
  end
102
102
  end # InstanceMethods
@@ -69,7 +69,7 @@ module AASM
69
69
  def aasm_ensure_initial_state
70
70
  AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |name|
71
71
  aasm_column = self.class.aasm(name).attribute_name
72
- aasm(name).enter_initial_state if send(aasm_column).value.blank?
72
+ aasm(name).enter_initial_state if !send(aasm_column).value || send(aasm_column).value.empty?
73
73
  end
74
74
  end
75
75
 
@@ -1,3 +1,3 @@
1
1
  module AASM
2
- VERSION = "5.0.1"
2
+ VERSION = "5.0.6"
3
3
  end
@@ -22,6 +22,12 @@ RUBY
22
22
 
23
23
  private
24
24
 
25
+ def column_exists?
26
+ table_name.singularize.humanize.constantize.column_names.include?(column_name.to_s)
27
+ rescue NameError
28
+ false
29
+ end
30
+
25
31
  def model_exists?
26
32
  File.exists?(File.join(destination_root, model_path))
27
33
  end
@@ -11,7 +11,9 @@ module ActiveRecord
11
11
  source_root File.expand_path("../templates", __FILE__)
12
12
 
13
13
  def copy_aasm_migration
14
- if model_exists?
14
+ if column_exists?
15
+ puts "Both model and column exists"
16
+ elsif model_exists?
15
17
  migration_template "migration_existing.rb", "db/migrate/add_#{column_name}_to_#{table_name}.rb"
16
18
  else
17
19
  migration_template "migration.rb", "db/migrate/aasm_create_#{table_name}.rb"
@@ -14,12 +14,19 @@ ActiveRecord::Migration.suppress_messages do
14
14
  ActiveRecord::Migration.create_table "implemented_abstract_class_dsls", :force => true do |t|
15
15
  t.string "status"
16
16
  end
17
+ ActiveRecord::Migration.create_table "users", :force => true do |t|
18
+ t.string "status"
19
+ end
17
20
 
18
21
  ActiveRecord::Migration.create_table "complex_active_record_examples", :force => true do |t|
19
22
  t.string "left"
20
23
  t.string "right"
21
24
  end
22
25
 
26
+ ActiveRecord::Migration.create_table "works", :force => true do |t|
27
+ t.string "status"
28
+ end
29
+
23
30
  %w(validators multiple_validators workers invalid_persistors multiple_invalid_persistors silent_persistors multiple_silent_persistors).each do |table_name|
24
31
  ActiveRecord::Migration.create_table table_name, :force => true do |t|
25
32
  t.string "name"
@@ -44,4 +51,9 @@ ActiveRecord::Migration.suppress_messages do
44
51
  t.string "search"
45
52
  t.string "sync"
46
53
  end
54
+
55
+ ActiveRecord::Migration.create_table "instance_level_skip_validation_examples", :force => true do |t|
56
+ t.string "state"
57
+ t.string "some_string"
58
+ end
47
59
  end