notion_rails 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ca08751cb4669bed3cd0ad791b3242ba6c2f873e3272310750beaa940eb17151
4
- data.tar.gz: fe85cdedfe168e670b5cd2588cb03d514347408ac24396e1dec60e3df8c2bdf6
3
+ metadata.gz: 95a025ff228890fafa2fd982d108512fa0980fbae091232c6fbfac6ae85667b8
4
+ data.tar.gz: 8bc99ae3b897b5116ed5e4fe91847a8160db7436490f76360d637c10128df487
5
5
  SHA512:
6
- metadata.gz: '052119acf124d0543367b3bbbc14d762eb08132428f3c1b92242b4069f8f3e8c9665f7fbd1db79f3e5a6915eec853531417853af72096fcd83e18e0fdf39669f'
7
- data.tar.gz: '09a7d8478a722c303e6f22782069dbf74ad83cad14a0949f1fe84453ba4e29dc3703a25de7ab31f4943b6258ab3f5fb471b421f638be2b97f8b780bdbc4533f5'
6
+ metadata.gz: bdd4412954a606cccf29b805a4db0c9f4ffcec32b9d1baf64feb7a7724fe48a2a72974e1e5650254bc86d0b5eb467961492c5e49284d1d3f9f7a62f31d072b0a
7
+ data.tar.gz: 426dee990473e7a39722e81e32f282cd3f3909156884e2a9dd46f64fff0ef4893a235ab8eff6921621603c445a6117932116961d370c423cb393fc88a145c171
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module NotionRails
2
4
  class BaseBlock
3
5
  include NotionRails::Renderers
@@ -58,11 +60,13 @@ module NotionRails
58
60
  when 'heading_2' then render_heading_2(rich_text, class: options[:heading_2])
59
61
  when 'heading_3' then render_heading_3(rich_text, class: options[:heading_3])
60
62
  when 'table_of_contents' then render_table_of_contents
61
- when 'bulleted_list_item' then render_bulleted_list_item(rich_text, @siblings, @children, class: options[:bulleted_list_item])
62
- when 'numbered_list_item' then render_numbered_list_item(rich_text, @siblings, @children, class: options[:numbered_list_item])
63
+ when 'bulleted_list_item'
64
+ render_bulleted_list_item(rich_text, @siblings, @children, class: options[:bulleted_list_item])
65
+ when 'numbered_list_item'
66
+ render_numbered_list_item(rich_text, @siblings, @children, class: options[:numbered_list_item])
63
67
  when 'quote' then render_quote(rich_text, class: options[:quote])
64
68
  when 'callout' then render_callout(rich_text, icon, class: options[:callout])
65
- when 'code' then render_code(rich_text, class: "#{options[:code]} language-#{@properties['language']}")
69
+ when 'code' then render_code(rich_text, class: "#{options[:code]} language-#{@properties["language"]}")
66
70
  when 'image' then render_image(*multi_media)
67
71
  when 'video' then render_video(*multi_media)
68
72
  else
@@ -92,7 +96,6 @@ module NotionRails
92
96
 
93
97
  private
94
98
 
95
- def render_table_of_contents
96
- end
99
+ def render_table_of_contents; end
97
100
  end
98
101
  end
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module NotionRails
2
4
  class BasePage
3
5
  include NotionRails::Renderers
6
+
4
7
  # TODO: validate object type is page
5
8
  attr_reader :id,
6
9
  :created_time,
@@ -12,9 +15,11 @@ module NotionRails
12
15
  :parent,
13
16
  :archived,
14
17
  :properties,
18
+ :published_at,
15
19
  :tags,
16
20
  :title,
17
21
  :slug,
22
+ :description,
18
23
  :url
19
24
 
20
25
  def initialize(data)
@@ -41,12 +46,22 @@ module NotionRails
41
46
  render_title(@title, options)
42
47
  end
43
48
 
49
+ def formatted_description(options = {})
50
+ render_paragraph(@description, options)
51
+ end
52
+
53
+ def formatted_published_at(options = {})
54
+ render_date(@published_at, options)
55
+ end
56
+
44
57
  private
