udongo 5.1.0 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/backend/application.js +1 -1
  3. data/app/controllers/backend/assets_controller.rb +52 -0
  4. data/app/models/asset.rb +37 -0
  5. data/app/models/asset_image.rb +172 -0
  6. data/app/models/concerns/imageable.rb +13 -0
  7. data/app/models/image.rb +11 -0
  8. data/app/uploaders/asset_uploader.rb +36 -0
  9. data/app/views/backend/assets/_filter.html.erb +17 -0
  10. data/app/views/backend/assets/_form.html.erb +28 -0
  11. data/app/views/backend/assets/edit.html.erb +5 -0
  12. data/app/views/backend/assets/index.html.erb +48 -0
  13. data/app/views/backend/assets/new.html.erb +4 -0
  14. data/app/views/backend/snippets/edit.html.erb +1 -1
  15. data/app/views/backend/users/index.html.erb +2 -2
  16. data/app/views/layouts/backend/_top_navigation.html.erb +1 -0
  17. data/changelog.md +9 -0
  18. data/config/locales/en.yml +2 -1
  19. data/config/locales/en_backend.yml +6 -0
  20. data/config/locales/en_general.yml +1 -0
  21. data/config/locales/fr.yml +1 -1
  22. data/config/locales/nl.yml +2 -1
  23. data/config/locales/nl_backend.yml +5 -0
  24. data/config/locales/nl_general.yml +1 -0
  25. data/config/routes.rb +2 -0
  26. data/db/migrate/20170225144523_create_assets.rb +14 -0
  27. data/db/migrate/20170228183831_create_images.rb +18 -0
  28. data/lib/udongo/configs/assets.rb +10 -0
  29. data/lib/udongo/version.rb +1 -1
  30. data/readme.md +11 -0
  31. data/spec/factories/addresses.rb +6 -0
  32. data/spec/factories/admins.rb +10 -0
  33. data/spec/factories/assets.rb +5 -0
  34. data/spec/factories/comments.rb +9 -0
  35. data/spec/factories/content_columns.rb +10 -0
  36. data/spec/factories/content_images.rb +9 -0
  37. data/spec/factories/content_rows.rb +5 -0
  38. data/spec/factories/content_texts.rb +4 -0
  39. data/spec/factories/email_templates.rb +8 -0
  40. data/spec/factories/emails.rb +11 -0
  41. data/spec/factories/images.rb +5 -0
  42. data/spec/factories/logs.rb +4 -0
  43. data/spec/factories/meta.rb +7 -0
  44. data/spec/factories/navigation_items.rb +5 -0
  45. data/spec/factories/navigations.rb +6 -0
  46. data/spec/factories/notes.rb +6 -0
  47. data/spec/factories/pages.rb +5 -0
  48. data/spec/factories/queued_tasks.rb +5 -0
  49. data/spec/factories/redirects.rb +7 -0
  50. data/spec/factories/search_indices.rb +6 -0
  51. data/spec/factories/search_modules.rb +5 -0
  52. data/spec/factories/search_synonyms.rb +7 -0
  53. data/spec/factories/settings.rb +7 -0
  54. data/spec/factories/snippets.rb +6 -0
  55. data/spec/factories/stores.rb +6 -0
  56. data/spec/factories/tagged_items.rb +5 -0
  57. data/spec/factories/tags.rb +7 -0
  58. data/spec/factories/users.rb +9 -0
  59. data/spec/support/concerns/cacheable.rb +10 -0
  60. data/spec/support/concerns/commentable.rb +10 -0
  61. data/spec/support/concerns/content_type.rb +20 -0
  62. data/spec/support/concerns/deletable.rb +33 -0
  63. data/spec/support/concerns/draggable.rb +29 -0
  64. data/spec/support/concerns/emailable.rb +7 -0
  65. data/spec/support/concerns/flexible_content.rb +22 -0
  66. data/spec/support/concerns/imageable.rb +15 -0
  67. data/spec/support/concerns/locale.rb +21 -0
  68. data/spec/support/concerns/loggable.rb +19 -0
  69. data/spec/support/concerns/notable.rb +10 -0
  70. data/spec/support/concerns/parentable.rb +73 -0
  71. data/spec/support/concerns/person.rb +15 -0
  72. data/spec/support/concerns/publishable.rb +41 -0
  73. data/spec/support/concerns/searchable.rb +130 -0
  74. data/spec/support/concerns/seo.rb +54 -0
  75. data/spec/support/concerns/sortable.rb +67 -0
  76. data/spec/support/concerns/spammable.rb +53 -0
  77. data/spec/support/concerns/storable.rb +18 -0
  78. data/spec/support/concerns/taggable.rb +51 -0
  79. data/spec/support/concerns/translatable.rb +71 -0
  80. data/spec/support/concerns/visible.rb +64 -0
  81. metadata +66 -2
