freeform 1.0.4 → 1.0.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.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NTJlZjkyNzA0NTJlNzRhZjkyMjFjOGJmMjQyZjBkNDE5MmRiYzNjNA==
4
+ NmE5OGYzZDc5Mzg0NjRiZmY2ZDQ5MzIyMzFkNmIxNmU4MmY0M2NiMw==
5
5
  data.tar.gz: !binary |-
6
- NmZjYjBkNGU2NjBhOGVlMzQ5ZTM2YTcwMDAyMTdjYzViZDFjYjNhOQ==
6
+ ZjBjODM1YzBkNTI4NjY1MGU5YWVlOTRhY2ZlYWM3YWVhNjhlZTIxNA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ZDMwZWJiYjljYzU5NWU3M2FkYzIxOTdkZGRlYzg2YzM4ODVmN2IzNzM2NjUy
10
- YTdmODEzMDg3ODc5OGJiZmUzOTAxOTllNzBkNzI1OGZlNmVjZjBlNjY3NWM3
11
- MGYxZDZmMGIwNjAzYWQyMDRlYjAzNDA2Yjg5Y2VkYzY4NTJiMDk=
9
+ ZDFmMDcwMjc3YjJjOGI0OWY1M2Q1ZjAyOTE2NWM1MGVhNzczZjhmZmM0ZGZj
10
+ NTEyNzBhZGRiYjEwYWQwNmEyNGQ2MTUxZDE0YWEyOTUzYzE5YTBhM2E4Mjkz
11
+ ODMyZDQxYzljODQ4YzAzN2Y2OWFjYTQzY2IzNDY0MTUwYWZiMTU=
12
12
  data.tar.gz: !binary |-
13
- ZDQzZDlkMjY1ZTYyMzZmODcyOTVkNGViNDFiZmJjMTcwZGQ0ZjIzYmU1MmVl
14
- OTlkZDA4MTY2ZTEwNjdkMmYzYzU2N2I4OTNhZjdhMzU0MzM2ZDU4Yzc0ODRj
15
- ZjUwYjYxNDc3YWZjMDJiYWUyZDkwY2VjMjMxODYwNGMzMzQ2ZWE=
13
+ OGYwZjZjNTZkZTAxNDg2YTg2OTRjNjUzNjk0NDE5Y2JmMWQ4Mzk5ZDI0MDFm
14
+ NGQwMWM3Y2I5MGQ4OThiODUyYzE4OGIyNDllMGQ4NDdjMTI0ODM1ODQ1OTU5
15
+ ZDFhNGY4YzM4MjgxMjViN2EwZTQxMzk3NzRlYTk3NzBjYzU0ZWM=
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- freeform (1.0.3)
4
+ freeform (1.0.4)
5
5
  activemodel
6
6
  formtastic
7
7
  nested_form (~> 0.3.2)
@@ -24,7 +24,6 @@ module FreeForm
24
24
  raise ArgumentError, "Invalid association. Make sure that a nested form for #{association.to_s} exists."
25
25
  end
26
26
 
27
- #FIXME: I need to use the build method for child models
28
27
  model_object = options.delete(:model_object) do
29
28
  object.send("build_#{association.to_s.singularize}")
30
29
  end
@@ -68,10 +67,16 @@ module FreeForm
68
67
 
69
68
  args << (options.delete(:href) || "javascript:void(0)")
70
69
  args << options
71
- hidden_field(:_destroy, :value => 0) << @template.link_to(*args, &block)
70
+
71
+ destroy = if self.object.send(:marked_for_destruction?)
72
+ "1"
73
+ else
74
+ "0"
75
+ end
76
+ hidden_field(:_destroy, :value => destroy) << @template.link_to(*args, &block)
72
77
  end
78
+
73
79
  def fields_for_with_nested_attributes(association_name, *args)
74
- # TODO Test this better
75
80
  block = args.pop || Proc.new { |fields| @template.render(:partial => "#{association_name.to_s.singularize}_fields", :locals => {:f => fields}) }
76
81
 
77
82
  options = args.dup.extract_options!
@@ -3,7 +3,7 @@ module FreeForm
3
3
  def self.included(base)
4
4
  base.extend(ClassMethods)