45
58
 
46
59
  def process_properties
47
60
  @tags = @properties['tags']
48
61
  @title = @properties.dig('name', 'title')
49
62
  @slug = @properties['slug']
63
+ @published_at = @properties.dig('published', 'date', 'start')
64
+ @description = @properties.dig('description', 'rich_text')
50
65
  end
51
66
  end
52
67
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module NotionRails
2
4
  class Page
3
5
  include NotionRails::Renderers
@@ -5,14 +7,16 @@ module NotionRails
5
7
  attr_reader :metadata, :blocks
6
8
 
7
9
  delegate :formatted_title, to: :metadata
10
+ delegate :formatted_description, to: :metadata
11
+ delegate :formatted_published_at, to: :metadata
8
12
 
9
13
  def initialize(base_page, base_blocks)
10
- @metadata = base_page
11
- @blocks = base_blocks
12
- end
14
+ @metadata = base_page
15
+ @blocks = base_blocks
16
+ end
13
17
 
14
- def formatted_blocks(options = {})
15
- @blocks.map { |block| block.render(options) }
16
- end
18
+ def formatted_blocks(options = {})
19
+ @blocks.map { |block| block.render(options) }
20
+ end
17
21
  end
18
22
  end
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'action_view'
4
+
1
5
  module NotionRails
2
6
  module Renderers
3
7
  include ActionView::Helpers::AssetTagHelper
@@ -15,7 +19,7 @@ module NotionRails
15
19
  when 'code'
16
20
  'inline-code' if annotations[key]
17
21
  when 'color'
18
- "text-#{annotations['color']}-600" if annotations[key] != 'default'
22
+ "text-#{annotations["color"]}-600" if annotations[key] != 'default'
19
23
  else
20
24
  annotations[key] ? key : nil
21
25
  end
@@ -23,61 +27,65 @@ module NotionRails
23
27
  classes.compact.join(' ')
24
28
  end
25
29
 
26
- def text_renderer(properties)
30
+ def text_renderer(properties, options = {})
27
31
  properties.map do |rich_text|
28
32
  classes = annotation_to_css_class(rich_text['annotations'])
29
33
  if rich_text['href']
30
34
  link_to(
31
35
  rich_text['plain_text'],
32
36
  rich_text['href'],
33
- class: "link #{classes}"
37
+ class: "link #{classes} #{options[:class]}"
34
38
  )
35
39
  elsif classes.present?
36
- content_tag(:span, rich_text['plain_text'], class: classes)
40
+ content_tag(:span, rich_text['plain_text'], class: "#{classes} #{options[:class]}")
37
41
  else
38
- rich_text['plain_text']
42
+ tag.span(rich_text['plain_text'], class: options[:class])
39
43
  end
40
- end
44
+ end.join('').html_safe
41
45
  end
42
46
 
43
47
  def render_title(title, options = {})
44
- render_paragraph(title, options)
48
+ render_heading_1(title, options)
49
+ end
50
+
51
+ def render_date(date, options = {})
52
+ # TODO: handle end and time zone
53
+ # date=end=, start=2023-07-13, time_zone=, id=%5BsvU, type=date
54
+ tag.p(date.to_date.to_fs(:long), class: options[:class])
45
55
  end
46
56
 
47
57
  def render_paragraph(rich_text_array, options = {})
48
58
  content_tag(:p, options) do
49
- text_renderer(rich_text_array).join('').html_safe
59
+ text_renderer(rich_text_array)
50
60
  end
51
61
  end
52
62
 
53
63
  def render_heading_1(rich_text_array, options = {})
54
- content_tag(:h1, class: 'mb-4 mt-6 text-3xl font-semibold', **options) do
55
- text_renderer(rich_text_array).join('').html_safe
56
- end
64
+ content_tag(:h1, class: 'mb-4 mt-6 text-3xl font-semibold', **options) do
65
+ text_renderer(rich_text_array)
66
+ end
57
67
  end
58
68
 
59
69
  def render_heading_2(rich_text_array, options = {})
