izi_json_ld 1.0.0 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/app/entities/aggregate_rating_entity.rb +3 -1
  3. data/app/entities/application_entity.rb +54 -7
  4. data/app/entities/article_entity.rb +16 -0
  5. data/app/entities/breadcrumb_list_entity.rb +7 -0
  6. data/app/entities/image_object_entity.rb +6 -0
  7. data/app/entities/list_item_entity.rb +9 -0
  8. data/app/entities/offer_entity.rb +2 -1
  9. data/app/entities/organization_entity.rb +10 -0
  10. data/app/entities/person_entity.rb +14 -0
  11. data/app/entities/product_entity.rb +2 -2
  12. data/app/entities/rating_entity.rb +1 -1
  13. data/app/entities/review_entity.rb +3 -3
  14. data/app/entities/web_page_entity.rb +11 -0
  15. data/app/helpers/json_ld_helper.rb +7 -0
  16. data/lib/izi_json_ld/types.rb +3 -0
  17. data/lib/izi_json_ld/types/date_time.rb +11 -0
  18. data/lib/izi_json_ld/types/one_or_more.rb +13 -0
  19. data/lib/izi_json_ld/version.rb +1 -1
  20. data/spec/dummy/config/application.rb +5 -1
  21. data/spec/dummy/config/environments/test.rb +9 -7
  22. data/spec/dummy/db/development.sqlite3 +0 -0
  23. data/spec/dummy/db/test.sqlite3 +0 -0
  24. data/spec/dummy/log/development.log +16 -0
  25. data/spec/dummy/log/test.log +13419 -0
  26. data/spec/entities/aggregate_rating_entity_spec.rb +2 -1
  27. data/spec/entities/article_entity_spec.rb +113 -0
  28. data/spec/entities/breadcrumb_list_entity_spec.rb +55 -0
  29. data/spec/entities/product_entity_spec.rb +52 -0
  30. data/spec/rails_helper.rb +1 -17
  31. metadata +18 -24
  32. data/app/entities/service_entity.rb +0 -10
  33. data/app/helpers/critical_helper.rb +0 -118
  34. data/spec/dummy/Gemfile +0 -14
  35. data/spec/dummy/Gemfile.lock +0 -229
  36. data/spec/dummy/app/models/application_record.rb +0 -3
  37. data/spec/dummy/bin/_guard-core +0 -29
  38. data/spec/dummy/bin/guard +0 -29
  39. data/spec/dummy/bin/rails +0 -4
  40. data/spec/dummy/bin/rake +0 -4
  41. data/spec/dummy/bin/rspec +0 -29
  42. data/spec/dummy/bin/setup +0 -33
  43. data/spec/entities/service_entity_spec.rb +0 -62
@@ -23,7 +23,8 @@ RSpec.describe AggregateRatingEntity do
23
23
 
24
24
  [
25
25
  ['bestRating', 5],
26
- ['worstRating', 1]
26
+ ['worstRating', 1],
27
+ ['reviewCount', 1]
27
28
  ].each do |(field, value)|
28
29
  it "should have default #{field}" do
