freeform 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
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