5
5
  end
6
-
6
+
7
7
  module ClassMethods
8
8
  # Nested Forms
9
9
  #------------------------------------------------------------------------
@@ -32,21 +32,21 @@ module FreeForm
32
32
  def define_nested_model_methods(attribute, form_class, options={})
33
33
  singularized_attribute = attribute.to_s.singularize.to_s
34
34
 
35
- # Example: form.addresses will return all nested address forms
35
+ # Example: form.addresses will return all nested address forms
36
36
  define_method(:"#{attribute}") do
37
37
  value = instance_variable_get("@nested_#{attribute}")
38
38
  value ||= []
39
39
  instance_variable_set("@nested_#{attribute}", value)
40
40
  end
41
-
41
+
42
42
  # Example: form.build_addresses (optional custom initializer)
43
43
  define_method(:"build_#{attribute}") do |initializer=nil|
44
44
  # Builder object
45
45
  parent_object = self
46
-
46
+
47
47
  # Get correct class
48
48
  form_class = self.class.nested_forms[:"#{attribute}"]
49
-
49
+
50
50
  # Default Initializer
51
51
  if options[:default_initializer]
52
52
  initializer ||= instance_eval(&options[:default_initializer])
@@ -84,11 +84,11 @@ module FreeForm
84
84
 
85
85
  # Instance Methods
86
86
  #--------------------------------------------------------------------------
87
- protected
87
+ protected
88
88
  def build_models_from_param_count(attribute, params)
89
89
  # Get the difference between sets of params passed and current form objects
90
90
  num_param_models = params.length
91
- num_built_models = self.send(:"#{attribute}").nil? ? 0 : self.send(:"#{attribute}").length
91
+ num_built_models = self.send(:"#{attribute}").nil? ? 0 : self.send(:"#{attribute}").length
92
92
  additional_models_needed = num_param_models - num_built_models
93
93
 
94
94
  # Make up the difference by building new nested form models
@@ -97,4 +97,4 @@ module FreeForm
97
97
  end
98
98
  end
99
99
  end
100
- end
100
+ end
@@ -19,11 +19,11 @@ module FreeForm
19
19
 
20
20
  def declared_model(name, opts={})
21
21
  @models ||= []
22
- @models << name
22
+ @models << name
23
23
  attr_accessor name
24
24
  end
25
25
  alias_method :form_model, :declared_model
26
-
26
+
27
27
  def declared_models(*names)
28
28
  names.each do |name|
29
29
  declared_model(name)
@@ -33,9 +33,9 @@ module FreeForm
33
33
 
34
34
  def child_model(name, opts={}, &block)
35
35
  @models ||= []
36
- @models << name
36
+ @models << name
37
37
  @child_models ||= []
38
- @child_models << name
38
+ @child_models << name
39
39
  attr_accessor name
40
40
  define_method("initialize_#{name}") do
41
41
  instance_variable_set("@#{name}", instance_eval(&block))
@@ -53,8 +53,14 @@ module FreeForm
53
53
  # Define _destroy method for marked-for-destruction handling
54
54
  attr_accessor :_destroy
55
55
  define_method(:_destroy=) do |value|
56
- false_values = [nil, 0 , false, "0", "false"]
57
- @_destroy = !(false_values.include?(value))
56
+ false_values = [nil, 0 , false, "0", "false"]
57
+ true_values = [1 , true, "1", "true"]
58
+
59
+ #TODO: Test this!!
60
+ @_destroy = !false_values.include?(value) # Mark initially
61
+ # if (@_destroy == false) # Can only switch if false
62
+ # @_destroy = true if true_values.include?(value)
63
+ # end
58
64
  end
59
65
  alias_method :marked_for_destruction?, :_destroy
60
66
  define_method(:mark_for_destruction) do
@@ -108,7 +114,7 @@ module FreeForm
108
114
  def params_to_date(year, month, day)
109
115
  day ||= 1 # FIXME: is that really what we want? test.
110
116
  begin # TODO: test fails.
111
- return Date.new(year.to_i, month.to_i, day.to_i)
117
+ return Date.new(year.to_i, month.to_i, day.to_i)
112
118
  rescue ArgumentError => e
113
119
  return nil
114
120
  end