29
30
  expect(item.as_json).to have_key field
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe ArticleEntity do
4
+ let(:item) do
5
+ described_class.new(
6
+ headline: 'Test Article Title',
7
+ mainEntityOfPage: {
8
+ web_page: 'https://exampla.com/blog/page'
9
+ }
10
+ )
11
+ end
12
+
13
+ it 'should be searializable as hash' do
14
+ expect(item.as_json).to be_present
15
+ end
16
+
17
+ it 'should have @type' do
18
+ expect(item.as_json).to have_key '@type'
19
+ expect(item.as_json['@type']).to be_present
20
+ expect(item.as_json['@type']).to eq 'Article'
21
+ end
22
+
23
+ it 'should have @context at root entity' do
24
+ expect(item.as_json).to have_key '@context'
25
+ expect(item.as_json['@context']).to be_present
26
+ expect(item.as_json['@context']).to include 'schema.org'
27
+ end
28
+
29
+ it 'possible to set mainEntityOfPage as URL' do
30
+ custom = described_class.new(headline: 'Title', mainEntityOfPage: 'https://exampla.com/blog/page')
31
+ expect(custom.as_json.dig('mainEntityOfPage')).to be_present
32
+ expect(custom.as_json.dig('mainEntityOfPage')).to eq 'https://exampla.com/blog/page'
33
+ end
34
+
35
+ context 'filled Article' do
36
+ let(:item) do
37
+ described_class.new(
38
+ mainEntityOfPage: {
39
+ web_page: 'https://exampla.com/blog/page'
40
+ },
41
+ headline: 'Test Article Title',
42
+ description: 'Test Article Description',
43
+ image: 'https://exampla.com/blog/page.png',
44
+ author: { name: 'TestUser' },
45
+ publisher: {
46
+ name: 'TestInc',
47
+ logo: { url: 'https://exampla.com/logo.png' }
48
+ },
49
+ datePublished: 1.week.ago,
50
+ dateModified: 2.days.ago
51
+ )
52
+ end
53
+
54
+ it 'should not fail' do
55
+ expect do
56
+ item.dump
57
+ item.as_json
58
+ item.to_json
59
+ end.not_to raise_exception
60
+ end
61
+
62
+ [
63
+ ['Publisher name', %w[publisher name], 'TestInc'],
64
+ ['Main Entity id', %w[mainEntityOfPage @id], 'https://exampla.com/blog/page'],
65
+ ['Publisher type', %w[publisher @type], 'Organization'],
66
+ ['Publisher logo type', %w[publisher logo @type], 'ImageObject'],
67
+ ['Publisher logo', %w[publisher logo url], 'https://exampla.com/logo.png'],
68
+ ['Post image', %w[image], 'https://exampla.com/blog/page.png'],
69
+ ['Author as org', %w[author @type], 'Organization'],
70
+ ['Author name', %w[author name], 'TestUser']
71
+ ].each do |(desc, path, val)|
72
+ it "should render #{desc}[#{path.join('.')}]" do
73
+ expect(item.as_json.dig(*path)).to be_present
74
+ expect(item.as_json.dig(*path)).to eq val
75
+ end
76
+ end
77
+ end
78
+
79
+ context 'with author as a Person' do
80
+ let(:item) do
81
+ described_class.new(
82
+ mainEntityOfPage: {
83
+ web_page: 'https://exampla.com/blog/page'
84
+ },
85
+ headline: 'Test Article Title',
86
+ publisher: {
87
+ person_name: 'SiteOwner',
88
+ email: 'site.owner@example.com',
89
+ image: 'owner.png'
90
+ },
91
+ author: {
92
+ person_name: 'TestBlogger',
93
+ image: 'blogger.png'
94
+ }
95
+ )
96
+ end
97
+
98
+ [
99
+ ['Publisher as person', %w[publisher @type], 'Person'],
100
+ ['Publisher name', %w[publisher name], 'SiteOwner'],
101
+ ['Publisher email', %w[publisher email], 'site.owner@example.com'],
102
+ ['Publisher image', %w[publisher image], 'owner.png'],
103
+ ['Author as person', %w[author @type], 'Person'],
104
+ ['Author name', %w[author name], 'TestBlogger'],
105
+ ['Author image', %w[author image], 'blogger.png']
106
+ ].each do |(desc, path, val)|
107
+ it "should render #{desc}[#{path.join('.')}]" do
108
+ expect(item.as_json.dig(*path)).to be_present
109
+ expect(item.as_json.dig(*path)).to eq val
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe BreadcrumbListEntity do
4
+ let(:item) { described_class.new(itemListElement: []) }
5
+
6
+ it 'should be searializable as hash' do
7
+ expect(item.as_json).to be_present
8
+ end
9
+
10
+ it 'should have @type' do
11
+ expect(item.as_json).to have_key '@type'
12
+ expect(item.as_json['@type']).to be_present
13
+ expect(item.as_json['@type']).to eq 'BreadcrumbList'
14
+ end
15
+
16
+ it 'should have @context at root entity' do
17
+ expect(item.as_json).to have_key '@context'
18
+ expect(item.as_json['@context']).to be_present
19
+ expect(item.as_json['@context']).to include 'schema.org'
20
+ end
21
+
22
+ context 'with simple item' do
23
+ let(:item) do
24
+ described_class.new(itemListElement: { name: 'Home', position: 0, item: 'https://example.com' })
25
+ end
26
+
27
+ it 'should not fail' do
28
+ expect do
29
+ item.dump
30
+ item.as_json
31
+ item.to_json
32
+ end.not_to raise_exception
33
+ end
34
+ end
35
+
36
+ context 'with multiple items' do
37
+ let(:item) do
38
+ described_class.new(
39
+ itemListElement: [
40
+ { name: 'Home', position: 0, item: 'https://example.com' },
41
+ { name: 'Blog', position: 1, item: 'https://example.com/blog' },
42
+ { name: 'Post', position: 2, item: 'https://example.com/blog/post' },
43
+ ]
44
+ )
45
+ end
46
+
47
+ it 'should not fail' do
48
+ expect do
49
+ item.dump
50
+ item.as_json
51
+ item.to_json
52
+ end.not_to raise_exception
53
+ end
54
+ end
55
+ end
@@ -35,6 +35,12 @@ RSpec.describe ProductEntity do
35
35
  expect(item.as_json['aggregateRating']).to be_present