@@ -0,0 +1,7 @@
1
+ FactoryGirl.define do
2
+ factory :search_synonym do
3
+ locale 'nl'
4
+ term 'fubar'
5
+ synonyms 'Foo,Bar'
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ FactoryGirl.define do
2
+ factory :setting do
3
+ sequence(:name) { |n| "name-#{n}" }
4
+ description 'foo'
5
+ end
6
+ end
7
+
@@ -0,0 +1,6 @@
1
+ FactoryGirl.define do
2
+ factory :snippet do
3
+ sequence(:identifier) { |n| "identifier-#{n}" }
4
+ description 'Foo'
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ FactoryGirl.define do
2
+ factory :store do
3
+ collection 'default'
4
+ name 'foo'
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ FactoryGirl.define do
2
+ factory :tagged_item do
3
+ tag
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ FactoryGirl.define do
2
+ factory :tag do
3
+ locale 'nl'
4
+ sequence(:name) { |n| "tag #{n}" }
5
+ sequence(:slug) { |n| "tag-#{n}" }
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ FactoryGirl.define do
2
+ factory :user do
3
+ first_name 'John'
4
+ last_name 'Doe'
5
+ sequence(:email) { |n| "john.doe.#{n}@example.com" }
6
+ password 'sekret'
7
+ password_confirmation 'sekret'
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ require 'rails_helper'
2
+
3
+ shared_examples_for :cacheable do
4
+ let(:model) { described_class }
5
+ let(:klass) { model.to_s.underscore.to_sym }
6
+
7
+ it '.respond_to?' do
8
+ expect(model).to respond_to(:find_in_cache)
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require 'rails_helper'
2
+
3
+ shared_examples_for :commentable do
4
+ let(:model) { described_class }
5
+ let(:klass) { model.to_s.underscore.to_sym }
6
+
7
+ it '#respond_to?' do
8
+ expect(model.new).to respond_to(:comments)
9
+ end
10
+ end
@@ -0,0 +1,20 @@
1
+ require 'rails_helper'
2
+
3
+ shared_examples_for :content_type do
4
+ let(:model) { described_class }
5
+ let(:klass) { model.to_s.underscore.to_sym }
6
+
7
+ it '#column' do
8
+ page = create(:page)
9
+ page.content_rows << create(:content_row, rowable: page)
10
+ row = page.content_rows.first
11
+ type = create(klass)
12
+ row.columns << create(:content_column, content_type: type.class, content_id: type.id)
13
+
14
+ expect(type.column).to eq row.columns.take
15
+ end
16
+
17
+ it '#respond_to?' do
18
+ expect(build(klass)).to respond_to(:column)
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ require 'rails_helper'
2
+
3
+ shared_examples_for :deletable do
4
+ let(:model) { described_class }
5
+ let(:klass) { model.to_s.underscore.to_sym }
6
+
7
+ describe 'after_intialize' do
8
+ context 'new record' do
9
+ it :true do
10
+ expect(build(klass)).to be_deletable
11
+ end
12
+
13
+ it :false do
14
+ expect(build(klass, deletable: false)).not_to be_deletable
15
+ end
16
+ end
17
+
18
+ context 'existing record after create' do
19
+ it :false do
20
+ instance = create(klass, deletable: false)
21
+ expect(instance).not_to be_deletable
22
+ end
23
+ end
24
+
25
+ context 'existing record after save' do
26
+ it :false do
27
+ instance = create(klass)
28
+ instance.update_attribute :deletable, false
29
+ expect(instance).not_to be_deletable
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,29 @@
1
+ require 'rails_helper'
2
+
3
+ shared_examples_for :draggable do
4
+ let(:model) { described_class }
5
+ let(:klass) { model.to_s.underscore.to_sym }
6
+
7
+ describe 'after_intialize' do
8
+ context 'new record' do
9
+ it :true do
10
+ expect(build(klass)).to be_draggable
11
+ end
12
+ end
13
+
14
+ context 'existing record after create' do
15
+ it :false do
16
+ instance = create(klass, draggable: false)
17
+ expect(instance).not_to be_draggable
18
+ end
19
+ end
20
+
21
+ context 'existing record after save' do
22
+ it :false do
23
+ instance = create(klass)
24
+ instance.update_attribute :draggable, false
25
+ expect(instance).not_to be_draggable
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,7 @@
1
+ require 'rails_helper'
2
+
3
+ shared_examples_for :emailable do
4
+ it '#respond_to?' do
5
+ expect(described_class.new).to respond_to(:email_vars)
6
+ end
7
+ end
@@ -0,0 +1,22 @@
1
+ require 'rails_helper'
2
+
3
+ shared_examples_for :flexible_content do
4
+ let(:model) { described_class }
5
+ let(:klass) { model.to_s.underscore.to_sym }
6
+
7
+ it '.flexible_content?' do
8
+ expect(model.flexible_content?).to be true
9
+ end
10
+
11
+ it '.respond_to?' do
12
+ expect(model).to respond_to(
13
+ :flexible_content?
14
+ )
15
+ end
16
+
17
+ it '#respond_to?' do
18
+ expect(model.new).to respond_to(
19
+ :content_rows
20
+ )
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ require 'rails_helper'
2
+
3
+ shared_examples_for :imageable do
4
+ let(:model) { described_class }
5
+ let(:klass) { model.to_s.underscore.to_sym }
6
+ let(:instance) { create(klass) }
7
+
8
+ it '#imageable?' do
9
+ expect(instance).to be_imageable
10
+ end
11
+
12
+ it '#respond_to?' do
13
+ expect(model.new).to respond_to(:images, :imageable?)
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ require 'rails_helper'
2
+
3
+ shared_examples_for :locale do
4
+ let(:model) { described_class }
5
+ let(:klass) { model.to_s.underscore.to_sym }
6
+
7
+ describe 'scopes' do
8
+ it '.by_locale' do
9
+ a = create(klass, locale: 'nl')
10
+ b = create(klass, locale: 'fr')
11
+ c = create(klass, locale: 'en')
12
+
13
+ expect(model.by_locale(:nl)).to eq [a]
14
+ expect(model.unscoped.by_locale(%w(nl fr)).order(:id)).to eq [a, b]
15
+ end
16
+ end
17
+
18
+ it '.respond_to?' do
19
+ expect(model).to respond_to(:by_locale)
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ require 'rails_helper'
2
+
3
+ shared_examples_for :loggable do
4
+ let(:model) { described_class }
5
+ let(:klass) { model.to_s.underscore.to_sym }
6
+
7
+ it :logs do
8
+ a = create(klass)
9
+ a.log 'foo', category: 'bar', data: { baz: true }
10
+ expect(a.logs.count).to be >= 1
11
+ expect(a.logs.unscoped.last.content).to eq 'foo'
12
+ expect(a.logs.unscoped.last.category).to eq 'bar'
13
+ expect(a.logs.unscoped.last.data).to eq({ baz: true })
14
+ end
15
+
16
+ it '#respond_to?' do
17
+ expect(model.new).to respond_to(:logs, :log)
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ require 'rails_helper'
2
+
3
+ shared_examples_for :notable do
4
+ let(:model) { described_class }
5
+ let(:klass) { model.to_s.underscore.to_sym }
6
+
7
+ it '#respond_to?' do
8
+ expect(model.new).to respond_to(:notes)
9
+ end
10
+ end
@@ -0,0 +1,73 @@
1
+ require 'rails_helper'
2
+
3
+ shared_examples_for :parentable do
4
+ let(:model) { described_class }
5
+ let(:klass) { model.to_s.underscore.to_sym }
6
+
7
+ let(:a) { create(klass) }
8
+ let(:b) { create(klass, parent: a) }
9
+ let(:c) { create(klass, parent: a) }
10
+
11
+ it '#children' do
12
+ expect(a.children).to eq [b, c]
13
+ end
14
+
15
+ it '#parent' do
16
+ expect(b.parent).to eq a
17
+ end
18
+
19
+ describe '#depth' do
20
+ let(:d) { create(klass, parent_id: b.id) }
21
+
22
+ it(:naught) { expect(a.depth).to eq 0 }
23
+ it(:one) { expect(b.depth).to eq 1 }
24
+ it(:two) { expect(d.depth).to eq 2 }
25
+ end
26
+
27
+ describe '#parentable?' do
28
+ it(:true) { expect(a.parentable?).to be true }
29
+ end
30
+
31
+ describe 'parents' do
32
+ it :basic do
33
+ expect(b.parents).to eq [b, a]
34
+ end
35
+
36
+ it :include_self do
37
+ expect(b.parents(include_self: false)).to eq [a]
38
+ end
39
+ end
40
+
41
+ describe '.flat_tree' do
42
+ context 'no records' do
43
+ it :empty do
44
+ expect(model.flat_tree).to eq []
45
+ end
46
+ end
47
+
48
+ context 'records' do
49
+ it :results do
50
+ articles = create(klass)
51
+ blog = create(klass)
52
+ blog_archive = create(klass, parent: blog)
53
+ blog_archive_current_year = create(klass, parent: blog_archive)
54
+ blog_archive_last_year = create(klass, parent: blog_archive)
55
+ blog_tags = create(klass, parent: blog)
56
+ articles_comments = create(klass, parent: articles)
57
+
58
+ expect(model.flat_tree).to eq [
59
+ articles, articles_comments, blog, blog_archive,
60
+ blog_archive_current_year, blog_archive_last_year, blog_tags
61
+ ]
62
+ end
63
+ end
64
+ end
65
+
66
+ it '#respond_to?' do
67
+ expect(model.new).to respond_to(:children, :parent, :depth, :parentable?, :parents)
68
+ end
69
+
70
+ it '.respond_to' do
71
+ expect(model).to respond_to(:flat_tree)
72
+ end
73
+ end
@@ -0,0 +1,15 @@
1
+ require 'rails_helper'
2
+
3
+ shared_examples_for :person do
4
+ let(:model) { described_class }
5
+ let(:klass) { model.to_s.underscore.to_sym }
6
+
7
+ it :full_name do
8
+ person = build(klass, first_name: 'Foo', last_name: 'Bar')
9
+ expect(person.full_name).to eq 'Foo Bar'
10
+ end
11
+
12
+ it '#respond_to?' do
13
+ expect(model.new).to respond_to(:full_name)
14
+ end
15
+ end
@@ -0,0 +1,41 @@
1
+ require 'rails_helper'
2
+
3
+ shared_examples_for :publishable do
4
+ let(:model) { described_class }
5
+ let(:klass) { model.to_s.underscore.to_sym }
6
+ let(:instance) { build(klass) }
7
+
8
+ describe 'scopes' do
9
+ before(:each) do
10
+ @a = create(klass, published_at: 3.days.from_now)
11
+ @b = create(klass, published_at: 1.day.ago)
12
+ @c = create(klass, published_at: 3.days.ago)
13
+ end
14
+
15
+ it(:published) { expect(model.published).to eq [@b, @c] }
16
+ it(:not_published) { expect(model.not_published).to eq [@a] }
17
+ end
18
+
19
+ describe 'defaults' do
20
+ it(:published_at) { expect(instance).not_to be_published }
21
+ end
22
+
23
+ it '#publish!' do
24
+ instance.publish!
25
+ expect(instance).to be_published
26
+ end
27
+
28
+ it '#unpublish!' do
29
+ instance.published_at = Time.zone.now
30
+ instance.unpublish!
31
+ expect(instance).not_to be_unpublished
32
+ end
33
+
34
+ it '.respond_to?' do
35
+ expect(model).to respond_to(:published, :not_published)
36
+ end
37
+
38
+ it '#respond_to?' do
39
+ expect(model.new).to respond_to(:publish!, :unpublish!, :published?)
40
+ end
41
+ end
@@ -0,0 +1,130 @@
1
+ require 'rails_helper'
2
+
3
+ shared_examples_for :searchable do
4
+ let(:klass) { described_class.to_s.underscore.to_sym }
5
+
6
+ describe 'after_save' do
7
+ context 'non-translatable model' do
8
+ before(:each) do
9
+ # Because this is a spec/support included through it_behaves_like,
10
+ # we have to mock the entry points of the data used in
11
+ # Concerns::Searchable.
12
+ allow(described_class).to receive(:searchable_fields_list) { [:foo] }
13
+ allow(described_class).to receive(:translatable_fields_list) { [] }
14
+ allow_any_instance_of(described_class).to receive(:foo) { 'bar' }
15
+ end
16
+
17
+ it 'default' do
18
+ instance = build(klass)
19
+ expect(instance.search_indices).to eq []
20
+ end
21
+
22
+ it 'creates a search index' do
23
+ instance = create(klass)
24
+ expect(instance.search_indices.find_by(locale: :nl, name: 'foo').value).to eq 'bar'
25
+ end
26
+
27
+ it 'saves an existing search index' do
28
+ instance = create(klass)
29
+ allow(instance).to receive(:foo) { 'foobar' }
30
+ instance.save!
31
+ expect(instance.search_indices.find_by(locale: :nl, name: 'foo').value).to eq 'foobar'
32
+ end
33
+ end
34
+
35
+ if described_class.respond_to?(:translatable_fields)
36
+ before(:each) do
37
+ allow(described_class).to receive(:searchable_fields_list) { [:foo] }
38
+ end
39
+
40
+ context 'translatable model' do
41
+ before(:each) do
42
+ described_class.translatable_fields :foo, :bar
43
+ end
44
+
45
+ let(:create_instance!) do
46
+ instance = build(klass)
47
+ Udongo.config.i18n.app.locales.each do |l|
48
+ t = instance.translation(l.to_sym)
49
+ t.foo = "baz #{l}"
50
+ t.bar = 'bak'
51
+ end
52
+ instance.save
53
+ instance
54
+ end
55
+
56
+ it 'creates search indices for every translation' do
57
+ instance = create_instance!
58
+ expect(instance.search_indices.find_by(locale: :nl, name: 'foo').value).to eq 'baz nl'
59
+ expect(instance.search_indices.find_by(locale: :fr, name: 'foo').value).to eq 'baz fr'
60
+ expect(instance.search_indices.find_by(locale: :en, name: 'foo').value).to eq 'baz en'
61
+ end
62
+
63
+ it 'saves existing search indices for every translation' do
64
+ instance = create_instance!
65
+
66
+ Udongo.config.i18n.app.locales.each do |l|
67
+ t = instance.translation(l.to_sym)
68
+ t.foo = "foobar #{l}"
69
+ t.bar = 'barfoo'
70
+ end
71
+
72
+ instance.save
73
+
74
+ expect(instance.search_indices.find_by(locale: :nl, name: 'foo').value).to eq 'foobar nl'
75
+ expect(instance.search_indices.find_by(locale: :fr, name: 'foo').value).to eq 'foobar fr'
76
+ expect(instance.search_indices.find_by(locale: :en, name: 'foo').value).to eq 'foobar en'
77
+ end
78
+
79
+ it 'does not copy translatable fields that are not in searchable fields' do
80
+ instance = create_instance!
81
+ expect(instance.search_indices.find_by(locale: :nl, name: 'bar')).to be nil
82
+ end
83
+ end
84
+ end
85
+
86
+ if described_class.respond_to?(:content_rows)
87
+ context 'model with flexible_content' do
88
+ let(:instance) { create(klass) }
89
+ let(:content) { create(:content_text, content: 'Lorem ipsum') }
90
+
91
+ before(:each) do
92
+ # Because this is a spec/support included through it_behaves_like,
93
+ # we have to mock the entry points of the data used in
94
+ # Concerns::Searchable.
95
+ allow(described_class).to receive(:searchable_fields_list) { [:foo] }
96
+ allow_any_instance_of(described_class).to receive(:foo) { 'bar' }
97
+ allow_any_instance_of(Concerns::Storable::Collection).to receive(:foo) { 'bar' }
98
+
99
+ row = create(:content_row, locale: 'nl', rowable: instance)
100
+ row.columns << create(:content_column, content: content)
101
+ instance.content_rows << row
102
+ end
103
+
104
+ it 'saves index when updating searchable instance' do
105
+ instance.save!
106
+ key = "flexible_content:#{content.id}"
107
+ expect(instance.search_indices.find_by(locale: :nl, name: key).value).to eq 'Lorem ipsum'
108
+ end
109
+
110
+ it 'saves index when updating flexible content' do
111
+ instance.save!
112
+ content.content = 'Dolor sit amet'
113
+ content.save!
114
+ key = "flexible_content:#{content.id}"
115
+ expect(instance.search_indices.find_by(locale: :nl, name: key).value).to eq 'Dolor sit amet'
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ it '.respond_to?' do
122
+ expect(described_class).to respond_to(
123
+ :searchable_field, :searchable_fields, :searchable_fields_list
124
+ )
125
+ end
126
+
127
+ it '#respond_to?' do
128
+ expect(described_class.new).to respond_to(:search_indices)
129
+ end
130
+ end