@@ -128,7 +134,7 @@ module FreeForm
128
134
 
129
135
  private
130
136
  def assign_attribute(attribute, value)
131
- self.send :"#{attribute}=", value unless ignore?(attribute, value)
137
+ self.send :"#{attribute}=", value unless ignore?(attribute, value)
132
138
  end
133
139
 
134
140
  def ignore?(attribute, value)
@@ -136,4 +142,4 @@ module FreeForm
136
142
  return (mapping[:ignore_blank] && value.blank?) unless mapping.nil?
137
143
  end
138
144
  end
139
- end
145
+ end
data/lib/freeform/form.rb CHANGED
@@ -26,44 +26,94 @@ module FreeForm
26
26
 
27
27
  def initialize(h={})
28
28
  h.each {|k,v| send("#{k}=",v)}
29
- initialize_child_models
30
- end
31
-
29
+ initialize_child_models
30
+ end
31
+
32
+ # def save
33
+ # return false unless valid?
34
+
35
+ # self.class.models.each do |form_model|
36
+ # model = send(form_model)
37
+ # model.is_a?(Array) ? model.each { |m| m.save } : save_or_destroy(model)
38
+ # end
39
+ # end
40
+
41
+ # def save!
42
+ # raise FreeForm::FormInvalid, "form invalid." unless valid?
43
+
44
+ # self.class.models.each do |form_model|
45
+ # model = send(form_model)
46
+ # model.is_a?(Array) ? model.each { |m| m.save! } : save_or_destroy!(model)
47
+ # end
48
+ # end
49
+
50
+
32
51
  def save
33
- return false unless valid?
52
+ return false unless valid? || marked_for_destruction?
34
53
 
54
+ # Loop through nested models first, sending them the save call
35
55
  self.class.models.each do |form_model|
36
56
  model = send(form_model)
37
- model.is_a?(Array) ? model.each { |m| m.save } : save_or_destroy(model)
57
+ # This is for nested models.
58
+ if model.is_a?(Array)
59
+ model.each { |m| m.save }
60
+ end
61
+ end
62
+
63
+ # puts "Saving... #{self}"
64
+
65
+ # After nested models are handled, save or destroy myself.
66
+ self.class.models.each do |form_model|
67
+ model = send(form_model)
68
+ # This is for form models.
69
+ unless model.is_a?(Array)
70
+ # puts "Saving model...#{model}"
71
+ if model.is_a?(Project)
72
+ # puts "All Tasks = #{Task.all}"
73
+ # puts "Tasks = #{model.tasks.inspect}"
74
+ end
75
+ if marked_for_destruction?
76
+ # puts "destroying model...#{model}"
77
+ model.destroy
78
+ else
79
+ model.save
80
+ end
81
+ end
38
82
  end
39
83
  end
40
-
84
+
41
85
  def save!
42
86
  raise FreeForm::FormInvalid, "form invalid." unless valid?
43
87
 
88
+ # Destroy all marked-for-destruction models
89
+
44
90
  self.class.models.each do |form_model|
45
91
  model = send(form_model)
46
92
  model.is_a?(Array) ? model.each { |m| m.save! } : save_or_destroy!(model)
47
93
  end
48
- end
49
-
94
+ end
95
+
50
96
  private
51
97
  def initialize_child_models
52
98
  self.class.child_models.each do |c|
53
99
  send("initialize_#{c}")
54
- end
100
+ end
55
101
  end
56
102
 
57
103
  def save_or_destroy(model)
58
104
  marked_for_destruction? ? model.destroy : model.save
59
105
  end
60
-
106
+
61
107
  def save_or_destroy!(model)
62
- marked_for_destruction? ? model.destroy : model.save!
108
+ if marked_for_destruction?
109
+ model.destroy
110
+ else
111
+ model.save!
112
+ end
63
113
  end
64
114
 
65
115
  def marked_for_destruction?
66
116
  respond_to?(:_destroy) ? _destroy : false
67
117
  end
68
118
  end
69
- end
119
+ end
@@ -1,3 +1,3 @@
1
1
  module Freeform
2
- VERSION = "1.0.4"
2
+ VERSION = "1.0.5"
3
3
  end