36
36
  expect(item.as_json.dig('aggregateRating', 'ratingValue')).to eq 4
37
37
  end
38
+
39
+ it 'should have aggregateRating @type' do
40
+ expect(item.as_json).to have_key 'aggregateRating'
41
+ expect(item.as_json['aggregateRating']).to be_present
42
+ expect(item.as_json.dig('aggregateRating', '@type')).to eq 'AggregateRating'
43
+ end
38
44
  end
39
45
 
40
46
  context 'with offers' do
@@ -45,5 +51,51 @@ RSpec.describe ProductEntity do
45
51
  expect(item.as_json['offers']).to be_present
46
52
  expect(item.as_json.dig('offers', 'url')).to eq 'test'
47
53
  end
54
+
55
+ it 'offers should have right @type' do
56
+ expect(item.as_json).to have_key 'offers'
57
+ expect(item.as_json['offers']).to be_present
58
+ expect(item.as_json.dig('offers', '@type')).to eq 'Offer'
59
+ end
60
+ end
61
+
62
+ context 'with review' do
63
+ let(:review) { { author: 'test', reviewRating: { ratingValue: 2 } } }
64
+ let(:reviews) do
65
+ [
66
+ { author: 'test', reviewRating: { ratingValue: 2 } },
67
+ { author: 'test2', reviewRating: { ratingValue: 3 } },
68
+ { author: 'test3', reviewRating: { ratingValue: 4 } }
69
+ ]
70
+ end
71
+
72
+ it 'should allow create product with single review' do
73
+ data = described_class.new(name: 'Test', review: review)
74
+ expect(data.to_json).to be_present
75
+ expect(data.as_json['review']).to be_present
76
+ expect(data.as_json['review'].class.name).to eq 'Hash'
77
+ end
78
+
79
+ it 'should allow create product with single review' do
80
+ data = described_class.new(name: 'Test', review: reviews)
81
+ expect(data.to_json).to be_present
82
+ expect(data.as_json['review']).to be_present
83
+ expect(data.as_json['review'].class.name).to eq 'Array'
84
+ end
85
+
86
+ it 'review & reviewRating should contain right @type' do
87
+ data = described_class.new(name: 'Test', review: reviews)
88
+ expect(data.as_json.dig('review', 0, '@type')).to eq 'Review'
89
+ expect(data.as_json.dig('review', 0, 'reviewRating', '@type')).to eq 'Rating'
90
+ end
91
+
92
+ it 'ensure proper serialization' do
93
+ data = described_class.new(name: 'Test', review: reviews)
94
+ raw = data.to_json
95
+ parsed = JSON.parse(raw)
96
+ expect(parsed).to be_present
97
+ expect(parsed.dig('review', 0, '@type')).to eq 'Review'
98
+ expect(parsed.dig('review', 0, 'reviewRating', '@type')).to eq 'Rating'
99
+ end
48
100
  end
