Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,11 @@ To download all new posts and their updates from dev.to need to run

```bash
bin/from_devto -f
```
```

## How to set custom urls for posts

1. Open `devto_urls_mapping.csv`
2. Change the URLs that you want to update (you should not change the first number in the line, this is the foreign key for the article on dev.to )
3. Save `devto_urls_mapping.csv`
4. Run `bin/from_devto -f`
76 changes: 61 additions & 15 deletions bin/from_devto
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ class SaveArticleToMarkdown
require 'time'
require 'net/http'
require 'json'
require 'csv'
require 'yaml'

DEV_TO_API_HOST = 'https://dev.to/api/articles/jetthoughts/'.freeze
JT_BLOG_HOST = 'https://jetthoughts.github.io/blog/'.freeze
USELESS_WORDS = ['and', 'the', 'a', 'but', 'to', 'is', 'so'].freeze

def initialize(article_slug)
@article_slug = article_slug
Expand All @@ -19,28 +22,52 @@ class SaveArticleToMarkdown

def call
data = fetch_article_data
set_custom_filename(data)
save_to_md_file(data)
update_canonical_url_on_dev_to(data)
fill_seo_attributes(data)
end

def set_custom_filename(data)
file = find_file_with_id(data['id'])

return if file.nil?

File.rename(file, 'content/blog/' + slug(data) + File.extname(file))
end

def fetch_article_data
uri = URI(DEV_TO_API_HOST + @article_slug)
response = Net::HTTP.get(uri)
JSON.parse(response)
end

def save_to_md_file(data)
File.open(stored_post_path_for(data), 'w') { |file| file.write(data['body_markdown']) }
local_article = find_file_with_id(data['id'])
File.open(local_article || stored_post_path_for(data), 'w') { |file| file.write(data['body_markdown']) }
puts "File #{slug(data)}.md successfully created."
end

def stored_post_path_for(data)
__dir__ + "/../content/blog/#{slug(data)}.md"
__dir__ + "/../content/blog/" + slug(data) + ".md"
end

def slug(data)
Time.parse(data['created_at']).utc.strftime('%y%m%d') + '-' + data['slug'].split('-')[0..-2].join('-')
csv_data = read_csv
row = csv_data.find { |r| r[0] == data['id'].to_s }

if row
row[1]
else
generated_slug = [data['slug'].split('-')[0..-2], data['tags'][0], data['tags'][1]]
.flatten
.uniq
.filter { |segment| !USELESS_WORDS.include?(segment) }
.compact
.join('-')
write_csv(data['id'], generated_slug)
generated_slug
end
end

def update_canonical_url_on_dev_to(data)
Expand All @@ -62,20 +89,39 @@ class SaveArticleToMarkdown
markdown = File.read(stored_post_path_for(data))

File.open(stored_post_path_for(data), 'w') do |file|
file.write("+++\n")
file.write("title = #{data['title'].to_json}\n")
file.write("description = #{data['description'].to_json}\n")
file.write("created_at = \"#{data['created_at']}\"\n")
file.write("edited_at = \"#{data['edited_at']}\"\n")
file.write("sync_date = \"#{Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')}\"\n")
file.write("draft = false\n")
file.write("tags = #{data['tags']}\n")
file.write("canonical_url = \"#{data['canonical_url']}\"\n")
file.write("slug = \"#{slug(data)}\"\n")
file.write("+++\n")
file.write("---\n")
file.write("id: #{data['id'].to_json}\n")
file.write("title: #{data['title'].to_json}\n")
file.write("description: #{data['description'].to_json}\n")
file.write("created_at: \"#{data['created_at']}\"\n")
file.write("edited_at: \"#{data['edited_at']}\"\n")
file.write("sync_date: \"#{Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')}\"\n")
file.write("draft: false\n")
file.write("tags: #{data['tags']}\n")
file.write("canonical_url: \"#{data['canonical_url']}\"\n")
file.write("slug: \"#{slug(data)}\"\n")
file.write("---\n")
file.write(markdown)
end
end

def read_csv
CSV.read(File.expand_path('../devto_urls_mapping.csv', __dir__), headers: false)
end

def write_csv(id, slug)
CSV.open(File.expand_path('../devto_urls_mapping.csv', __dir__), 'a+') do |csv|
csv << [id, slug]
end
end

def find_file_with_id(id)
Dir.glob('content/blog/*.md').each do |filename|
data = YAML.load_file(filename)
return filename if data['id'] == id
end
nil
end
end

page = 1
Expand Down Expand Up @@ -106,4 +152,4 @@ loop do
page += 1
end

pp "TODO: Need to uncomment canonical url update when blog will be live."
puts 'TODO: Need to uncomment canonical url update when blog will be live.'
119 changes: 119 additions & 0 deletions devto_urls_mapping.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
1853650,what-are-next-steps-when-your-project-failing-management
1853649,cheap-tests-with-ghost-inspector-webdev-legacy
1853643,effective-project-onboarding-checklist-management-productivity
1853641,who-blame-when-project-fails-management
1853640,tools-provide-effective-feedback-for-distributed-development-teams-productivity-startup
1853635,running-tests-in-containers-with-docker-compose-coding-tutorial
1853624,cross-platform-development-using-reactxp-react-javascript
1853621,how-have-clean-css-structure-with-rscssitcss-rscss
1853617,things-that-remote-teams-expect-from-product-owner-startup
1853611,cons-of-private-chats-for-team-collaboration-communication-process
1853610,migrate-from-sidekiq-sidekiqcr-in-rails-application-tdd-testing
1853605,git-minimum-for-effective-project-development
1853596,typical-day-at-jetthoughts-agile-remote
1853575,how-know-what-your-team-doing-remote-startup
1853571,feature-branches-where-find-them-development-coding
1853556,simplest-step-by-guide-creating-post-blogging-writing
1853553,load-web-page-in-less-than-one-second-optimization-html
1853550,how-write-right-content-for-your-article-blogging-posting
1853549,tips-for-writing-readable-system-tests-in-rails-capybara-ruby
1853545,how-handle-remote-services-in-tests-rails-tutorial
1853539,why-communication-important-when-you-work-remotely-remote
1853537,tips-hire-great-people-startup-hiring
1853531,8-step-sales-process-in-5-min-productivity
1853524,how-does-onboarding-look-like-in-jetthoughts-productivity-startup
1853478,why-it-hard-find-great-developer-on-upwork-other-freelance-platforms-senior
1853475,react-native-testing-options-overview
1853472,services-tools-automatize-development-for-remote-teams-workflow-automation
1853469,jetthoughts-receives-first-review-on-clutchco-thank-you-startup
1853466,speed-up-github-prs-review-of-your-react-applications-development-tutorial
1853461,deploying-jekyll-github-pages-with-circleci-20
1852952,what-are-steps-of-an-automation-test-plan-qualitycontrol-testing
1852950,enum-validation-in-ruby-on-rails-71
1852940,pitfalls-of-using-metaprogramming-in-ruby-on-rails-application
1852752,regular-automatic-dependencies-update-with-circleci-github-circle
1852749,design-rails-json-api-with-performance-in-mind-cache
1852747,practical-guide-writing-introductions-introduction
1852745,trial-period-for-staff-augmentation-in-jetthoughts-startup-engagement
1852741,how-avoid-n1-query-using-sql-views-materialized-in-rails-application-ruby
1852739,rscss-styling-css-without-losing-your-sanity
1852738,rails-virtual-attributes-use-cases-ruby
1852733,4-tricks-write-catchy-headlines-contentwriting
1852719,5-free-tools-make-sales-process-easier-leadgeneration
1852712,what-are-most-common-misconceptions-about-remote-work-misconception
1852642,deploying-subdirectory-projects-heroku-git
1852632,simple-lead-generation-tactics-startup-sails
1852630,cleaning-routines-keep-your-project-without-bugs-agile
1852624,which-platforms-are-better-use-for-non-tech-founders-look-contractors-freelance-development
1852622,how-create-technical-post-in-short-time-writing-blogging
1852621,ultimate-guide-sales-onboarding-in-it-companies-sails-leadgeneration
1852618,how-start-an-open-source-project-building-reso-api-js-client-javascript-opensource
1852617,prepare-pull-request-before-asking-review-git-pullrequest
1852612,where-read-env-variables-in-ruby-on-rails-application
1852611,jetthoughts-recognized-by-techreviewer-as-top-web-development-company-in-2020-webdev
1852603,delivery-flow-for-distributed-remote-teams-agile-kanban
1852581,what-do-for-developer-when-wip-limit-reached-agile-kanban
1852579,we-got-3-main-principles-for-writing-technical-blog-posts-heres-what-learned
1852577,benefits-of-working-remotely-remote
1852568,collecting-javascript-code-coverage-with-capybara-in-ruby-on-rails-application-testing
1852564,automated-delivery-react-vue-app-for-each-pull-request-ci
1852562,tips-attract-readers-read-your-post-blogging
1852561,what-activities-are-expected-from-remote-developer-for-effective-collaboration-development-process
1851499,new-asserts-for-testing-stopped-streams-after-ruby-on-rails-71-changelog
1846033,migration-from-medium-devto-hugo-blog-webdev
1783325,setting-up-docker-for-ruby-on-rails-7-beginners
1774708,more-control-over-enum-in-rails-71-webdev
1760994,upgrading-postgresql-on-heroku-step-by-guide-database
1740002,install-official-firefox-deb-in-dockerfile-docker-devops
1739418,anonymous-block-argument-in-ruby-tutorial
1698810,stimulus-keyboard-event-filter
1689481,integrating-bun-with-vite-ruby-for-lightning-fast-frontend-builds-startup-productivity
1677293,custom-ordering-without-sql-with-ruby-on-rails-7
1673284,how-we-configure-simplecov-for-our-ruby-on-rails-projects
1667161,integrating-bun-with-vite-ruby-for-lightning-fast-frontend-builds-rails-javascript
1561382,improving-ruby-on-rails-test-suite-performance-by-disabling-animations-programming-tutorial
1560068,optimize-your-chrome-options-for-testing-get-x125-impact-performance
1559624,ruby-on-rails-views-resources-for-frontend-developer-programming-javascript
1556678,align-remote-teams-with-okrs-impact-mapping-management-devops
1454204,how-large-transaction-can-be-source-of-db-deadlocks-this-fixed-ruby-database
1375276,how-keep-clean-ruby-on-rails-views-with-null-object-pattern
1297134,how-wip-limits-improves-effectiveness-productivity-management
1283284,when-use-microservices-devops-distributedsystems
1277349,what-ruby-on-rails-middleware
1222970,how-create-triangles-in-tailwindcss-html-css
1215921,troubleshooting-ruby-build-tutorial
1212480,custom-templates-for-rails-scaffolding-ruby
1205578,our-mvp-team-structure-startup-management
1205643,tldr-move-cicd-scripts-into-automation-devops-productivity
1188292,how-make-vertically-scrollable-in-css-html
1200329,auto-install-system-dependencies-for-ruby-on-rails-programming
1198201,how-avoid-callbacks-using-services-rails-refactoring
1189342,incremental-lint-fixes-by-github-actions-devops
1182433,data-migrations-with-rails-ruby
1170570,how-use-ruby-on-rails-concerns-webdev
1168677,how-create-circles-in-css-html
1156418,how-make-truncate-text-in-css-html
1148536,how-create-triangles-in-css-html
1144505,how-setup-default-values-for-attributes-in-ruby-on-rails-programming
1140235,what-difference-between-joins-includes-in-rails-activerecord-ruby
1125937,how-use-linear-gradient-in-css-html
1116924,how-use-background-size-in-css-html
1115250,pitfalls-of-using-metaprogramming-in-ruby-on-rails-application-programming
1113061,how-use-nth-child-in-css-html
1108673,preview-ui-changes-with-ruby-on-rails-variants
1109046,how-style-checkbox-using-css-html
1103639,generating-random-strings-with-ruby-webdev
1103884,how-vertically-center-an-element-without-flex-css-html
1101579,how-horizontally-center-an-element-without-flex-css-html
1099690,change-inputs-placeholder-color-with-css-html
1093446,vertical-align-with-full-screen-across-tailwind-css-jetthoughts
1091289,manage-bundler-indirect-dependencies-versions-ruby-beginners
1089599,how-get-build-full-urls-in-rails-ruby
539418,why-how-use-tdd-main-tips-testing
470562,how-make-small-valuable-async-standups-productivity-development
462572,how-create-technical-post-in-short-time-writing-blogging
462569,trial-period-for-staff-augmentation-in-jetthoughts-startup-engagement
462554,who-blame-when-project-fails-blamegame
462546,what-are-next-steps-when-your-project-failing-management
399009,how-use-transaction-script-aka-service-objects-in-ruby-on-rails-simple-example
462581,communication-agreement-in-remote-environment-agile