@@ -36,7 +36,7 @@ describe FreeForm::Form do
36
36
  has_many :tasks, :class => Module::TaskForm, :default_initializer => :default_task_initializer
37
37
 
38
38
  def default_task_initializer
39
- { :task => Task.new(:project => project) }
39
+ { :task => project.tasks.build }
40
40
  end
41
41
  end
42
42
  # This wrapper just avoids CONST warnings
@@ -67,7 +67,7 @@ describe FreeForm::Form do
67
67
 
68
68
  it "initializes with Task with project parent" do
69
69
  task = form.tasks.first.task
70
- task.project.should eq(form.project)
70
+ task.should eq(form.project.tasks.first)
71
71
  end
72
72
  end
73
73
 
@@ -229,7 +229,7 @@ describe FreeForm::Form do
229
229
  end
230
230
  end
231
231
 
232
- context "with invalid form, and invalid marked for destruction nested model", :failing => true do
232
+ context "with invalid form, and invalid marked for destruction nested model" do
233
233
  let(:attributes) do {
234
234
  :company_name => "",
235
235
  :project_name => "rails app",
@@ -374,10 +374,111 @@ describe FreeForm::Form do
374
374
  end
375
375
 
376
376
  it "should raise error on 'save!'" do
377
- expect{ form.save!.should be_false }.to raise_error(FreeForm::FormInvalid)
377
+ expect{ form.save! }.to raise_error(FreeForm::FormInvalid)
378
378
  end
379
379
  end
380
380
 
381
+ context "with invalid attributes, and marked for destruction nested model", :failing => true do
382
+ let(:attributes) do {
383
+ :company_name => "dummycorp",
384
+ :project_name => "",
385
+ "due_date(1i)" => "2014",
386
+ "due_date(2i)" => "10",
387
+ "due_date(3i)" => "30",
388
+ :tasks_attributes => {
389
+ "0" => {
390
+ :name => "task_1",
391
+ "start_date(1i)" => "2012",
392
+ "start_date(2i)" => "1",
393
+ "start_date(3i)" => "2",
394
+ },
395
+ "3234322345" => {
396
+ :name => "task_2",
397
+ "end_date(1i)" => "2011",
398
+ "end_date(2i)" => "12",
399
+ "end_date(3i)" => "15",
400
+ :_destroy => "1"
401
+ }
402
+ } }
403
+ end
404
+
405
+ before(:each) do
406
+ form.fill(attributes)
407
+ end
408
+
409
+ it "should not be valid" do
410
+ form.should_not be_valid
411
+ end
412
+
413
+ describe "persisting destroy after failed save" do
414
+ before(:each) { form.save }
415
+
416
+ it "should have the second form still set to _destroy" do
417
+ form.tasks.second.should be_marked_for_destruction
418
+ end
419
+ end
420
+
421
+ # it "should have valid company and project after save", :failing_1 => true do
422
+ # form.save
423
+ # # form.tasks.first.task.destroy
424
+ # form.company.should be_valid
425
+ # form.project.valid?
426
+ # puts "Errors = #{form.project.errors.inspect}"
427
+ # form.project.should be_valid
428
+ # end
429
+
430
+ # it "should not raise error on 'save!'" do
431
+ # expect{ form.save! }.to_not raise_error
432
+ # end
433
+ end
434
+
435
+ context "with invalid, marked for destruction nested model" do
436
+ let(:attributes) do {
437
+ :company_name => "dummycorp",
438
+ :project_name => "railsapp",
439
+ "due_date(1i)" => "2014",
440
+ "due_date(2i)" => "10",
441
+ "due_date(3i)" => "30",
442
+ :tasks_attributes => {
443
+ "0" => {
444
+ :name => "task_1",
445
+ "start_date(1i)" => "2012",
446
+ "start_date(2i)" => "1",
447
+ "start_date(3i)" => "2",
448
+ },
449
+ "1" => {
450
+ :name => "task_2",
451
+ "end_date(1i)" => "2011",
452
+ "end_date(2i)" => "12",
453
+ "end_date(3i)" => "15",
454
+ :_destroy => "1"
455
+ }
456
+ } }
457
+ end
458
+
459
+ before(:each) do
460
+ form.fill(attributes)
461
+ end
462
+
463
+ it { is pending }
464
+ # it "should return true on 'save'" do
465
+ # form.save.should be_true
466
+ # end
467
+
468
+ # it "should have valid company and project after save", :failing_1 => true do
469
+ # form.save
470
+ # # form.tasks.first.task.destroy
471
+ # form.company.should be_valid
472
+ # form.project.valid?
473
+ # puts "Errors = #{form.project.errors.inspect}"
474
+ # form.project.should be_valid
475
+ # end
476
+
477
+ # it "should not raise error on 'save!'" do
478
+ # expect{ form.save! }.to_not raise_error
479
+ # end
480
+ end
481
+
381
482
  context "with valid attributes" do
382
483
  let(:attributes) do {
383
484
  :company_name => "dummycorp",
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Full Behavior', :js => true do
4
+ include Capybara::DSL
5
+
6
+ [:jquery].each do |js_framework|
7
+
8
+ url = case js_framework
9
+ when :jquery then '/projects/new'
10
+ end
11
+
12
+ context "with #{js_framework}" do
13
+ context 'adding/removing fields' do
14
+ it 'adds fields and increments count' do
15
+ visit url
16
+ click_link 'Add new task'
17
+ all("input[id^='project_tasks_attributes_']", visible: true).count.should eq(1)
18
+ click_link 'Add new task'
19
+ all("input[id^='project_tasks_attributes_']", visible: true).count.should eq(2)
20
+ click_link 'Add new task'
21
+ all("input[id^='project_tasks_attributes_']", visible: true).count.should eq(3)
22
+ end
23
+
24
+ it 'removes fields' do
25
+ visit url
26
+ click_link 'Add new task'
27
+ all("input[id^='project_tasks_attributes_']", visible: true).count.should eq(1)
28
+ click_link 'Remove'
29
+ all("input[id^='project_tasks_attributes_']", visible: true).count.should eq(0)
30
+ click_link 'Add new task'
31
+ all("input[id^='project_tasks_attributes_']", visible: true).count.should eq(1)
32
+ end
33
+ end
34
+
35
+ context 'after application submission' do
36
+ it 'emits general remove event' do
37
+ visit url
38
+ click_link 'Add new task'
39
+ all("input[id^='project_tasks_attributes_']", visible: true).count.should eq(1)
40
+ click_link 'Remove'
41
+ all("input[id^='project_tasks_attributes_']", visible: true).count.should eq(0)
42
+ click_button 'Submit'
43
+ page.should have_selector(:xpath, "//input[@id='project_tasks_attributes_0__destroy' and @value='1']", visible: false)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -3,9 +3,15 @@ class ProjectsController < ApplicationController
3
3
  @project = Project.new
4
4
  @form = project_form(@project)
5
5
  end
6
-
6
+
7
+ def create
8
+ @project = Project.new
9
+ @form = project_form(@project).fill(params[:project])
10
+ render :action => :new
11
+ end
12
+
7
13
  private
8
14
  def project_form(project)
9
15
  ProjectForm.new(:project => project)
10
- end
16
+ end
11
17
  end
@@ -1,10 +1,11 @@
1
1
  class TaskForm < FreeForm::Form
2
2
  form_models :task
3
- property :name, :on => :task
3
+ property :name, :on => :task
4
+ allow_destroy_on_save
4
5
 
5
6
  has_many :milestones, :class => MilestoneForm, :default_initializer => :milestone_initializer
6
7
 
7
8
  def milestone_initializer
8
9
  { :milestone => Milestone.new }
9
10
  end
10
- end
11
+ end
@@ -1,4 +1,4 @@
1
- <%= freeform_for @form do |f| -%>
1
+ <%= freeform_for @form, :url => projects_path, :method => :post do |f| -%>
2
2
  <%= f.text_field :name %>
3
3
  <%= f.fields_for :tasks do |tf| -%>
4
4
  <%= tf.text_field :name %>
@@ -10,4 +10,5 @@
10
10
  <%= tf.link_to_remove 'Remove' %>
11
11
  <% end -%>
12
12
  <%= f.link_to_add 'Add new task', :tasks %>
13
+ <%= f.submit "Submit" %>
13
14
  <% end -%>
Binary file