49
101
  end
data/spec/rails_helper.rb CHANGED
@@ -22,25 +22,9 @@ require 'rspec/rails'
22
22
  #
23
23
  # Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }
24
24
 
25
- # Checks for pending migrations and applies them before tests are run.
26
- # If you are not using ActiveRecord, you can remove these lines.
27
- begin
28
- ActiveRecord::Migration.maintain_test_schema!
29
- rescue ActiveRecord::PendingMigrationError => e
30
- puts e.to_s.strip
31
- exit 1
32
- end
33
25
  RSpec.configure do |config|
34
- # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
35
- config.fixture_path = "#{::Rails.root}/spec/fixtures"
36
-
37
- # If you're not using ActiveRecord, or you'd prefer not to run each of your
38
- # examples within a transaction, remove the following line or assign false
39
- # instead of true.
40
- config.use_transactional_fixtures = true
41
-
42
26
  # You can uncomment this line to turn off ActiveRecord support entirely.
43
- # config.use_active_record = false
27
+ config.use_active_record = false
44
28
 
45
29
  # RSpec Rails can automatically mix in different behaviours to your tests
46
30
  # based on their file location, for example enabling you to call `get` and
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: izi_json_ld
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - IzikAJ
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-15 00:00:00.000000000 Z
11
+ date: 2021-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-struct
@@ -49,31 +49,30 @@ files:
49
49
  - README.rdoc
50
50
  - app/entities/aggregate_rating_entity.rb
51
51
  - app/entities/application_entity.rb
52
+ - app/entities/article_entity.rb
53
+ - app/entities/breadcrumb_list_entity.rb
54
+ - app/entities/image_object_entity.rb
55
+ - app/entities/list_item_entity.rb
52
56
  - app/entities/offer_entity.rb
57
+ - app/entities/organization_entity.rb
58
+ - app/entities/person_entity.rb
53
59
  - app/entities/product_entity.rb
54
60
  - app/entities/rating_entity.rb
55
61
  - app/entities/review_entity.rb
56
- - app/entities/service_entity.rb
57
- - app/helpers/critical_helper.rb
62
+ - app/entities/web_page_entity.rb
63
+ - app/helpers/json_ld_helper.rb
58
64
  - lib/izi_json_ld.rb
59
65
  - lib/izi_json_ld/engine.rb
60
66
  - lib/izi_json_ld/extentions/autoload_paths.rb
61
67
  - lib/izi_json_ld/types.rb
68
+ - lib/izi_json_ld/types/date_time.rb
69
+ - lib/izi_json_ld/types/one_or_more.rb
62
70
  - lib/izi_json_ld/version.rb
63
- - spec/dummy/Gemfile
64
- - spec/dummy/Gemfile.lock
65
71
  - spec/dummy/Rakefile
66
72
  - spec/dummy/app/assets/config/manifest.js
67
73
  - spec/dummy/app/controllers/application_controller.rb
68
74
  - spec/dummy/app/helpers/application_helper.rb
69
- - spec/dummy/app/models/application_record.rb
70
75
  - spec/dummy/app/views/layouts/application.html.erb
