object_attorney 1.0.2 → 1.1.1
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 +8 -8
- data/.gitignore +3 -2
- data/.rspec +3 -0
- data/.rvmrc +1 -0
- data/Gemfile +12 -0
- data/Guardfile +5 -0
- data/Rakefile +18 -0
- data/db/migrate/20131205114900_create_posts.rb +14 -0
- data/db/schema.rb +22 -0
- data/lib/object_attorney/nested_objects.rb +14 -6
- data/lib/object_attorney/orm.rb +21 -7
- data/lib/object_attorney/orm_handlers/smooth_operator.rb +6 -6
- data/lib/object_attorney/version.rb +1 -1
- data/lib/object_attorney.rb +24 -31
- data/object_attorney.gemspec +2 -2
- data/spec/object_attorney/bulk_posts_form_child_spec.rb +191 -0
- data/spec/object_attorney/bulk_posts_form_spec.rb +178 -0
- data/spec/object_attorney/post_form_child_spec.rb +60 -0
- data/spec/object_attorney/post_form_spec.rb +60 -0
- data/spec/spec_helper.rb +47 -0
- data/spec/support/active_model/validations.rb +21 -0
- data/spec/support/database_setup.rb +14 -0
- data/spec/support/models/bulk_posts_form.rb +21 -0
- data/spec/support/models/bulk_posts_form_child.rb +7 -0
- data/spec/support/models/post.rb +3 -0
- data/spec/support/models/post_form.rb +13 -0
- data/spec/support/models/post_form_child.rb +7 -0
- metadata +36 -7
checksums.yaml
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
!binary "U0hBMQ==":
|
|
3
3
|
metadata.gz: !binary |-
|
|
4
|
-
|
|
4
|
+
YzdjZjUwYTk2MGM5NDM2Nzg3MTE4YmY3ODVhZDVlODQ4MmRiMmRjYg==
|
|
5
5
|
data.tar.gz: !binary |-
|
|
6
|
-
|
|
6
|
+
NjQ0YzRjYjg3YTlkMzgyYTU5Njk4MTNmMTkwNDU2NWRiNWMxNmE2NQ==
|
|
7
7
|
SHA512:
|
|
8
8
|
metadata.gz: !binary |-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
NTYwMTk5MGNjNzI4NTQwMWM4ZGFlOTBkY2JkMDYwZTdmZmQwMjk0ZGVjMDZl
|
|
10
|
+
NjIwZmUxNWY3ZThlNTViNTg5YjRiZjMwYTRiZDAyYTZmMGM5ZjJhYzg4ZGM5
|
|
11
|
+
YThhODIzOTFjMzAzMmE4YjM3MDgyMzhkNTlkOGRlYjlkODkwOWQ=
|
|
12
12
|
data.tar.gz: !binary |-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
MzQzNGRkZDIwMjYwOGVlMjFkMjkzYTA4MGM2OTQ5ZDk1OTdiNmQxZjE3MGM0
|
|
14
|
+
MTdmMGJmOWIzZGI2MzAwYWJmZTZhOGQ2YTM0MzczYmNlZWYzYzZlODUxYmU0
|
|
15
|
+
YjU4ODQ1MmYwZDkxMzk4MDBiNGViMWFhZGQyNWY2YWM1OTAyODQ=
|
data/.gitignore
CHANGED
data/.rspec
ADDED
data/.rvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
rvm 1.9.3@object_attorney --create
|
data/Gemfile
CHANGED
|
@@ -2,3 +2,15 @@ source 'https://rubygems.org'
|
|
|
2
2
|
|
|
3
3
|
# Specify your gem's dependencies in object_attorney.gemspec
|
|
4
4
|
gemspec
|
|
5
|
+
|
|
6
|
+
group :development, :test do
|
|
7
|
+
gem "rspec", "~> 2.11"
|
|
8
|
+
gem "sqlite3"
|
|
9
|
+
gem "activerecord"
|
|
10
|
+
#gem 'database_cleaner'
|
|
11
|
+
gem "pry"
|
|
12
|
+
|
|
13
|
+
unless ENV["CI"]
|
|
14
|
+
gem "guard-rspec", "~> 0.7"
|
|
15
|
+
end
|
|
16
|
+
end
|
data/Guardfile
ADDED
data/Rakefile
CHANGED
|
@@ -1 +1,19 @@
|
|
|
1
1
|
require "bundler/gem_tasks"
|
|
2
|
+
|
|
3
|
+
# see: http://blog.aizatto.com/2007/05/27/activerecord-migrations-without-rails/
|
|
4
|
+
# require 'active_record'
|
|
5
|
+
# require 'yaml'
|
|
6
|
+
|
|
7
|
+
# ENV["RAILS_ENV"] ||= 'test'
|
|
8
|
+
|
|
9
|
+
# task :default => :migrate
|
|
10
|
+
|
|
11
|
+
# desc "Migrate the database through scripts in db/migrate. Target specific version with VERSION=x"
|
|
12
|
+
# task :migrate => :environment do
|
|
13
|
+
# ActiveRecord::Migrator.migrate('db/migrate', ENV["VERSION"] ? ENV["VERSION"].to_i : nil )
|
|
14
|
+
# end
|
|
15
|
+
|
|
16
|
+
# task :environment do
|
|
17
|
+
# ActiveRecord::Base.establish_connection(YAML::load(File.open('config/database.yml'))[ENV["RAILS_ENV"]])
|
|
18
|
+
# ActiveRecord::Base.logger = Logger.new(File.open('tmp/database.log', 'a'))
|
|
19
|
+
# end
|
data/db/schema.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# This file is auto-generated from the current state of the database. Instead
|
|
2
|
+
# of editing this file, please use the migrations feature of Active Record to
|
|
3
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
|
4
|
+
#
|
|
5
|
+
# Note that this schema.rb definition is the authoritative source for your
|
|
6
|
+
# database schema. If you need to create the application database on another
|
|
7
|
+
# system, you should be using db:schema:load, not running all the migrations
|
|
8
|
+
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
|
9
|
+
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
|
10
|
+
#
|
|
11
|
+
# It's strongly recommended that you check this file into your version control system.
|
|
12
|
+
|
|
13
|
+
ActiveRecord::Schema.define(version: 20131020175047) do
|
|
14
|
+
|
|
15
|
+
create_table "posts", force: true do |t|
|
|
16
|
+
t.string "title"
|
|
17
|
+
t.text "body"
|
|
18
|
+
t.datetime "created_at"
|
|
19
|
+
t.datetime "updated_at"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
@@ -2,7 +2,7 @@ module ObjectAttorney
|
|
|
2
2
|
module NestedObjects
|
|
3
3
|
|
|
4
4
|
def nested_objects
|
|
5
|
-
|
|
5
|
+
self.class.nested_objects.map { |nested_object_sym| self.send(nested_object_sym) }.flatten
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
def mark_for_destruction
|
|
@@ -18,11 +18,17 @@ module ObjectAttorney
|
|
|
18
18
|
|
|
19
19
|
_destroy = attributes["_destroy"] || attributes[:_destroy]
|
|
20
20
|
|
|
21
|
-
object.mark_for_destruction if ["true", "1"].include?(_destroy)
|
|
21
|
+
object.mark_for_destruction if ["true", "1", true].include?(_destroy)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
protected #################### PROTECTED METHODS DOWN BELOW ######################
|
|
25
25
|
|
|
26
|
+
def save_nested_objects(save_method)
|
|
27
|
+
nested_objects.map do |nested_object|
|
|
28
|
+
call_save_or_destroy(nested_object, save_method)
|
|
29
|
+
end.all?
|
|
30
|
+
end
|
|
31
|
+
|
|
26
32
|
def validate_nested_objects
|
|
27
33
|
#nested_objects.all?(&:valid?) #will not validate all nested_objects
|
|
28
34
|
return true if nested_objects.reject(&:marked_for_destruction?).map(&:valid?).all?
|
|
@@ -31,7 +37,7 @@ module ObjectAttorney
|
|
|
31
37
|
end
|
|
32
38
|
|
|
33
39
|
def import_nested_objects_errors
|
|
34
|
-
|
|
40
|
+
self.class.nested_objects.map do |nested_object_sym|
|
|
35
41
|
|
|
36
42
|
[*self.send(nested_object_sym)].each do |nested_object|
|
|
37
43
|
nested_object.errors.full_messages.each { |message| self.errors.add(nested_object_sym, message) }
|
|
@@ -54,8 +60,6 @@ module ObjectAttorney
|
|
|
54
60
|
base.class_eval do
|
|
55
61
|
validate :validate_nested_objects
|
|
56
62
|
end
|
|
57
|
-
|
|
58
|
-
@@nested_objects = []
|
|
59
63
|
end
|
|
60
64
|
|
|
61
65
|
def attributes_without_destroy(attributes)
|
|
@@ -101,7 +105,7 @@ module ObjectAttorney
|
|
|
101
105
|
|
|
102
106
|
def build_new_nested_objects(existing_and_new_nested_objects, nested_object_name)
|
|
103
107
|
(send("#{nested_object_name}_attributes") || {}).values.each do |attributes|
|
|
104
|
-
next if attributes["id"].present?
|
|
108
|
+
next if attributes["id"].present? || attributes[:id].present?
|
|
105
109
|
|
|
106
110
|
new_nested_object = send("build_#{nested_object_name.to_s.singularize}", attributes_without_destroy(attributes))
|
|
107
111
|
mark_for_destruction_if_necessary(new_nested_object, attributes)
|
|
@@ -122,6 +126,10 @@ module ObjectAttorney
|
|
|
122
126
|
nil
|
|
123
127
|
end
|
|
124
128
|
|
|
129
|
+
def nested_objects
|
|
130
|
+
self.instance_variable_get("@nested_objects") || zuper_method('nested_objects') || []
|
|
131
|
+
end
|
|
132
|
+
|
|
125
133
|
private #################### PRIVATE METHODS DOWN BELOW ######################
|
|
126
134
|
|
|
127
135
|
def define_nested_objects_getter_methods(nested_objects_list)
|
data/lib/object_attorney/orm.rb
CHANGED
|
@@ -2,13 +2,17 @@ require "object_attorney/orm_handlers/smooth_operator"
|
|
|
2
2
|
|
|
3
3
|
module ObjectAttorney
|
|
4
4
|
module ORM
|
|
5
|
+
|
|
6
|
+
def id
|
|
7
|
+
represented_object.try(:id)
|
|
8
|
+
end
|
|
5
9
|
|
|
6
10
|
def new_record?
|
|
7
|
-
try_or_return(
|
|
11
|
+
try_or_return(represented_object, :new_record?, true)
|
|
8
12
|
end
|
|
9
13
|
|
|
10
14
|
def persisted?
|
|
11
|
-
try_or_return(
|
|
15
|
+
try_or_return(represented_object, :persisted?, false)
|
|
12
16
|
end
|
|
13
17
|
|
|
14
18
|
def save
|
|
@@ -23,13 +27,13 @@ module ObjectAttorney
|
|
|
23
27
|
end
|
|
24
28
|
|
|
25
29
|
def destroy
|
|
26
|
-
return true if
|
|
27
|
-
evoke_method_on_object(
|
|
30
|
+
return true if represented_object.blank?
|
|
31
|
+
evoke_method_on_object(represented_object, :destroy)
|
|
28
32
|
end
|
|
29
33
|
|
|
30
34
|
def call_save_or_destroy(object, save_method)
|
|
31
35
|
if object == self
|
|
32
|
-
|
|
36
|
+
represented_object.present? ? evoke_method_on_object(represented_object, save_method) : true
|
|
33
37
|
else
|
|
34
38
|
save_method = :destroy if check_if_marked_for_destruction?(object)
|
|
35
39
|
evoke_method_on_object(object, save_method)
|
|
@@ -42,8 +46,18 @@ module ObjectAttorney
|
|
|
42
46
|
def after_save; end
|
|
43
47
|
|
|
44
48
|
def save_after_validations(save_method)
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
submit(save_method)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def submit(save_method)
|
|
53
|
+
save_result = save_represented_object(save_method)
|
|
54
|
+
save_result = save_nested_objects(save_method) if save_result
|
|
55
|
+
save_result
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def save_represented_object(save_method)
|
|
59
|
+
return true if represented_object.blank?
|
|
60
|
+
call_save_or_destroy(represented_object, save_method)
|
|
47
61
|
end
|
|
48
62
|
|
|
49
63
|
private #################### PRIVATE METHODS DOWN BELOW ######################
|
|
@@ -15,13 +15,13 @@ module ObjectAttorney
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def destroy(options = {})
|
|
18
|
-
return true if
|
|
19
|
-
evoke_method_on_object(
|
|
18
|
+
return true if represented_object.blank?
|
|
19
|
+
evoke_method_on_object(represented_object, :destroy, options)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def call_save_or_destroy(object, save_method, options = {})
|
|
23
|
-
if object == self || object ==
|
|
24
|
-
|
|
23
|
+
if object == self || object == represented_object
|
|
24
|
+
represented_object.present? ? evoke_method_on_object(represented_object, save_method, options) : true
|
|
25
25
|
else
|
|
26
26
|
save_method = :destroy if check_if_marked_for_destruction?(object)
|
|
27
27
|
evoke_method_on_object(object, save_method, options)
|
|
@@ -31,8 +31,8 @@ module ObjectAttorney
|
|
|
31
31
|
protected #################### PROTECTED METHODS DOWN BELOW ######################
|
|
32
32
|
|
|
33
33
|
def save_after_validations(save_method, options = {})
|
|
34
|
-
return true if
|
|
35
|
-
evoke_method_on_object(
|
|
34
|
+
return true if represented_object.blank?
|
|
35
|
+
evoke_method_on_object(represented_object, save_method, options).ok?
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
private #################### PRIVATE METHODS DOWN BELOW ######################
|
data/lib/object_attorney.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require "object_attorney/version"
|
|
2
2
|
require "object_attorney/nested_objects"
|
|
3
3
|
require "object_attorney/orm"
|
|
4
|
+
require 'active_record'
|
|
4
5
|
|
|
5
6
|
module ObjectAttorney
|
|
6
7
|
|
|
@@ -39,21 +40,21 @@ module ObjectAttorney
|
|
|
39
40
|
|
|
40
41
|
def allowed_attribute(attribute)
|
|
41
42
|
attribute = attribute.to_s
|
|
42
|
-
|
|
43
|
-
return false if !respond_to?("#{attribute}=") || self.class.black_list.include?(attribute)
|
|
44
|
-
return true if self.class.white_list.empty?
|
|
45
|
-
|
|
46
|
-
self.class.white_list.include?(attribute)
|
|
43
|
+
respond_to?("#{attribute}=")
|
|
47
44
|
end
|
|
48
45
|
|
|
49
46
|
def validate_represented_object
|
|
50
|
-
valid = override_validations? ? true : try_or_return(
|
|
47
|
+
valid = override_validations? ? true : try_or_return(represented_object, :valid?, true)
|
|
51
48
|
import_represented_object_errors unless valid
|
|
52
49
|
valid
|
|
53
50
|
end
|
|
54
51
|
|
|
55
52
|
def import_represented_object_errors
|
|
56
|
-
|
|
53
|
+
represented_object.errors.each { |key, value| self.errors.add(key, value) }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def represented_object
|
|
57
|
+
@represented_object ||= self.class.represented_object_class.try(:new)
|
|
57
58
|
end
|
|
58
59
|
|
|
59
60
|
private #################### PRIVATE METHODS DOWN BELOW ######################
|
|
@@ -87,36 +88,28 @@ module ObjectAttorney
|
|
|
87
88
|
|
|
88
89
|
module ClassMethods
|
|
89
90
|
|
|
90
|
-
def represents(
|
|
91
|
-
|
|
91
|
+
def represents(represented_object_name, represented_object_class = nil)
|
|
92
|
+
self.instance_variable_set("@represented_object_class", represented_object_class || represented_object_name.to_s.camelize.constantize)
|
|
92
93
|
|
|
93
|
-
define_method(
|
|
94
|
-
|
|
94
|
+
define_method(represented_object_name) do
|
|
95
|
+
represented_object
|
|
95
96
|
end
|
|
96
97
|
end
|
|
97
98
|
|
|
98
|
-
def
|
|
99
|
-
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def delegate_propertiy(property, options)
|
|
103
|
-
delegate property, "#{property}=", options
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def attr_white_list=(*white_list)
|
|
107
|
-
@@white_list = white_list.map(&:to_s)
|
|
99
|
+
def represented_object_class
|
|
100
|
+
self.instance_variable_get("@represented_object_class") || zuper_method('represented_object_class')
|
|
108
101
|
end
|
|
109
102
|
|
|
110
|
-
def
|
|
111
|
-
|
|
103
|
+
def zuper_method(method_name, *args)
|
|
104
|
+
self.superclass.send(method_name, *args) if self.superclass.respond_to?(method_name)
|
|
112
105
|
end
|
|
113
106
|
|
|
114
|
-
def
|
|
115
|
-
|
|
107
|
+
def delegate_properties(*properties, options)
|
|
108
|
+
properties.each { |property| delegate_property(property, options) }
|
|
116
109
|
end
|
|
117
110
|
|
|
118
|
-
def
|
|
119
|
-
|
|
111
|
+
def delegate_property(property, options)
|
|
112
|
+
delegate property, "#{property}=", options
|
|
120
113
|
end
|
|
121
114
|
|
|
122
115
|
def human_attribute_name(attribute_key_name, options = {})
|
|
@@ -130,19 +123,19 @@ module ObjectAttorney
|
|
|
130
123
|
|
|
131
124
|
translation = I18n.translate(defaults.shift, options.merge(default: defaults))
|
|
132
125
|
|
|
133
|
-
if translation == no_translation &&
|
|
134
|
-
translation =
|
|
126
|
+
if translation == no_translation && represented_object_class.respond_to?(:human_attribute_name)
|
|
127
|
+
translation = represented_object_class.human_attribute_name(attribute_key_name, options)
|
|
135
128
|
end
|
|
136
129
|
|
|
137
130
|
translation
|
|
138
131
|
end
|
|
139
132
|
|
|
140
133
|
def model_name
|
|
141
|
-
|
|
134
|
+
@_model_name ||= begin
|
|
142
135
|
namespace = self.parents.detect do |n|
|
|
143
136
|
n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming?
|
|
144
137
|
end
|
|
145
|
-
ActiveModel::Name.new(
|
|
138
|
+
ActiveModel::Name.new(represented_object_class || self, namespace)
|
|
146
139
|
end
|
|
147
140
|
end
|
|
148
141
|
|
data/object_attorney.gemspec
CHANGED
|
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
|
|
|
8
8
|
spec.version = ObjectAttorney::VERSION
|
|
9
9
|
spec.authors = ["João Gonçalves"]
|
|
10
10
|
spec.email = ["goncalves.joao@gmail.com"]
|
|
11
|
-
spec.description = %q{Form Object
|
|
12
|
-
spec.summary = %q{This gem allows you to extract the code responsible for
|
|
11
|
+
spec.description = %q{Form Object pattern implementation for Rails}
|
|
12
|
+
spec.summary = %q{This gem allows you to extract the code responsible for 'validations', 'nested objects' and 'strong parameters' from your model onto a specific class for a specific use case.}
|
|
13
13
|
spec.homepage = "https://github.com/goncalvesjoao/object_attorney"
|
|
14
14
|
spec.license = "MIT"
|
|
15
15
|
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe BulkPostsFormChild do
|
|
4
|
+
|
|
5
|
+
it "Creating multiple Posts, with a tabless model 'BulkPostsFormChild' has if it had 'accepts_nested_attributes_for :posts'" do
|
|
6
|
+
params = {
|
|
7
|
+
bulk_post: {
|
|
8
|
+
admin: true,
|
|
9
|
+
posts_attributes: {
|
|
10
|
+
"0" => { state: "draft", title: "My title1" },
|
|
11
|
+
"1" => { state: "public", title: "My title2" }
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
bulk_posts_form_child = BulkPostsFormChild.new(params[:bulk_post])
|
|
17
|
+
bulk_posts_form_child.save
|
|
18
|
+
|
|
19
|
+
expect(Post.all.count).to(eq(2))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "Trying to create multiple Posts, with the same title (testing the 'validates_nested_uniqueness')" do
|
|
23
|
+
params = {
|
|
24
|
+
bulk_post: {
|
|
25
|
+
admin: true,
|
|
26
|
+
posts_attributes: {
|
|
27
|
+
"0" => { state: "draft", title: "My title1" },
|
|
28
|
+
"1" => { state: "public", title: "My title1" }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
bulk_posts_form = BulkPostsFormChild.new(params[:bulk_post])
|
|
34
|
+
bulk_posts_form.save
|
|
35
|
+
|
|
36
|
+
# TODO: Ensure that the nested objects remember their respective errors
|
|
37
|
+
# see: http://stackoverflow.com/questions/13879700/rails-model-valid-flusing-custom-errors-and-falsely-returning-true
|
|
38
|
+
|
|
39
|
+
expect(Post.all.count).to(eq(0))
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "Creating new Post and editing an existing one" do
|
|
43
|
+
params = {
|
|
44
|
+
bulk_post: {
|
|
45
|
+
admin: true,
|
|
46
|
+
posts_attributes: {
|
|
47
|
+
"0" => { id: 1, state: "draft", title: "Changed title" },
|
|
48
|
+
"1" => { state: "public", title: "My title2" }
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
existing_post = Post.create(title: "My title1")
|
|
54
|
+
BulkPostsFormChild.new(params[:bulk_post]).save
|
|
55
|
+
existing_post.reload
|
|
56
|
+
|
|
57
|
+
expect(Post.all.count).to(eq(2)) && expect(existing_post.title).to(eq('Changed title'))
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "Creating new Post and deleting an existing one" do
|
|
61
|
+
params = {
|
|
62
|
+
bulk_post: {
|
|
63
|
+
admin: true,
|
|
64
|
+
posts_attributes: {
|
|
65
|
+
"0" => { id: 1, state: "draft", title: "Changed title", _destroy: true },
|
|
66
|
+
"1" => { state: "public", title: "My title2" }
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
existing_post = Post.create(title: "My title1")
|
|
72
|
+
BulkPostsFormChild.new(params[:bulk_post]).save
|
|
73
|
+
|
|
74
|
+
expect(Post.all.count).to(eq(1)) && expect(Post.where(id: existing_post.id).present?).to(eq(false))
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "Trying to create multiple Posts, but one of them is invalid" do
|
|
78
|
+
params = {
|
|
79
|
+
bulk_post: {
|
|
80
|
+
admin: true,
|
|
81
|
+
posts_attributes: {
|
|
82
|
+
"0" => { title: "My title1" },
|
|
83
|
+
"1" => { state: "public", title: "My title2" }
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
BulkPostsFormChild.new(params[:bulk_post]).save
|
|
89
|
+
|
|
90
|
+
params = {
|
|
91
|
+
bulk_post: {
|
|
92
|
+
admin: true,
|
|
93
|
+
posts_attributes: {
|
|
94
|
+
"0" => { state: 'draft', title: "My title1" },
|
|
95
|
+
"1" => { state: "public" }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
BulkPostsFormChild.new(params[:bulk_post]).save
|
|
101
|
+
|
|
102
|
+
expect(Post.all.count).to(eq(0))
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "Trying to create new Post and editing an existing one, but one of them is invalid" do
|
|
106
|
+
params = {
|
|
107
|
+
bulk_post: {
|
|
108
|
+
admin: true,
|
|
109
|
+
posts_attributes: {
|
|
110
|
+
"0" => { id: 1, title: "Changed title" },
|
|
111
|
+
"1" => { state: "public", title: "My title2" }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
existing_post = Post.create(title: "My title1")
|
|
117
|
+
BulkPostsFormChild.new(params[:bulk_post]).save
|
|
118
|
+
existing_post.reload
|
|
119
|
+
|
|
120
|
+
expect(Post.all.count).to(eq(1)) && expect(existing_post.title).to(eq('My title1'))
|
|
121
|
+
|
|
122
|
+
params = {
|
|
123
|
+
bulk_post: {
|
|
124
|
+
admin: true,
|
|
125
|
+
posts_attributes: {
|
|
126
|
+
"0" => { id: 1, state: 'draft', title: "Changed title" },
|
|
127
|
+
"1" => { state: "public" }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
BulkPostsFormChild.new(params[:bulk_post]).save
|
|
133
|
+
existing_post.reload
|
|
134
|
+
|
|
135
|
+
expect(Post.all.count).to(eq(1)) && expect(existing_post.title).to(eq('My title1'))
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it "Trying to create new Post and deleting an existing one, but the new one is invalid" do
|
|
139
|
+
params = {
|
|
140
|
+
bulk_post: {
|
|
141
|
+
admin: true,
|
|
142
|
+
posts_attributes: {
|
|
143
|
+
"0" => { id: 1, state: "draft", title: "Changed title", _destroy: true },
|
|
144
|
+
"1" => { state: "public" }
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
existing_post = Post.create(title: "My title1")
|
|
150
|
+
BulkPostsFormChild.new(params[:bulk_post]).save
|
|
151
|
+
existing_post.reload
|
|
152
|
+
|
|
153
|
+
expect(Post.all.count).to(eq(1)) && expect(existing_post.title).to(eq('My title1'))
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it "Trying to create new Post and deleting an existing one, the existing one is invalid but since it is marked for destruction, it should be deleted" do
|
|
157
|
+
params = {
|
|
158
|
+
bulk_post: {
|
|
159
|
+
admin: true,
|
|
160
|
+
posts_attributes: {
|
|
161
|
+
"0" => { id: 1, title: "Changed title", _destroy: true },
|
|
162
|
+
"1" => { state: "public", title: "My title2" }
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
existing_post = Post.create(title: "My title1")
|
|
168
|
+
bulk_posts_form_child = BulkPostsFormChild.new(params[:bulk_post])
|
|
169
|
+
bulk_posts_form_child.save
|
|
170
|
+
|
|
171
|
+
expect(Post.all.count).to(eq(1)) && expect(Post.where(id: existing_post.id).present?).to(eq(false))
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it "Trying to create new Post and deleting an existing one, both of them are invalid, no changes should occur." do
|
|
175
|
+
params = {
|
|
176
|
+
bulk_post: {
|
|
177
|
+
admin: true,
|
|
178
|
+
posts_attributes: {
|
|
179
|
+
"0" => { id: 1, title: "Changed title", _destroy: true },
|
|
180
|
+
"1" => { state: "public" }
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
existing_post = Post.create(title: "My title1")
|
|
186
|
+
BulkPostsFormChild.new(params[:bulk_post]).save
|
|
187
|
+
|
|
188
|
+
expect(Post.all.count).to(eq(1)) && expect(existing_post.title).to(eq('My title1'))
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
end
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe BulkPostsForm do
|
|
4
|
+
|
|
5
|
+
it "Creating multiple Posts, with a tabless model 'BulkPostsForm' has if it had 'accepts_nested_attributes_for :posts'" do
|
|
6
|
+
params = {
|
|
7
|
+
bulk_post: {
|
|
8
|
+
posts_attributes: {
|
|
9
|
+
"0" => { state: "draft", title: "My title1" },
|
|
10
|
+
"1" => { state: "public", title: "My title2" }
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
BulkPostsForm.new(params[:bulk_post]).save
|
|
16
|
+
|
|
17
|
+
expect(Post.all.count).to(eq(2))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "Creating multiple Posts, with the same title (testing the 'validates_nested_uniqueness')" do
|
|
21
|
+
params = {
|
|
22
|
+
bulk_post: {
|
|
23
|
+
posts_attributes: {
|
|
24
|
+
"0" => { state: "draft", title: "My title1" },
|
|
25
|
+
"1" => { state: "public", title: "My title1" }
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
bulk_posts_form = BulkPostsForm.new(params[:bulk_post])
|
|
31
|
+
bulk_posts_form.save
|
|
32
|
+
|
|
33
|
+
# TODO: Ensure that the nested objects remember their respective errors
|
|
34
|
+
# see: http://stackoverflow.com/questions/13879700/rails-model-valid-flusing-custom-errors-and-falsely-returning-true
|
|
35
|
+
|
|
36
|
+
expect(Post.all.count).to(eq(0))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "Creating new Post and editing an existing one" do
|
|
40
|
+
params = {
|
|
41
|
+
bulk_post: {
|
|
42
|
+
posts_attributes: {
|
|
43
|
+
"0" => { id: 1, state: "draft", title: "Changed title" },
|
|
44
|
+
"1" => { state: "public", title: "My title2" }
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
existing_post = Post.create(title: "My title1")
|
|
50
|
+
BulkPostsForm.new(params[:bulk_post]).save
|
|
51
|
+
existing_post.reload
|
|
52
|
+
|
|
53
|
+
expect(Post.all.count).to(eq(2)) && expect(existing_post.title).to(eq('Changed title'))
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "Creating new Post and deleting an existing one" do
|
|
57
|
+
params = {
|
|
58
|
+
bulk_post: {
|
|
59
|
+
posts_attributes: {
|
|
60
|
+
"0" => { id: 1, state: "draft", title: "Changed title", _destroy: true },
|
|
61
|
+
"1" => { state: "public", title: "My title2" }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
existing_post = Post.create(title: "My title1")
|
|
67
|
+
BulkPostsForm.new(params[:bulk_post]).save
|
|
68
|
+
|
|
69
|
+
expect(Post.all.count).to(eq(1)) && expect(Post.where(id: existing_post.id).present?).to(eq(false))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "Trying to create multiple Posts, but one of them is invalid" do
|
|
73
|
+
params = {
|
|
74
|
+
bulk_post: {
|
|
75
|
+
posts_attributes: {
|
|
76
|
+
"0" => { title: "My title1" },
|
|
77
|
+
"1" => { state: "public", title: "My title2" }
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
BulkPostsForm.new(params[:bulk_post]).save
|
|
83
|
+
|
|
84
|
+
params = {
|
|
85
|
+
bulk_post: {
|
|
86
|
+
posts_attributes: {
|
|
87
|
+
"0" => { state: 'draft', title: "My title1" },
|
|
88
|
+
"1" => { state: "public" }
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
BulkPostsForm.new(params[:bulk_post]).save
|
|
94
|
+
|
|
95
|
+
expect(Post.all.count).to(eq(0))
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it "Trying to create new Post and editing an existing one, but one of them is invalid" do
|
|
99
|
+
params = {
|
|
100
|
+
bulk_post: {
|
|
101
|
+
posts_attributes: {
|
|
102
|
+
"0" => { id: 1, title: "Changed title" },
|
|
103
|
+
"1" => { state: "public", title: "My title2" }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
existing_post = Post.create(title: "My title1")
|
|
109
|
+
BulkPostsForm.new(params[:bulk_post]).save
|
|
110
|
+
existing_post.reload
|
|
111
|
+
|
|
112
|
+
expect(Post.all.count).to(eq(1)) && expect(existing_post.title).to(eq('My title1'))
|
|
113
|
+
|
|
114
|
+
params = {
|
|
115
|
+
bulk_post: {
|
|
116
|
+
posts_attributes: {
|
|
117
|
+
"0" => { id: 1, state: 'draft', title: "Changed title" },
|
|
118
|
+
"1" => { state: "public" }
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
BulkPostsForm.new(params[:bulk_post]).save
|
|
124
|
+
existing_post.reload
|
|
125
|
+
|
|
126
|
+
expect(Post.all.count).to(eq(1)) && expect(existing_post.title).to(eq('My title1'))
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it "Trying to create new Post and deleting an existing one, but the new one is invalid" do
|
|
130
|
+
params = {
|
|
131
|
+
bulk_post: {
|
|
132
|
+
posts_attributes: {
|
|
133
|
+
"0" => { id: 1, state: "draft", title: "Changed title", _destroy: true },
|
|
134
|
+
"1" => { state: "public" }
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
existing_post = Post.create(title: "My title1")
|
|
140
|
+
BulkPostsForm.new(params[:bulk_post]).save
|
|
141
|
+
existing_post.reload
|
|
142
|
+
|
|
143
|
+
expect(Post.all.count).to(eq(1)) && expect(existing_post.title).to(eq('My title1'))
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it "Trying to create new Post and deleting an existing one, the existing one is invalid but since it is marked for destruction, it should be deleted" do
|
|
147
|
+
params = {
|
|
148
|
+
bulk_post: {
|
|
149
|
+
posts_attributes: {
|
|
150
|
+
"0" => { id: 1, title: "Changed title", _destroy: true },
|
|
151
|
+
"1" => { state: "public", title: "My title2" }
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
existing_post = Post.create(title: "My title1")
|
|
157
|
+
BulkPostsForm.new(params[:bulk_post]).save
|
|
158
|
+
|
|
159
|
+
expect(Post.all.count).to(eq(1)) && expect(Post.where(id: existing_post.id).present?).to(eq(false))
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it "Trying to create new Post and deleting an existing one, both of them are invalid, no changes should occur." do
|
|
163
|
+
params = {
|
|
164
|
+
bulk_post: {
|
|
165
|
+
posts_attributes: {
|
|
166
|
+
"0" => { id: 1, title: "Changed title", _destroy: true },
|
|
167
|
+
"1" => { state: "public" }
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
existing_post = Post.create(title: "My title1")
|
|
173
|
+
BulkPostsForm.new(params[:bulk_post]).save
|
|
174
|
+
|
|
175
|
+
expect(Post.all.count).to(eq(1)) && expect(existing_post.title).to(eq('My title1'))
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe PostFormChild do
|
|
4
|
+
|
|
5
|
+
it "PostFormChild becomes invalid when Post does and incorporates its errors" do
|
|
6
|
+
post = Post.new
|
|
7
|
+
post.should have(1).error_on(:title)
|
|
8
|
+
post.title = "My title"
|
|
9
|
+
post.should have(:no).errors_on(:title)
|
|
10
|
+
|
|
11
|
+
post_form = PostFormChild.new({ state: 'draft', date: Date.today })
|
|
12
|
+
post_form.should have(1).error_on(:title)
|
|
13
|
+
post_form.title = "My title"
|
|
14
|
+
post_form.should have(:no).errors_on(:title)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "PostFormChild may require the validations of fields that Post doesn't have" do
|
|
18
|
+
params = { post: { title: "My title" } }
|
|
19
|
+
|
|
20
|
+
post = Post.new(params[:post])
|
|
21
|
+
post.should have(:no).errors_on(:date)
|
|
22
|
+
|
|
23
|
+
post_form = PostFormChild.new(params[:post].merge(state: 'public'))
|
|
24
|
+
post_form.should have(1).error_on(:date)
|
|
25
|
+
post_form.date = Date.today
|
|
26
|
+
post_form.should have(:no).errors_on(:date)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "Post creation through PostFormChild" do
|
|
30
|
+
params = { post: { state: 'public', title: "My title", body: "My body", date: Date.today } }
|
|
31
|
+
post_form = PostFormChild.new(params[:post])
|
|
32
|
+
|
|
33
|
+
expect(post_form.save).to(eq(true)) && expect(post_form.post.persisted?).to(eq(true))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "Post can't be created if PostFormChild isn't valid" do
|
|
37
|
+
params = { post: { state: 'public', title: "My title", body: "My body" } }
|
|
38
|
+
post_form = PostFormChild.new(params[:post])
|
|
39
|
+
|
|
40
|
+
expect(post_form.save).to(eq(false)) && expect(post_form.post.persisted?).to(eq(false))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "Post can't be created if Post isn't valid" do
|
|
44
|
+
params = { post: { state: 'public', date: Date.today, body: "My body" } }
|
|
45
|
+
post_form = PostFormChild.new(params[:post])
|
|
46
|
+
|
|
47
|
+
expect(post_form.save).to(eq(false)) && expect(post_form.post.persisted?).to(eq(false))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "PostFormChild won't allow weak params to be updated, unlike Post" do
|
|
51
|
+
params = { post: { title: 'My title', body: "My body", admin: true } }
|
|
52
|
+
|
|
53
|
+
post_form = PostFormChild.new(params[:post].merge({ state: 'public', date: Date.today }))
|
|
54
|
+
expect(post_form.save).to(eq(true)) && expect(post_form.post.admin).to(eq(false))
|
|
55
|
+
|
|
56
|
+
post = Post.new(params[:post])
|
|
57
|
+
expect(post.save).to(eq(true)) && expect(post.admin).to(eq(true))
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe PostForm do
|
|
4
|
+
|
|
5
|
+
it "PostForm becomes invalid when Post does and incorporates its errors" do
|
|
6
|
+
post = Post.new
|
|
7
|
+
post.should have(1).error_on(:title)
|
|
8
|
+
post.title = "My title"
|
|
9
|
+
post.should have(:no).errors_on(:title)
|
|
10
|
+
|
|
11
|
+
post_form = PostForm.new({ state: 'draft' })
|
|
12
|
+
post_form.should have(1).error_on(:title)
|
|
13
|
+
post_form.title = "My title"
|
|
14
|
+
post_form.should have(:no).errors_on(:title)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "PostForm may require the validations of fields that Post doesn't have" do
|
|
18
|
+
params = { post: { title: "My title" } }
|
|
19
|
+
|
|
20
|
+
post = Post.new(params[:post])
|
|
21
|
+
post.should have(:no).errors_on(:state)
|
|
22
|
+
|
|
23
|
+
post_form = PostForm.new(params[:post])
|
|
24
|
+
post_form.should have(1).error_on(:state)
|
|
25
|
+
post_form.state = "draft"
|
|
26
|
+
post_form.should have(:no).errors_on(:state)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "Post creation through PostForm" do
|
|
30
|
+
params = { post: { state: 'public', title: "My title", body: "My body" } }
|
|
31
|
+
post_form = PostForm.new(params[:post])
|
|
32
|
+
|
|
33
|
+
expect(post_form.save).to(eq(true)) && expect(post_form.post.persisted?).to(eq(true))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "Post can't be created if PostForm isn't valid" do
|
|
37
|
+
params = { post: { title: "My title", body: "My body" } }
|
|
38
|
+
post_form = PostForm.new(params[:post])
|
|
39
|
+
|
|
40
|
+
expect(post_form.save).to(eq(false)) && expect(post_form.post.persisted?).to(eq(false))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "Post can't be created if Post isn't valid" do
|
|
44
|
+
params = { post: { state: 'public', body: "My body" } }
|
|
45
|
+
post_form = PostForm.new(params[:post])
|
|
46
|
+
|
|
47
|
+
expect(post_form.save).to(eq(false)) && expect(post_form.post.persisted?).to(eq(false))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "PostForm won't allow weak params to be updated, unlike Post" do
|
|
51
|
+
params = { post: { title: 'My title', body: "My body", admin: true } }
|
|
52
|
+
|
|
53
|
+
post_form = PostForm.new(params[:post].merge({ state: 'public' }))
|
|
54
|
+
expect(post_form.save).to(eq(true)) && expect(post_form.post.admin).to(eq(false))
|
|
55
|
+
|
|
56
|
+
post = Post.new(params[:post])
|
|
57
|
+
expect(post.save).to(eq(true)) && expect(post.admin).to(eq(true))
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
|
|
3
|
+
|
|
4
|
+
ENV["RAILS_ENV"] ||= 'test'
|
|
5
|
+
|
|
6
|
+
require 'bundler'
|
|
7
|
+
Bundler.setup
|
|
8
|
+
require 'rspec'
|
|
9
|
+
require 'pry'
|
|
10
|
+
#require 'database_cleaner'
|
|
11
|
+
|
|
12
|
+
require 'object_attorney'
|
|
13
|
+
require 'support/database_setup'
|
|
14
|
+
require 'support/active_model/validations'
|
|
15
|
+
require 'support/models/post'
|
|
16
|
+
require 'support/models/post_form'
|
|
17
|
+
require 'support/models/post_form_child'
|
|
18
|
+
require 'support/models/bulk_posts_form'
|
|
19
|
+
require 'support/models/bulk_posts_form_child'
|
|
20
|
+
|
|
21
|
+
RSpec.configure do |config|
|
|
22
|
+
|
|
23
|
+
I18n.enforce_available_locales = false
|
|
24
|
+
|
|
25
|
+
# see: http://iain.nl/testing-activerecord-in-isolation
|
|
26
|
+
config.around do |example|
|
|
27
|
+
ActiveRecord::Base.transaction do
|
|
28
|
+
example.run
|
|
29
|
+
raise ActiveRecord::Rollback
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# see: https://github.com/bmabey/database_cleaner#rspec-example
|
|
34
|
+
# config.before(:suite) do
|
|
35
|
+
# DatabaseCleaner.strategy = :transaction
|
|
36
|
+
# DatabaseCleaner.clean_with(:truncation)
|
|
37
|
+
# end
|
|
38
|
+
|
|
39
|
+
# config.before(:each) do
|
|
40
|
+
# DatabaseCleaner.start
|
|
41
|
+
# end
|
|
42
|
+
|
|
43
|
+
# config.after(:each) do
|
|
44
|
+
# DatabaseCleaner.clean
|
|
45
|
+
# end
|
|
46
|
+
|
|
47
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
# see: http://iain.nl/testing-activerecord-in-isolation
|
|
3
|
+
module ActiveModel::Validations
|
|
4
|
+
# Extension to enhance `should have` on AR Model instances. Calls
|
|
5
|
+
# model.valid? in order to prepare the object's errors object.
|
|
6
|
+
#
|
|
7
|
+
# You can also use this to specify the content of the error messages.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
#
|
|
11
|
+
# model.should have(:no).errors_on(:attribute)
|
|
12
|
+
# model.should have(1).error_on(:attribute)
|
|
13
|
+
# model.should have(n).errors_on(:attribute)
|
|
14
|
+
#
|
|
15
|
+
# model.errors_on(:attribute).should include("can't be blank")
|
|
16
|
+
def errors_on(attribute)
|
|
17
|
+
self.valid?
|
|
18
|
+
[self.errors[attribute]].flatten.compact
|
|
19
|
+
end
|
|
20
|
+
alias :error_on :errors_on
|
|
21
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# see: http://iain.nl/testing-activerecord-in-isolation
|
|
2
|
+
ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
|
|
3
|
+
ActiveRecord::Migrator.up "db/migrate"
|
|
4
|
+
|
|
5
|
+
# see: http://blog.aizatto.com/2007/05/27/activerecord-migrations-without-rails/
|
|
6
|
+
#ActiveRecord::Base.establish_connection(YAML::load(File.open('config/database.yml'))[ENV["RAILS_ENV"]])
|
|
7
|
+
#ActiveRecord::Base.logger = Logger.new(File.open('tmp/database.log', 'a'))
|
|
8
|
+
|
|
9
|
+
# config/database.yml
|
|
10
|
+
# test:
|
|
11
|
+
# adapter: sqlite3
|
|
12
|
+
# database: db/test.sqlite3
|
|
13
|
+
# pool: 5
|
|
14
|
+
# timeout: 5000
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'object_attorney/nested_uniqueness_validator'
|
|
2
|
+
|
|
3
|
+
class BulkPostsForm
|
|
4
|
+
|
|
5
|
+
include ObjectAttorney
|
|
6
|
+
|
|
7
|
+
accepts_nested_objects :posts
|
|
8
|
+
|
|
9
|
+
validates_nested_uniqueness :posts, uniq_value: :title
|
|
10
|
+
|
|
11
|
+
##################### BODY BELLOW THIS LINE ####################
|
|
12
|
+
|
|
13
|
+
def build_post(attributes = {}, post = nil)
|
|
14
|
+
PostForm.new(attributes, post)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def existing_posts
|
|
18
|
+
Post.all.map { |post| build_post({}, post) }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: object_attorney
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- João Gonçalves
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2013-12-
|
|
11
|
+
date: 2013-12-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -38,7 +38,7 @@ dependencies:
|
|
|
38
38
|
- - ! '>='
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '0'
|
|
41
|
-
description: Form Object
|
|
41
|
+
description: Form Object pattern implementation for Rails
|
|
42
42
|
email:
|
|
43
43
|
- goncalves.joao@gmail.com
|
|
44
44
|
executables: []
|
|
@@ -46,10 +46,15 @@ extensions: []
|
|
|
46
46
|
extra_rdoc_files: []
|
|
47
47
|
files:
|
|
48
48
|
- .gitignore
|
|
49
|
+
- .rspec
|
|
50
|
+
- .rvmrc
|
|
49
51
|
- Gemfile
|
|
52
|
+
- Guardfile
|
|
50
53
|
- LICENSE.txt
|
|
51
54
|
- README.md
|
|
52
55
|
- Rakefile
|
|
56
|
+
- db/migrate/20131205114900_create_posts.rb
|
|
57
|
+
- db/schema.rb
|
|
53
58
|
- lib/object_attorney.rb
|
|
54
59
|
- lib/object_attorney/nested_objects.rb
|
|
55
60
|
- lib/object_attorney/nested_uniqueness_validator.rb
|
|
@@ -57,6 +62,18 @@ files:
|
|
|
57
62
|
- lib/object_attorney/orm_handlers/smooth_operator.rb
|
|
58
63
|
- lib/object_attorney/version.rb
|
|
59
64
|
- object_attorney.gemspec
|
|
65
|
+
- spec/object_attorney/bulk_posts_form_child_spec.rb
|
|
66
|
+
- spec/object_attorney/bulk_posts_form_spec.rb
|
|
67
|
+
- spec/object_attorney/post_form_child_spec.rb
|
|
68
|
+
- spec/object_attorney/post_form_spec.rb
|
|
69
|
+
- spec/spec_helper.rb
|
|
70
|
+
- spec/support/active_model/validations.rb
|
|
71
|
+
- spec/support/database_setup.rb
|
|
72
|
+
- spec/support/models/bulk_posts_form.rb
|
|
73
|
+
- spec/support/models/bulk_posts_form_child.rb
|
|
74
|
+
- spec/support/models/post.rb
|
|
75
|
+
- spec/support/models/post_form.rb
|
|
76
|
+
- spec/support/models/post_form_child.rb
|
|
60
77
|
homepage: https://github.com/goncalvesjoao/object_attorney
|
|
61
78
|
licenses:
|
|
62
79
|
- MIT
|
|
@@ -80,7 +97,19 @@ rubyforge_project:
|
|
|
80
97
|
rubygems_version: 2.1.10
|
|
81
98
|
signing_key:
|
|
82
99
|
specification_version: 4
|
|
83
|
-
summary: This gem allows you to extract the code responsible for
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
100
|
+
summary: This gem allows you to extract the code responsible for 'validations', 'nested
|
|
101
|
+
objects' and 'strong parameters' from your model onto a specific class for a specific
|
|
102
|
+
use case.
|
|
103
|
+
test_files:
|
|
104
|
+
- spec/object_attorney/bulk_posts_form_child_spec.rb
|
|
105
|
+
- spec/object_attorney/bulk_posts_form_spec.rb
|
|
106
|
+
- spec/object_attorney/post_form_child_spec.rb
|
|
107
|
+
- spec/object_attorney/post_form_spec.rb
|
|
108
|
+
- spec/spec_helper.rb
|
|
109
|
+
- spec/support/active_model/validations.rb
|
|
110
|
+
- spec/support/database_setup.rb
|
|
111
|
+
- spec/support/models/bulk_posts_form.rb
|
|
112
|
+
- spec/support/models/bulk_posts_form_child.rb
|
|
113
|
+
- spec/support/models/post.rb
|
|
114
|
+
- spec/support/models/post_form.rb
|
|
115
|
+
- spec/support/models/post_form_child.rb
|