60
- content_tag(:h2, class: 'mb-4 mt-6 text-2xl font-semibold', **options) do
61
- text_renderer(rich_text_array).join('').html_safe
62
- end
70
+ content_tag(:h2, class: 'mb-4 mt-6 text-2xl font-semibold', **options) do
71
+ text_renderer(rich_text_array)
72
+ end
63
73
  end
64
74
 
65
75
  def render_heading_3(rich_text_array, options = {})
66
- content_tag(:h3, class: 'mb-2 mt-6 text-xl font-semibold', **options) do
67
- text_renderer(rich_text_array).join('').html_safe
68
- end
76
+ content_tag(:h3, class: 'mb-2 mt-6 text-xl font-semibold', **options) do
77
+ text_renderer(rich_text_array)
78
+ end
69
79
  end
70
80
 
71
81
  def render_code(rich_text_array, options = {})
72
82
  # TODO: render captions
73
83
  pre_options = options
74
- pre_options[:class] = "p-6 rounded #{pre_options[:class]}"
84
+ pre_options[:class] = "border-2 p-6 rounded #{pre_options[:class]}"
75
85
  content_tag(:div, class: 'mt-4', data: { controller: 'highlight' }) do
76
86
  content_tag(:div, data: { highlight_target: 'source' }) do
77
87
  content_tag(:pre, pre_options) do
78
- content_tag(:code, options) do
79
- text_renderer(rich_text_array).join('').html_safe
80
- end
88
+ text_renderer(rich_text_array, options)
81
89
  end
82
90
  end
83
91
  end
@@ -88,7 +96,7 @@ module NotionRails
88
96
  pre_options[:class] = "list-disc break-words #{pre_options[:class]}"
89
97
  content_tag(:ul, pre_options) do
90
98
  content = content_tag(:li, options) do
91
- text_renderer(rich_text_array).join('').html_safe
99
+ text_renderer(rich_text_array)
92
100
  end
93
101
  if children.present?
94
102
  res = children.map do |child|
@@ -110,13 +118,13 @@ module NotionRails
110
118
 
111
119
  def render_list_items(type, rich_text_array, siblings, children, options = {})
112
120
  content = content_tag(:li, options) do
113
- text_renderer(rich_text_array).join('').html_safe
121
+ text_renderer(rich_text_array)
114
122
  end
115
123
  if children.present?
116
- res = children.map do |child|
117
- render_numbered_list_item(child.rich_text, child.siblings, child.children)
118
- end
119
- content += res.join('').html_safe
124
+ res = children.map do |child|
125
+ render_numbered_list_item(child.rich_text, child.siblings, child.children)
126
+ end
127
+ content += res.join('').html_safe
120
128
  end
121
129
  if siblings.present?
122
130
  content += siblings.map do |sibling|
@@ -132,8 +140,10 @@ module NotionRails
132
140
  div_options[:class] = "mt-4 #{options[:class]}"
133
141
  content_tag(:div, div_options) do
134
142
  pre_options[:class] = "border-l-4 border-black px-5 py-1 #{options[:class]}"
135
- content_tag(:cite, pre_options) do
136
- text_renderer(rich_text_array).join('').html_safe
143
+ content_tag(:cite) do
144
+ content_tag(:p, pre_options) do
145
+ text_renderer(rich_text_array)
146
+ end
137
147
  end
138
148
  end
139
149
  end
@@ -143,15 +153,15 @@ module NotionRails
143
153
  pre_options[:class] = "p-4 rounded bg-neutral-200 mt-4 #{pre_options[:class]}"
144
154
  content_tag(:div, pre_options) do
145
155
  content = tag.span(icon, class: 'pr-2')
146
- content += text_renderer(rich_text_array).join('').html_safe
156
+ content += text_renderer(rich_text_array)
147
157
  content
148
158
  end
149
159
  end
150
160
 
151
161
  def render_image(src, expiry_time, caption, type, options = {})
152
162
  content_tag(:figure, options) do