71
- - spec/dummy/bin/_guard-core
72
- - spec/dummy/bin/guard
73
- - spec/dummy/bin/rails
74
- - spec/dummy/bin/rake
75
- - spec/dummy/bin/rspec
76
- - spec/dummy/bin/setup
77
76
  - spec/dummy/config.ru
78
77
  - spec/dummy/config/application.rb
79
78
  - spec/dummy/config/boot.rb
@@ -89,16 +88,18 @@ files:
89
88
  - spec/dummy/config/initializers/wrap_parameters.rb
90
89
  - spec/dummy/config/puma.rb
91
90
  - spec/dummy/config/routes.rb
91
+ - spec/dummy/db/development.sqlite3
92
92
  - spec/dummy/db/test.sqlite3
93
93
  - spec/dummy/log/development.log
94
94
  - spec/dummy/log/test.log
95
95
  - spec/dummy/tmp/development_secret.txt
96
96
  - spec/entities/aggregate_rating_entity_spec.rb
97
+ - spec/entities/article_entity_spec.rb
98
+ - spec/entities/breadcrumb_list_entity_spec.rb
97
99
  - spec/entities/offer_entity_spec.rb
98
100
  - spec/entities/product_entity_spec.rb
99
101
  - spec/entities/rating_entity_spec.rb
100
102
  - spec/entities/review_entity_spec.rb
101
- - spec/entities/service_entity_spec.rb
102
103
  - spec/rails_helper.rb
103
104
  - spec/spec_helper.rb
104
105
  homepage: https://bitbucket.org/netfixllc/izi_json_ld
@@ -126,17 +127,10 @@ specification_version: 4
126
127
  summary: Izi Lightup
127
128
  test_files:
128
129
  - spec/spec_helper.rb
129
- - spec/dummy/app/models/application_record.rb
130
130
  - spec/dummy/app/controllers/application_controller.rb
131
131
  - spec/dummy/app/views/layouts/application.html.erb
132
132
  - spec/dummy/app/assets/config/manifest.js
133
133
  - spec/dummy/app/helpers/application_helper.rb
134
- - spec/dummy/bin/rake
135
- - spec/dummy/bin/setup
136
- - spec/dummy/bin/_guard-core
137
- - spec/dummy/bin/guard
138
- - spec/dummy/bin/rspec
139
- - spec/dummy/bin/rails
140
134
  - spec/dummy/config/routes.rb
141
135
  - spec/dummy/config/environments/production.rb
142
136
  - spec/dummy/config/environments/development.rb
@@ -154,15 +148,15 @@ test_files:
154
148
  - spec/dummy/config.ru
155
149
  - spec/dummy/Rakefile
156
150
  - spec/dummy/db/test.sqlite3
157
- - spec/dummy/Gemfile
151
+ - spec/dummy/db/development.sqlite3
158
152
  - spec/dummy/log/test.log
159
153
  - spec/dummy/log/development.log
160
- - spec/dummy/Gemfile.lock
161
154
  - spec/dummy/tmp/development_secret.txt
162
155
  - spec/rails_helper.rb
163
156
  - spec/entities/aggregate_rating_entity_spec.rb
164
- - spec/entities/service_entity_spec.rb
165
157
  - spec/entities/review_entity_spec.rb
158
+ - spec/entities/breadcrumb_list_entity_spec.rb
166
159
  - spec/entities/rating_entity_spec.rb
167
160
  - spec/entities/offer_entity_spec.rb
