mongoid-embedded-errors 1.0.1 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +20 -0
- data/.gitignore +2 -0
- data/.rspec +3 -0
- data/.rubocop.yml +83 -0
- data/.semaphore/semaphore.yml +46 -0
- data/Appraisals +15 -0
- data/CHANGELOG.md +27 -0
- data/Gemfile +9 -3
- data/Guardfile +42 -0
- data/README.md +14 -4
- data/Rakefile +12 -6
- data/gemfiles/.bundle/config +2 -0
- data/gemfiles/mongoid_4.gemfile +16 -0
- data/gemfiles/mongoid_5.gemfile +16 -0
- data/gemfiles/mongoid_6.gemfile +16 -0
- data/gemfiles/mongoid_7.gemfile +16 -0
- data/lib/mongoid-embedded-errors.rb +3 -29
- data/lib/mongoid/embedded_errors.rb +49 -0
- data/lib/mongoid/embedded_errors/embedded_in.rb +18 -0
- data/lib/mongoid/embedded_errors/version.rb +6 -0
- data/mongoid-embedded-errors.gemspec +26 -15
- data/spec/lib/mongoid/embedded_errors_spec.rb +89 -0
- data/spec/spec_helper.rb +36 -9
- data/spec/support/models/annotation.rb +11 -0
- data/spec/support/models/article.rb +17 -0
- data/spec/support/models/page.rb +13 -0
- data/spec/support/models/section.rb +13 -0
- data/spec/support/mongoid.yml +13 -0
- metadata +86 -32
- data/Gemfile.lock +0 -44
- data/lib/mongoid-embedded-errors/embedded_in.rb +0 -16
- data/lib/mongoid-embedded-errors/version.rb +0 -5
- data/spec/config.yml +0 -6
- data/spec/embedded_errors_spec.rb +0 -46
- data/spec/support/models.rb +0 -37
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 129c9a68d1921837c6cd5942d64272049bbc8fd8f756c29b0a825faa4c1d1527
|
4
|
+
data.tar.gz: 57c35bcc8813866051d60d5139e5901169b8478ce4d5be291e862e0bb0604e17
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 857c87ef479cc4eb0ea15462f9c6386d2eff11987d0a0f895bc3429f14ffd9242fcbce2292bb7de04f2588481fa76bbdc1d0d9874bd2b6a4bcffc02b5149f22a
|
7
|
+
data.tar.gz: 3935c4a3d82f9e343b21f2272c05d4e8cf642191d79be7b89e82116220c76ba73b56b1e24885824e5fe6c9f17312a4f083cf2714a8356cb2768c0f1b4b57b488
|
data/.codeclimate.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
---
|
2
|
+
version: "2"
|
3
|
+
|
4
|
+
plugins:
|
5
|
+
rubocop:
|
6
|
+
enabled: true
|
7
|
+
bundler-audit:
|
8
|
+
enabled: true
|
9
|
+
fixme:
|
10
|
+
enabled: true
|
11
|
+
duplication:
|
12
|
+
enabled: true
|
13
|
+
reek:
|
14
|
+
enabled: true
|
15
|
+
git-legal:
|
16
|
+
enabled: true
|
17
|
+
|
18
|
+
config:
|
19
|
+
languages:
|
20
|
+
- ruby
|
data/.gitignore
CHANGED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# Use RuboCop RSpec
|
2
|
+
require: rubocop-rspec
|
3
|
+
|
4
|
+
# Common configuration.
|
5
|
+
AllCops:
|
6
|
+
# Cop names are not displayed in offense messages by default. Change behavior
|
7
|
+
# by overriding DisplayCopNames, or by giving the -D/--display-cop-names
|
8
|
+
# option.
|
9
|
+
DisplayCopNames: true
|
10
|
+
Exclude:
|
11
|
+
- Guardfile
|
12
|
+
- gemfiles/**/*
|
13
|
+
- bin/*
|
14
|
+
- vendor/bundle/**/*
|
15
|
+
- tmp/**/*
|
16
|
+
NewCops: enable
|
17
|
+
|
18
|
+
Naming/FileName:
|
19
|
+
Exclude:
|
20
|
+
- Appraisals
|
21
|
+
|
22
|
+
Layout/SpaceInsideHashLiteralBraces:
|
23
|
+
EnforcedStyle: no_space
|
24
|
+
EnforcedStyleForEmptyBraces: no_space
|
25
|
+
SupportedStyles:
|
26
|
+
- space
|
27
|
+
- no_space
|
28
|
+
|
29
|
+
Metrics/MethodLength:
|
30
|
+
CountComments: false # count full line comments?
|
31
|
+
Max: 40
|
32
|
+
|
33
|
+
Metrics/AbcSize:
|
34
|
+
Max: 40
|
35
|
+
|
36
|
+
Layout/LineLength:
|
37
|
+
Max: 120
|
38
|
+
|
39
|
+
Metrics/BlockLength:
|
40
|
+
Max: 50
|
41
|
+
|
42
|
+
Style/Documentation:
|
43
|
+
Description: 'Document classes and non-namespace modules.'
|
44
|
+
Enabled: false
|
45
|
+
|
46
|
+
Style/AutoResourceCleanup:
|
47
|
+
Description: 'Suggests the usage of an auto resource cleanup version of a method (if available).'
|
48
|
+
Enabled: true
|
49
|
+
|
50
|
+
Style/CollectionMethods:
|
51
|
+
Description: 'Preferred collection methods.'
|
52
|
+
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#map-find-select-reduce-size'
|
53
|
+
Enabled: true
|
54
|
+
|
55
|
+
Style/MethodCalledOnDoEndBlock:
|
56
|
+
Description: 'Avoid chaining a method call on a do...end block.'
|
57
|
+
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks'
|
58
|
+
Enabled: true
|
59
|
+
|
60
|
+
Layout/ExtraSpacing:
|
61
|
+
Description: 'Do not use unnecessary spacing.'
|
62
|
+
Enabled: true
|
63
|
+
|
64
|
+
Style/ClassAndModuleChildren:
|
65
|
+
# Checks the style of children definitions at classes and modules.
|
66
|
+
#
|
67
|
+
# Basically there are two different styles:
|
68
|
+
#
|
69
|
+
# `nested` - have each child on a separate line
|
70
|
+
# class Foo
|
71
|
+
# class Bar
|
72
|
+
# end
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# `compact` - combine definitions as much as possible
|
76
|
+
# class Foo::Bar
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# The compact style is only forced, for classes / modules with one child.
|
80
|
+
EnforcedStyle: compact
|
81
|
+
SupportedStyles:
|
82
|
+
- nested
|
83
|
+
- compact
|
@@ -0,0 +1,46 @@
|
|
1
|
+
version: v1.0
|
2
|
+
name: mongoid-embedded-errors
|
3
|
+
agent:
|
4
|
+
machine:
|
5
|
+
type: e1-standard-2
|
6
|
+
os_image: ubuntu1804
|
7
|
+
|
8
|
+
blocks:
|
9
|
+
- name: Code Climate
|
10
|
+
task:
|
11
|
+
jobs:
|
12
|
+
- name: Code Climate
|
13
|
+
commands:
|
14
|
+
- checkout
|
15
|
+
- cache restore gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock),gems-$SEMAPHORE_GIT_BRANCH,gems-master
|
16
|
+
- bundle install --path vendor/bundle
|
17
|
+
- cache store gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock) vendor/bundle
|
18
|
+
- docker run --interactive --tty --rm --env CODECLIMATE_CODE="$PWD" --volume "$PWD":/code --volume /tmp/cc:/tmp/cc --volume /var/run/docker.sock:/var/run/docker.sock codeclimate/codeclimate analyze
|
19
|
+
|
20
|
+
- name: Unit tests
|
21
|
+
task:
|
22
|
+
jobs:
|
23
|
+
- name: Appraisal
|
24
|
+
commands:
|
25
|
+
- checkout
|
26
|
+
- cache restore gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock),gems-$SEMAPHORE_GIT_BRANCH,gems-master
|
27
|
+
- bundle install --path vendor/bundle
|
28
|
+
- cache store gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock) vendor/bundle
|
29
|
+
- cache restore appraisal-$SEMAPHORE_GIT_BRANCH-$(checksum gemfiles/*.gemfile.lock),appraisal-$SEMAPHORE_GIT_BRANCH,appraisal-master
|
30
|
+
- bundle exec appraisal
|
31
|
+
- cache store appraisal-$SEMAPHORE_GIT_BRANCH-$(checksum gemfiles/*.gemfile.lock) vendor/bundle
|
32
|
+
- sem-service start mongodb
|
33
|
+
- bundle exec appraisal rake test
|
34
|
+
|
35
|
+
- name: Mutant
|
36
|
+
task:
|
37
|
+
jobs:
|
38
|
+
- name: Mutant
|
39
|
+
commands:
|
40
|
+
- checkout
|
41
|
+
- cache restore gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock),gems-$SEMAPHORE_GIT_BRANCH,gems-master
|
42
|
+
- bundle install --path vendor/bundle
|
43
|
+
- cache store gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock) vendor/bundle
|
44
|
+
- sem-service start mongodb
|
45
|
+
- MUTANT=true bundle exec mutant --since "$SEMAPHORE_GIT_SHA~1" --include lib --require mongoid --use rspec "Mongoid::EmbeddedErrors*"
|
46
|
+
|
data/Appraisals
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# Changelog
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
5
|
+
|
6
|
+
## [Unreleased]
|
7
|
+
- TODO
|
8
|
+
|
9
|
+
## [v3.0.1] - 2020-09-29
|
10
|
+
### Added
|
11
|
+
- Started 🔎 tracking changes in a changelog!
|
12
|
+
- Mongoid 7 support
|
13
|
+
- Ruby 2.4 support by using `send` instead of `public_send` [#18]
|
14
|
+
|
15
|
+
### Removed
|
16
|
+
- Mongoid 3 support
|
17
|
+
|
18
|
+
### Fixed
|
19
|
+
- Updated style to fix Rubocop issues (as per included .rubocop.yml configuration file)
|
20
|
+
- Removed .lock files as these are not supposed to be included with gem source code
|
21
|
+
|
22
|
+
## [v3.0.0] - 2020-09-29 [YANKED]
|
23
|
+
- Yanked due to wrong dependencies in gemspec.
|
24
|
+
|
25
|
+
[Unreleased]: https://github.com/glooko/mongoid-embedded-errors/compare/v3.0.1...HEAD
|
26
|
+
[v3.0.1]: https://github.com/glooko/mongoid-embedded-errors/compare/v3.0.0...v3.0.1
|
27
|
+
[v3.0.0]: https://github.com/glooko/mongoid-embedded-errors/compare/f1ce0d8ed140de86c894b2fad7ad197504fefd5a...v3.0.0
|
data/Gemfile
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
# Specify your gem's dependencies in
|
3
|
+
# Specify your gem's dependencies in mongoid-embedded-errors.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
-
|
7
|
-
gem '
|
6
|
+
group :development do
|
7
|
+
gem 'appraisal'
|
8
|
+
gem 'database_cleaner'
|
9
|
+
gem 'guard-rspec'
|
10
|
+
gem 'mutant-rspec'
|
11
|
+
gem 'rake'
|
12
|
+
gem 'rspec'
|
13
|
+
end
|
data/Guardfile
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
5
|
+
# directories %w(app lib config test spec features) \
|
6
|
+
# .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
|
7
|
+
|
8
|
+
## Note: if you are using the `directories` clause above and you are not
|
9
|
+
## watching the project directory ('.'), then you will want to move
|
10
|
+
## the Guardfile to a watched dir and symlink it back, e.g.
|
11
|
+
#
|
12
|
+
# $ mkdir config
|
13
|
+
# $ mv Guardfile config/
|
14
|
+
# $ ln -s config/Guardfile .
|
15
|
+
#
|
16
|
+
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
17
|
+
|
18
|
+
# Note: The cmd option is now required due to the increasing number of ways
|
19
|
+
# rspec may be run, below are examples of the most common uses.
|
20
|
+
# * bundler: 'bundle exec rspec'
|
21
|
+
# * bundler binstubs: 'bin/rspec'
|
22
|
+
# * spring: 'bin/rspec' (This will use spring if running and you have
|
23
|
+
# installed the spring binstubs per the docs)
|
24
|
+
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
25
|
+
# * 'just' rspec: 'rspec'
|
26
|
+
|
27
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
28
|
+
require "guard/rspec/dsl"
|
29
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
30
|
+
|
31
|
+
# Feel free to open issues for suggestions and improvements
|
32
|
+
|
33
|
+
# RSpec files
|
34
|
+
rspec = dsl.rspec
|
35
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
36
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
37
|
+
watch(rspec.spec_files)
|
38
|
+
|
39
|
+
# Ruby files
|
40
|
+
ruby = dsl.ruby
|
41
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
42
|
+
end
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Mongoid::EmbeddedErrors
|
2
|
+
[![Code Climate](https://codeclimate.com/github/glooko/mongoid-embedded-errors/badges/gpa.svg)](https://codeclimate.com/github/glooko/mongoid-embedded-errors)
|
2
3
|
|
3
|
-
Easily bubble up errors from embedded documents in Mongoid.
|
4
|
+
Easily bubble up errors from embedded documents in Mongoid and newer.
|
4
5
|
|
5
6
|
## Installation
|
6
7
|
|
@@ -61,7 +62,7 @@ If we were to create an invalid `Article` with an invalid `Page` and tried to va
|
|
61
62
|
article = Article.new(pages: [Page.new])
|
62
63
|
article.valid? # => false
|
63
64
|
|
64
|
-
article.
|
65
|
+
article.errors.messages
|
65
66
|
# => {:name=>["can't be blank"], :summary=>["can't be blank"], :pages=>["is invalid"]}
|
66
67
|
```
|
67
68
|
|
@@ -83,8 +84,13 @@ end
|
|
83
84
|
article = Article.new(pages: [Page.new(sections: [Section.new])])
|
84
85
|
article.valid? # => false
|
85
86
|
|
86
|
-
article.
|
87
|
-
|
87
|
+
article.errors.messages
|
88
|
+
{
|
89
|
+
:name => ["can't be blank"],
|
90
|
+
:summary => ["can't be blank"],
|
91
|
+
:"pages[0].title" => ["can't be blank"],
|
92
|
+
:"pages[0].sections[0].header" => ["can't be blank"]
|
93
|
+
}
|
88
94
|
```
|
89
95
|
|
90
96
|
Now, isn't that much nicer? Yeah, I think so to.
|
@@ -96,3 +102,7 @@ Now, isn't that much nicer? Yeah, I think so to.
|
|
96
102
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
97
103
|
4. Push to the branch (`git push origin my-new-feature`)
|
98
104
|
5. Create new Pull Request
|
105
|
+
|
106
|
+
## Contributors
|
107
|
+
|
108
|
+
[See here](https://github.com/glooko/mongoid-embedded-errors/graphs/contributors)
|
data/Rakefile
CHANGED
@@ -1,9 +1,15 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
|
2
6
|
Bundler::GemHelper.install_tasks
|
3
7
|
|
4
|
-
desc
|
5
|
-
task :
|
8
|
+
desc 'Run tests'
|
9
|
+
task default: [:test]
|
10
|
+
|
11
|
+
desc 'Run tests'
|
12
|
+
task spec: [:test]
|
6
13
|
|
7
|
-
|
8
|
-
|
9
|
-
end
|
14
|
+
desc 'Run tests'
|
15
|
+
task(:test) { system 'bundle exec rspec' }
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "mongoid", "~> 4.0"
|
6
|
+
|
7
|
+
group :development do
|
8
|
+
gem "appraisal"
|
9
|
+
gem "database_cleaner"
|
10
|
+
gem "guard-rspec"
|
11
|
+
gem "mutant-rspec"
|
12
|
+
gem "rake"
|
13
|
+
gem "rspec"
|
14
|
+
end
|
15
|
+
|
16
|
+
gemspec path: "../"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "mongoid", "~> 5.0"
|
6
|
+
|
7
|
+
group :development do
|
8
|
+
gem "appraisal"
|
9
|
+
gem "database_cleaner"
|
10
|
+
gem "guard-rspec"
|
11
|
+
gem "mutant-rspec"
|
12
|
+
gem "rake"
|
13
|
+
gem "rspec"
|
14
|
+
end
|
15
|
+
|
16
|
+
gemspec path: "../"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "mongoid", "~> 6.0"
|
6
|
+
|
7
|
+
group :development do
|
8
|
+
gem "appraisal"
|
9
|
+
gem "database_cleaner"
|
10
|
+
gem "guard-rspec"
|
11
|
+
gem "mutant-rspec"
|
12
|
+
gem "rake"
|
13
|
+
gem "rspec"
|
14
|
+
end
|
15
|
+
|
16
|
+
gemspec path: "../"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "mongoid", "~> 7.0"
|
6
|
+
|
7
|
+
group :development do
|
8
|
+
gem "appraisal"
|
9
|
+
gem "database_cleaner"
|
10
|
+
gem "guard-rspec"
|
11
|
+
gem "mutant-rspec"
|
12
|
+
gem "rake"
|
13
|
+
gem "rspec"
|
14
|
+
end
|
15
|
+
|
16
|
+
gemspec path: "../"
|
@@ -1,29 +1,3 @@
|
|
1
|
-
|
2
|
-
require
|
3
|
-
|
4
|
-
|
5
|
-
module Mongoid
|
6
|
-
module EmbeddedErrors
|
7
|
-
|
8
|
-
def self.included(klass)
|
9
|
-
# make sure that the alias only happens once:
|
10
|
-
unless klass.instance_methods.include?(:errors_without_embedded_errors)
|
11
|
-
klass.alias_method_chain(:errors, :embedded_errors)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def errors_with_embedded_errors
|
16
|
-
errs = errors_without_embedded_errors
|
17
|
-
self.embedded_relations.each do |name, metadata|
|
18
|
-
if errs[name]
|
19
|
-
errs.delete(name.to_sym)
|
20
|
-
self.send(name).each do |rel|
|
21
|
-
errs[name] = {rel.id.to_s => rel.errors.messages} if rel.errors.any?
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
return errs
|
26
|
-
end
|
27
|
-
|
28
|
-
end
|
29
|
-
end
|
1
|
+
# rubocop:disable Naming/FileName
|
2
|
+
require 'mongoid/embedded_errors'
|
3
|
+
# rubocop:enable Naming/FileName
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'mongoid'
|
4
|
+
require 'mongoid/embedded_errors/version'
|
5
|
+
require 'mongoid/embedded_errors/embedded_in'
|
6
|
+
|
7
|
+
module Mongoid::EmbeddedErrors
|
8
|
+
def self.included(klass)
|
9
|
+
return if klass.instance_methods.include?(:errors_without_embedded_errors)
|
10
|
+
|
11
|
+
klass.send :alias_method, :errors_without_embedded_errors, :errors
|
12
|
+
klass.send :alias_method, :errors, :errors_with_embedded_errors
|
13
|
+
end
|
14
|
+
|
15
|
+
def errors_with_embedded_errors
|
16
|
+
errors_without_embedded_errors.tap do |errs|
|
17
|
+
embedded_relations.each do |name, metadata|
|
18
|
+
# name is something like pages or sections
|
19
|
+
# if there is an 'is invalid' message for the relation then let's work it:
|
20
|
+
next unless Array(public_send(name)).any? { |doc| doc.errors.any? }
|
21
|
+
|
22
|
+
# first delete the unless 'is invalid' error for the relation
|
23
|
+
errs[name].delete 'is invalid'
|
24
|
+
errs.delete name.to_sym if errs[name].empty?
|
25
|
+
|
26
|
+
# next, loop through each of the relations (pages, sections, etc...)
|
27
|
+
[public_send(name)].flatten.reject(&:nil?).each_with_index do |rel, i|
|
28
|
+
next unless rel.errors.any?
|
29
|
+
|
30
|
+
# get each of their individual message and add them to the parent's errors:
|
31
|
+
rel.errors.each do |k, v|
|
32
|
+
relation = if Gem::Version.new(Mongoid::VERSION) >= Gem::Version.new('7.0.0')
|
33
|
+
metadata.class
|
34
|
+
else
|
35
|
+
metadata.relation
|
36
|
+
end
|
37
|
+
key = if relation.equal? EMBEDS_MANY
|
38
|
+
"#{name}[#{i}].#{k}"
|
39
|
+
else
|
40
|
+
"#{name}.#{k}"
|
41
|
+
end.to_sym
|
42
|
+
errs.delete(key)
|
43
|
+
errs.add key, v if v.present?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
if Gem::Version.new(Mongoid::VERSION) >= Gem::Version.new('7.0.0')
|
2
|
+
require 'mongoid/association/embedded/embedded_in'
|
3
|
+
ASSOCIATION = Mongoid::Association::Macros::ClassMethods
|
4
|
+
EMBEDS_MANY = Mongoid::Association::Embedded::EmbedsMany
|
5
|
+
else
|
6
|
+
require 'mongoid/relations/embedded/in'
|
7
|
+
ASSOCIATION = Mongoid::Relations::Macros::ClassMethods
|
8
|
+
EMBEDS_MANY = Mongoid::Relations::Embedded::Many
|
9
|
+
end
|
10
|
+
|
11
|
+
module ASSOCIATION
|
12
|
+
alias embedded_in_without_embedded_errors embedded_in
|
13
|
+
def embedded_in(*args)
|
14
|
+
relation = embedded_in_without_embedded_errors(*args)
|
15
|
+
send(:include, Mongoid::EmbeddedErrors)
|
16
|
+
relation
|
17
|
+
end
|
18
|
+
end
|
@@ -1,21 +1,32 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'mongoid
|
5
|
+
require 'mongoid/embedded_errors/version'
|
5
6
|
|
6
7
|
Gem::Specification.new do |gem|
|
7
|
-
gem.name =
|
8
|
-
gem.version = Mongoid::EmbeddedErrors::VERSION
|
9
|
-
gem.authors = [
|
10
|
-
gem.email = [
|
11
|
-
gem.description =
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
8
|
+
gem.name = 'mongoid-embedded-errors'
|
9
|
+
gem.version = Mongoid::EmbeddedErrors::VERSION.dup
|
10
|
+
gem.authors = ['Mark Bates', 'Kristijan Novoselić']
|
11
|
+
gem.email = ['mark@markbates.com', 'kristijan@glooko.com']
|
12
|
+
gem.description = 'Embedded documents in Mongoid can be really useful. '\
|
13
|
+
'However, when one of those embedded documents is '\
|
14
|
+
'invalid, Mongoid does not say which validation has '\
|
15
|
+
'failed. Instead of just saying that an embedded '\
|
16
|
+
'document is invalid, this gem modifies Mongoid '\
|
17
|
+
'behavior so it explicitly provides validation errors '\
|
18
|
+
'on a per-field basis for embedded documents, the '\
|
19
|
+
'same way it does for parent documents.'
|
20
|
+
gem.summary = 'Easily bubble up errors from embedded '\
|
21
|
+
'documents in Mongoid.'
|
22
|
+
gem.homepage = 'https://github.com/glooko/mongoid-embedded-errors'
|
23
|
+
gem.licenses = ['MIT']
|
24
|
+
gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
25
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
17
26
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
-
gem.require_paths = [
|
27
|
+
gem.require_paths = ['lib']
|
19
28
|
|
20
|
-
gem.add_dependency
|
29
|
+
gem.add_dependency 'mongoid', '>=4.0', '<8.0.0'
|
30
|
+
gem.add_development_dependency 'rubocop', '~> 0.92'
|
31
|
+
gem.add_development_dependency 'rubocop-rspec', '~> 1.43'
|
21
32
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Mongoid::EmbeddedErrors do
|
4
|
+
describe '#errors_with_embedded_errors' do
|
5
|
+
subject(:article) { Article.new name: 'Test', summary: '-', pages: pages }
|
6
|
+
|
7
|
+
before { |spec| article.validate unless spec.metadata[:do_not_validate] }
|
8
|
+
|
9
|
+
context 'when module is already included in a class', :do_not_validate do
|
10
|
+
let(:dummy_class) do
|
11
|
+
Class.new do
|
12
|
+
include Mongoid::Document
|
13
|
+
include Mongoid::EmbeddedErrors
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'does not create errors_without_embedded_errors alias again' do
|
18
|
+
a = dummy_class.instance_method(:errors_without_embedded_errors)
|
19
|
+
dummy_class.include described_class
|
20
|
+
|
21
|
+
expect(
|
22
|
+
a
|
23
|
+
).to eq(dummy_class.instance_method(:errors_without_embedded_errors))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when article does not have any pages associated' do
|
28
|
+
let(:pages) { [] }
|
29
|
+
|
30
|
+
it { is_expected.not_to be_valid }
|
31
|
+
|
32
|
+
it "returns `can't be blank` error for pages" do
|
33
|
+
expect(article.errors[:pages]).to include "can't be blank"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when article has one or more invalid pages' do
|
38
|
+
let(:pages) { [Page.new] }
|
39
|
+
|
40
|
+
it { is_expected.not_to be_valid }
|
41
|
+
|
42
|
+
it 'does not have any errors under `:pages` key' do
|
43
|
+
expect(article.errors[:pages]).to be_empty
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'returns all errors for `pages[0]` object' do
|
47
|
+
expect(article.errors[:'pages[0].title']).to include "can't be blank"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when validated multiple times' do
|
52
|
+
let(:pages) { [Page.new] }
|
53
|
+
|
54
|
+
it 'does not have duplicated errors for the same object' do
|
55
|
+
article.valid?
|
56
|
+
expect(article.errors[:'pages[0].title']).to eq(["can't be blank"])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'when embeds_many relation is invalid' do
|
61
|
+
let(:pages) { [Page.new(title: 'Test page', sections: sections)] }
|
62
|
+
let(:sections) { [Section.new] }
|
63
|
+
|
64
|
+
it 'returns all errors for `sections[0]` object' do
|
65
|
+
expect(article.errors[:'pages[0].sections[0].header']).to include "can't be blank"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'when embeds_one relation is invalid' do
|
70
|
+
subject(:article) { Article.new name: 'Test', summary: '-', pages: pages, annotation: annotation }
|
71
|
+
|
72
|
+
let(:pages) { [Page.new(title: 'Test page')] }
|
73
|
+
let(:annotation) { Annotation.new }
|
74
|
+
|
75
|
+
it 'returns all errors for `annotation` object' do
|
76
|
+
expect(article.errors[:'annotation.text']).to include "can't be blank"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'when embedded document has not been validated',
|
81
|
+
:do_not_validate do
|
82
|
+
let(:pages) { [Page.new] }
|
83
|
+
|
84
|
+
it 'does not trigger validations' do
|
85
|
+
expect(article.errors).to be_empty
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,18 +1,45 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'mongoid-embedded-errors' # and any other gems you need
|
1
|
+
# frozen_string_literal: true
|
4
2
|
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'mongoid-embedded-errors'
|
5
5
|
require 'database_cleaner'
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
current_path = File.dirname(__FILE__)
|
8
|
+
SPEC_MODELS_PATH = File.join(current_path, 'support/**/*.rb').freeze
|
9
|
+
Dir[SPEC_MODELS_PATH].each { |f| require f }
|
9
10
|
|
10
|
-
|
11
|
+
Mongoid.load! File.join(current_path, 'support/mongoid.yml'), :test
|
11
12
|
|
12
13
|
RSpec.configure do |config|
|
14
|
+
config.run_all_when_everything_filtered = true
|
15
|
+
config.filter_run :focus
|
16
|
+
config.filter_run_excluding :skip
|
17
|
+
|
18
|
+
config.expect_with :rspec do |expectations|
|
19
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
20
|
+
expectations.syntax = :expect
|
21
|
+
end
|
13
22
|
|
14
|
-
config.
|
15
|
-
|
23
|
+
config.mock_with :rspec do |mocks|
|
24
|
+
mocks.verify_partial_doubles = true
|
16
25
|
end
|
26
|
+
config.disable_monkey_patching!
|
17
27
|
|
18
|
-
|
28
|
+
config.before(:suite) do
|
29
|
+
DatabaseCleaner.strategy = :truncation
|
30
|
+
end
|
31
|
+
|
32
|
+
config.around do |example|
|
33
|
+
DatabaseCleaner.cleaning { example.run }
|
34
|
+
end
|
35
|
+
|
36
|
+
config.before do
|
37
|
+
# Need to manually reload spec models for mutant to work as expected
|
38
|
+
if ENV['MUTANT']
|
39
|
+
Dir[SPEC_MODELS_PATH].each do |filename|
|
40
|
+
Object.send(:remove_const, File.basename(filename, '.rb').capitalize)
|
41
|
+
load filename
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Article
|
4
|
+
include Mongoid::Document
|
5
|
+
include Mongoid::Timestamps
|
6
|
+
include Mongoid::EmbeddedErrors
|
7
|
+
|
8
|
+
embeds_many :pages
|
9
|
+
embeds_one :annotation
|
10
|
+
|
11
|
+
field :name, type: String
|
12
|
+
field :summary, type: String
|
13
|
+
|
14
|
+
validates :name, presence: true
|
15
|
+
validates :summary, presence: true
|
16
|
+
validates :pages, presence: true
|
17
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Section
|
4
|
+
include Mongoid::Document
|
5
|
+
include Mongoid::Timestamps
|
6
|
+
|
7
|
+
embedded_in :page, inverse_of: :sections
|
8
|
+
|
9
|
+
field :header, type: String
|
10
|
+
field :body, type: String
|
11
|
+
|
12
|
+
validates :header, presence: true
|
13
|
+
end
|
metadata
CHANGED
@@ -1,79 +1,133 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongoid-embedded-errors
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
5
|
-
prerelease:
|
4
|
+
version: 3.0.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Mark Bates
|
9
|
-
|
8
|
+
- Kristijan Novoselić
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2020-09-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mongoid
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
17
|
requirements:
|
19
|
-
- -
|
18
|
+
- - ">="
|
20
19
|
- !ruby/object:Gem::Version
|
21
|
-
version:
|
20
|
+
version: '4.0'
|
21
|
+
- - "<"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 8.0.0
|
22
24
|
type: :runtime
|
23
25
|
prerelease: false
|
24
26
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
27
|
requirements:
|
27
|
-
- -
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '4.0'
|
31
|
+
- - "<"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 8.0.0
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rubocop
|
36
|
+
requirement: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.92'
|
41
|
+
type: :development
|
42
|
+
prerelease: false
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.92'
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: rubocop-rspec
|
50
|
+
requirement: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.43'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
28
60
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
30
|
-
description:
|
61
|
+
version: '1.43'
|
62
|
+
description: Embedded documents in Mongoid can be really useful. However, when one
|
63
|
+
of those embedded documents is invalid, Mongoid does not say which validation has
|
64
|
+
failed. Instead of just saying that an embedded document is invalid, this gem modifies
|
65
|
+
Mongoid behavior so it explicitly provides validation errors on a per-field basis
|
66
|
+
for embedded documents, the same way it does for parent documents.
|
31
67
|
email:
|
32
68
|
- mark@markbates.com
|
69
|
+
- kristijan@glooko.com
|
33
70
|
executables: []
|
34
71
|
extensions: []
|
35
72
|
extra_rdoc_files: []
|
36
73
|
files:
|
37
|
-
- .
|
74
|
+
- ".codeclimate.yml"
|
75
|
+
- ".gitignore"
|
76
|
+
- ".rspec"
|
77
|
+
- ".rubocop.yml"
|
78
|
+
- ".semaphore/semaphore.yml"
|
79
|
+
- Appraisals
|
80
|
+
- CHANGELOG.md
|
38
81
|
- Gemfile
|
39
|
-
-
|
82
|
+
- Guardfile
|
40
83
|
- LICENSE.txt
|
41
84
|
- README.md
|
42
85
|
- Rakefile
|
86
|
+
- gemfiles/.bundle/config
|
87
|
+
- gemfiles/mongoid_4.gemfile
|
88
|
+
- gemfiles/mongoid_5.gemfile
|
89
|
+
- gemfiles/mongoid_6.gemfile
|
90
|
+
- gemfiles/mongoid_7.gemfile
|
43
91
|
- lib/mongoid-embedded-errors.rb
|
44
|
-
- lib/mongoid
|
45
|
-
- lib/mongoid
|
92
|
+
- lib/mongoid/embedded_errors.rb
|
93
|
+
- lib/mongoid/embedded_errors/embedded_in.rb
|
94
|
+
- lib/mongoid/embedded_errors/version.rb
|
46
95
|
- mongoid-embedded-errors.gemspec
|
47
|
-
- spec/
|
48
|
-
- spec/embedded_errors_spec.rb
|
96
|
+
- spec/lib/mongoid/embedded_errors_spec.rb
|
49
97
|
- spec/spec_helper.rb
|
50
|
-
- spec/support/models.rb
|
51
|
-
|
52
|
-
|
53
|
-
|
98
|
+
- spec/support/models/annotation.rb
|
99
|
+
- spec/support/models/article.rb
|
100
|
+
- spec/support/models/page.rb
|
101
|
+
- spec/support/models/section.rb
|
102
|
+
- spec/support/mongoid.yml
|
103
|
+
homepage: https://github.com/glooko/mongoid-embedded-errors
|
104
|
+
licenses:
|
105
|
+
- MIT
|
106
|
+
metadata: {}
|
107
|
+
post_install_message:
|
54
108
|
rdoc_options: []
|
55
109
|
require_paths:
|
56
110
|
- lib
|
57
111
|
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
-
none: false
|
59
112
|
requirements:
|
60
|
-
- -
|
113
|
+
- - ">="
|
61
114
|
- !ruby/object:Gem::Version
|
62
115
|
version: '0'
|
63
116
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
-
none: false
|
65
117
|
requirements:
|
66
|
-
- -
|
118
|
+
- - ">="
|
67
119
|
- !ruby/object:Gem::Version
|
68
120
|
version: '0'
|
69
121
|
requirements: []
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
specification_version: 3
|
122
|
+
rubygems_version: 3.1.4
|
123
|
+
signing_key:
|
124
|
+
specification_version: 4
|
74
125
|
summary: Easily bubble up errors from embedded documents in Mongoid.
|
75
126
|
test_files:
|
76
|
-
- spec/
|
77
|
-
- spec/embedded_errors_spec.rb
|
127
|
+
- spec/lib/mongoid/embedded_errors_spec.rb
|
78
128
|
- spec/spec_helper.rb
|
79
|
-
- spec/support/models.rb
|
129
|
+
- spec/support/models/annotation.rb
|
130
|
+
- spec/support/models/article.rb
|
131
|
+
- spec/support/models/page.rb
|
132
|
+
- spec/support/models/section.rb
|
133
|
+
- spec/support/mongoid.yml
|
data/Gemfile.lock
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
mongoid-embedded-errors (1.0.1)
|
5
|
-
mongoid (>= 3.0.0)
|
6
|
-
|
7
|
-
GEM
|
8
|
-
remote: https://rubygems.org/
|
9
|
-
specs:
|
10
|
-
activemodel (3.2.9)
|
11
|
-
activesupport (= 3.2.9)
|
12
|
-
builder (~> 3.0.0)
|
13
|
-
activesupport (3.2.9)
|
14
|
-
i18n (~> 0.6)
|
15
|
-
multi_json (~> 1.0)
|
16
|
-
builder (3.0.4)
|
17
|
-
database_cleaner (0.9.1)
|
18
|
-
diff-lcs (1.1.3)
|
19
|
-
i18n (0.6.1)
|
20
|
-
mongoid (3.0.9)
|
21
|
-
activemodel (~> 3.1)
|
22
|
-
moped (~> 1.1)
|
23
|
-
origin (~> 1.0)
|
24
|
-
tzinfo (~> 0.3.22)
|
25
|
-
moped (1.2.7)
|
26
|
-
multi_json (1.3.7)
|
27
|
-
origin (1.0.9)
|
28
|
-
rspec (2.11.0)
|
29
|
-
rspec-core (~> 2.11.0)
|
30
|
-
rspec-expectations (~> 2.11.0)
|
31
|
-
rspec-mocks (~> 2.11.0)
|
32
|
-
rspec-core (2.11.1)
|
33
|
-
rspec-expectations (2.11.3)
|
34
|
-
diff-lcs (~> 1.1.3)
|
35
|
-
rspec-mocks (2.11.3)
|
36
|
-
tzinfo (0.3.35)
|
37
|
-
|
38
|
-
PLATFORMS
|
39
|
-
ruby
|
40
|
-
|
41
|
-
DEPENDENCIES
|
42
|
-
database_cleaner
|
43
|
-
mongoid-embedded-errors!
|
44
|
-
rspec
|
@@ -1,16 +0,0 @@
|
|
1
|
-
require "mongoid/relations/embedded/in"
|
2
|
-
|
3
|
-
module Mongoid
|
4
|
-
module Relations
|
5
|
-
module Macros
|
6
|
-
module ClassMethods
|
7
|
-
alias :embedded_in_without_embedded_errors :embedded_in
|
8
|
-
def embedded_in(*args)
|
9
|
-
relation = embedded_in_without_embedded_errors(*args)
|
10
|
-
self.send(:include, Mongoid::EmbeddedErrors)
|
11
|
-
return relation
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
data/spec/config.yml
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Mongoid::EmbeddedErrors do
|
4
|
-
|
5
|
-
let(:article) { Article.new }
|
6
|
-
let(:invalid_page) { Page.new }
|
7
|
-
let(:invalid_section) { Section.new }
|
8
|
-
let(:valid_section) { Section.new(header: "My Header") }
|
9
|
-
|
10
|
-
describe "errors" do
|
11
|
-
|
12
|
-
it "bubbles up errors from embedded documents" do
|
13
|
-
invalid_page.sections << invalid_section
|
14
|
-
article.pages << invalid_page
|
15
|
-
article.should_not be_valid
|
16
|
-
puts article.errors.messages
|
17
|
-
article.errors.messages.should eql({
|
18
|
-
name: ["can't be blank"],
|
19
|
-
summary: ["can't be blank"],
|
20
|
-
pages: [{
|
21
|
-
invalid_page.id.to_s => {
|
22
|
-
title: ["can't be blank"],
|
23
|
-
sections: [{
|
24
|
-
invalid_section.id.to_s => {
|
25
|
-
header: ["can't be blank"]
|
26
|
-
}
|
27
|
-
}]
|
28
|
-
}
|
29
|
-
}]
|
30
|
-
})
|
31
|
-
end
|
32
|
-
|
33
|
-
it "save works as before" do
|
34
|
-
article.save.should be_false
|
35
|
-
article.should_not be_persisted
|
36
|
-
article.errors.messages.should eql(name: ["can't be blank"], summary: ["can't be blank"])
|
37
|
-
end
|
38
|
-
|
39
|
-
it "handles errors on the main object" do
|
40
|
-
article.should_not be_valid
|
41
|
-
article.errors.messages.should eql(name: ["can't be blank"], summary: ["can't be blank"])
|
42
|
-
end
|
43
|
-
|
44
|
-
end
|
45
|
-
|
46
|
-
end
|
data/spec/support/models.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
class Article
|
2
|
-
include Mongoid::Document
|
3
|
-
include Mongoid::Timestamps
|
4
|
-
include Mongoid::EmbeddedErrors
|
5
|
-
|
6
|
-
field :name, type: String
|
7
|
-
field :summary, type: String
|
8
|
-
|
9
|
-
validates :name, presence: true
|
10
|
-
validates :summary, presence: true
|
11
|
-
|
12
|
-
embeds_many :pages
|
13
|
-
end
|
14
|
-
|
15
|
-
class Page
|
16
|
-
include Mongoid::Document
|
17
|
-
include Mongoid::Timestamps
|
18
|
-
|
19
|
-
field :title, type: String
|
20
|
-
|
21
|
-
validates :title, presence: true
|
22
|
-
|
23
|
-
embedded_in :article, inverse_of: :pages
|
24
|
-
embeds_many :sections
|
25
|
-
end
|
26
|
-
|
27
|
-
class Section
|
28
|
-
include Mongoid::Document
|
29
|
-
include Mongoid::Timestamps
|
30
|
-
|
31
|
-
field :header, type: String
|
32
|
-
field :body, type: String
|
33
|
-
|
34
|
-
validates :header, presence: true
|
35
|
-
|
36
|
-
embedded_in :page, inverse_of: :sections
|
37
|
-
end
|