153
- content = tag.img(src:, alt: '')
154
- content += tag.figcaption(text_renderer(caption).join('').html_safe)
163
+ content = tag.img(src: src, alt: '')
164
+ content += tag.figcaption(text_renderer(caption))
155
165
  content
156
166
  end
157
167
  end
@@ -159,11 +169,11 @@ module NotionRails
159
169
  def render_video(src, expiry_time, caption, type, options = {})
160
170
  content_tag(:figure, options) do
161
171
  content = if type == 'file'
162
- video_tag(src, controls: true)
163
- elsif type == 'external'
164
- tag.iframe(src:, allowfullscreen: true, class: 'w-full aspect-video')
165
- end
166
- content += tag.figcaption(text_renderer(caption).join('').html_safe)
172
+ video_tag(src, controls: true)
173
+ elsif type == 'external'
174
+ tag.iframe(src: src, allowfullscreen: true, class: 'w-full aspect-video')
175
+ end
176
+ content += tag.figcaption(text_renderer(caption))
167
177
  content
168
178
  end
169
179
  end
@@ -1,78 +1,77 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module NotionRails
2
4
  class Service
3
5
  def initialize
4
- @client = Notion::Client.new
6
+ @client = Notion::Client.new(token: NotionRails.config.notion_api_token)
5
7
  end
6
8
 
7
- def get_articles(tag: nil, slug: nil)
9
+ def default_query(tag: nil, slug: nil)
8
10
  query = [
9
11
  {
10
12
  property: 'public',
11
13
  checkbox: {
12
14
  equals: true
13
15
  }
14
- },
15
- {
16
+ }
17
+ ]
18
+
19
+ if slug
20
+ query.push({
16
21
  property: 'slug',
17
22
  rich_text: {
18
- is_not_empty: true
23
+ equals: slug
19
24
  }
20
- }
21
- ]
25
+ })
26
+ end
22
27
 
23
- query.push({
24
- property: 'slug',
25
- rich_text: {
26
- equals: slug,
27
- }
28
- }) if slug
28
+ if tag
29
+ query.push({
30
+ property: 'tags',
31
+ multi_select: {
32
+ contains: tag
33
+ }
34
+ })
35
+ end
29
36
 
30
- query.push({
31
- property: 'tags',
32
- multi_select: {
33
- contains: tag,
34
- }
35
- }) if tag
37
+ query
38
+ end
36
39
 
37
- pages = @client.database_query(
38
- database_id: Rails.application.credentials.notion.database_id,
39
- sorts: [
40
- {
41
- property: 'published',
42
- direction: 'descending'
43
- }
44
- ],
45
- filter: {
46
- 'and': query
47
- }
48
- )
49
- pages['results'].map { |page| Notion::BasePage.new(page) }
40
+ def default_sorting
41
+ {
42
+ property: 'published',
43
+ direction: 'descending'
44
+ }
45
+ end
46
+
47
+ def get_articles(tag: nil, slug: nil, page_size: 10)
48
+ __get_articles(tag: tag, slug: slug, page_size: page_size)['results'].map do |page|
49
+ NotionRails::BasePage.new(page)
50
+ end
50
51
  end
51
52
 
52
53
  def get_article(id)
53
- base_page = Notion::BasePage.new(@client.page(page_id: id))
54
- base_blocks = get_blocks(id)
55
- Notion::Page.new(base_page, base_blocks)
54
+ base_page = NotionRails::BasePage.new(__get_page(id))
55
+ base_blocks = NotionRails.config.cache_store.fetch(id) { get_blocks(id) }
56
+ NotionRails::Page.new(base_page, base_blocks)
56
57
  end
57
58
 
58
59
  def get_blocks(id)
59
- blocks = @client.block_children(block_id: id)
60
+ blocks = __get_blocks(id)
60
61
  parent_list_block_index = nil
61
62
  results = []
62
63
  blocks['results'].each_with_index do |block, index|
63
- base_block = Notion::BaseBlock.new(block)
64
- if base_block.has_children
65
- base_block.children = get_blocks(base_block.id)
66
- end
64
+ base_block = NotionRails::BaseBlock.new(block)
65
+ base_block.children = get_blocks(base_block.id) if base_block.has_children
67
66
  # Notion returns same list items as different blocks so we have to do some processing to have them be related