161
+ - spec/entities/article_entity_spec.rb
168
162
  - spec/entities/product_entity_spec.rb
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class ServiceEntity < ApplicationEntity
4
- extra '@context', 'http://www.schema.org'
5
- extra '@type', 'Service'
6
-
7
- attribute :name, ::IziJsonLd::Types::String
8
- attribute? :review, ::ReviewEntity.optional
9
- attribute? :reviews, ::IziJsonLd::Types::Array.of(::ReviewEntity).optional
10
- end
@@ -1,118 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CriticalHelper
4
- def css_preload(names)
5
- return '' if names.blank?
6
- names = Array.wrap(names)
7
-
8
- link = stylesheet_link_tag(*names, rel: 'preload', as: 'style', onload: 'this.rel="stylesheet"')
9
- noscript = content_tag :noscript do
10
- stylesheet_link_tag(*names)
11
- end
12
-
13
- link + noscript
14
- end
15
-
16
- def inline_js(name)
17
- IziJsonLd::InlineAsset.inline_js(name).html_safe
18
- end
19
-
20
- def inline_css(name)
21
- raw = IziJsonLd::InlineAsset.inline_css(name).html_safe
22
- raw = "<!-- CRIT CSS: #{name} -->".html_safe + raw if Rails.env.development?
23
- raw
24
- end
25
-
26
- def critical_js
27
- inline_js('crit-utils/bundle.js').presence || '<!-- CRIT JS NOT FOUND! -->'.html_safe
28
- end
29
-
30
- def critical_css(params = {})
31
- scope = params.fetch(:scope, 'critical')
32
- name = find_scoped_css(scope)
33
- stylesheets = Array.wrap(params.fetch(:stylesheets, []))
34
- data = StringIO.new
35
-
36
- if name.blank?
37
- # insert stylesheets directly if not crit css
38
- data << '<!-- CRIT CSS NOT FOUND! -->'
39
- data << stylesheet_link_tag(*stylesheets) if stylesheets.present?
40
- else
41
- data << inline_css(name)
42
- data << css_preload(stylesheets) if stylesheets.present?
43
- data << inline_js('crit-utils/measure.js') if Rails.env.development?
44
- end
45
-
46
- data.string.html_safe
47
- end
48
-
49
- def smart_picture(object, *args, **xargs, &block)
50
- return '' if object.blank?
51
-
52
- IziJsonLd::SmartPicture.render(object, *args, **xargs, &block)
53
- end
54
-
55
- def debug_critical_css(scope_name = 'critical')
56
- {
57
- lookup: scoped_css_files(scope_name),
58
- found: find_scoped_css(scope_name)
59
- }.to_json.html_safe
60
- end
61
-
62
- private
63
-
64
- def scoped_css_files(scope_name)
65
- [
66
- "#{controller_path}_#{action_name}",
67
- controller_path,
68
- *scoped_namespace_file(scope_name),
69
- "#{controller_name}_#{action_name}",
70
- controller_name,
71
- scope_name
72
- ].compact.uniq.map { |l| File.join(scope_name, "#{l}.css") }
73
- end
74
-
75
- def scoped_namespace_file(scope_name)
76
- scopes = []
77
- return scopes if controller_path == controller_name
78
-
79
- parts = controller_path.gsub(/\/#{controller_name}$/, '').split('/').reject(&:blank?)
80
- parts.each { |p| scopes.unshift(File.join([scopes.first, p].compact)) }
81
-
82
- scopes.uniq
83
- end
84
-
85
- def find_scoped_css(scope_name)
86
- scoped_css_files(scope_name).detect { |n| asset_exist?(n) }
87
- end
88
-
89
- def fetch_items(object, fields)
90
- Array.wrap(fields).map do |name|
91
- object.respond_to?(name) ? object.public_send(name) : nil
92
- end.compact
93
- end
94
-
95
- def asset_exist?(name)
96
- return find_asset_source(name)&.present? if Rails.env.development?
97
-
98
- manifest.assets.key?(name)
99
- end
100
-
101
- def find_asset_source(name)
102
- return find_sources_fallback(name) if old_manifest?
103
-
104
- manifest.find_sources(name)&.first
105
- end
106
-
107
- def find_sources_fallback(asset_path)
108
- Rails.application.assets.find_asset(asset_path)
109
- end
110
-
111
- def old_manifest?
112
- !manifest.respond_to?(:find_sources)
113
- end
114
-
115
- def manifest
116
- @manifest ||= Rails.application.assets_manifest
117
- end
118
- end