68
67
  # TODO: Separate this into a function, add support for bulleted items.
69
68
  # Currently bulleted items render fine, but they do it in separate ul blocks
70
69
  # Make them appear in the same ul block as numbered_items appear in the same ol block
71
70
  if %w[numbered_list_item].include? base_block.type
72
71
  siblings = !parent_list_block_index.nil? &&
73
- index != parent_list_block_index &&
74
- base_block.type == results[parent_list_block_index]&.type &&
75
- base_block.parent == results[parent_list_block_index]&.parent
72
+ index != parent_list_block_index &&
73
+ base_block.type == results[parent_list_block_index]&.type &&
74
+ base_block.parent == results[parent_list_block_index]&.parent
76
75
  if siblings
77
76
  results[parent_list_block_index].siblings << base_block
78
77
  next
@@ -86,5 +85,28 @@ module NotionRails
86
85
  end
87
86
  results
88
87
  end
88
+
89
+ private
90
+
91
+ def __get_articles(tag: nil, slug: nil, page_size: 10)
92
+ @client.database_query(
93
+ database_id: NotionRails.config.notion_database_id,
94
+ sorts: [
95
+ default_sorting
96
+ ],
97
+ filter: {
98
+ 'and': default_query(tag: tag, slug: slug)
99
+ },
100
+ page_size: page_size
101
+ )
102
+ end
103
+
104
+ def __get_page(id)
105
+ @client.page(page_id: id)
106
+ end
107
+
108
+ def __get_blocks(id)
109
+ @client.block_children(block_id: id)
110
+ end
89
111
  end
90
112
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NotionRails
4
- VERSION = "0.1.1"
4
+ VERSION = '0.2.0'
5
5
  end
data/lib/notion_rails.rb CHANGED
@@ -1,3 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "notion_rails/version"
3
+ require 'notion_rails/renderers'
4
+ require 'notion_rails/base_block'
5
+ require 'notion_rails/base_page'
6
+ require 'notion_rails/page'
7
+ require 'notion_rails/service'
8
+ require 'dry-configurable'
9
+
10
+ module NotionRails
11
+ extend Dry::Configurable
12
+
13
+ setting :notion_api_token
14
+ setting :notion_database_id
15
+ setting :cache_store, default: ActiveSupport::Cache::MemoryStore.new
16
+ end
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: notion_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Guillermo Aguirre
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-09 00:00:00.000000000 Z
11
+ date: 2024-08-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rails
14
+ name: actionview
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
@@ -24,9 +24,51 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 7.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 7.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 7.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: dry-configurable
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: notion-ruby-client
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.2.2
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.2.2
27
69
  description: Simple gem to render Notion blocks as HTML using Rails
28
70
  email:
29
- - guillermoaguirre1@gmail.com
71
+ - guillermoaguirre@hey.com
30
72
  executables: []
31
73
  extensions: []
32
74
  extra_rdoc_files: []
@@ -46,7 +88,7 @@ licenses:
46
88
  metadata:
47
89
  homepage_uri: https://github.com/guillermoap/notion-rails
48
90
  source_code_uri: https://github.com/guillermoap/notion-rails
49
- post_install_message:
91
+ post_install_message:
50
92
  rdoc_options: []
51
93
  require_paths:
52
94
  - lib
@@ -54,15 +96,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
54
96
  requirements:
55
97
  - - ">="
56
98
  - !ruby/object:Gem::Version
57
- version: 2.6.0
99
+ version: '3.0'
58
100
  required_rubygems_version: !ruby/object:Gem::Requirement
59
101
  requirements:
60
102
  - - ">="
61
103
  - !ruby/object:Gem::Version
62
104
  version: '0'
63
105
  requirements: []
64
- rubygems_version: 3.4.10
65
- signing_key:
106
+ rubygems_version: 3.5.11
107
+ signing_key:
66
108
  specification_version: 4
67
109
  summary: Notion HTML renderer for Rails
68
110
  test_files: []