├── .env.sample
├── .gitignore
├── .rubocop.yml
├── .travis.yml
├── CONTRIBUTING.md
├── Gemfile
├── Gemfile.lock
├── LICENSE
├── Procfile
├── Procfile.dev
├── README.md
├── Rakefile
├── app
├── assets
│ ├── config
│ │ └── manifest.js
│ ├── fonts
│ │ └── font-awesome
│ │ │ ├── fontawesome-webfont.eot
│ │ │ ├── fontawesome-webfont.svg
│ │ │ ├── fontawesome-webfont.ttf
│ │ │ ├── fontawesome-webfont.woff
│ │ │ └── fontawesome-webfont.woff2
│ ├── images
│ │ ├── .keep
│ │ ├── announcing-next.png
│ │ ├── badges
│ │ │ ├── 1000lemming.png
│ │ │ ├── 100lemming.png
│ │ │ ├── 24-continuous-sync.png
│ │ │ ├── 24-participant.png
│ │ │ ├── altrustic.png
│ │ │ ├── bear.png
│ │ │ ├── bear3.png
│ │ │ ├── beaver-old.png
│ │ │ ├── beaver.png
│ │ │ ├── beaver3.png
│ │ │ ├── changelogd.png
│ │ │ ├── charity.png
│ │ │ ├── coffee.png
│ │ │ ├── comingsoon.png
│ │ │ ├── cub.png
│ │ │ ├── desertlocust.png
│ │ │ ├── desertlocust3.png
│ │ │ ├── earlyadopter.png
│ │ │ ├── entrepreneur.png
│ │ │ ├── epidexipteryx.png
│ │ │ ├── epidexipteryx3.png
│ │ │ ├── forked.png
│ │ │ ├── forked1.png
│ │ │ ├── forked100.png
│ │ │ ├── forked20.png
│ │ │ ├── forked50.png
│ │ │ ├── general-skills.png
│ │ │ ├── github-gameoff-honorable-mention-2012.png
│ │ │ ├── github-gameoff-judge-2012.png
│ │ │ ├── github-gameoff-participant-2012.png
│ │ │ ├── github-gameoff-runner-up-2012.png
│ │ │ ├── github-gameoff-winner-2012.png
│ │ │ ├── go.png
│ │ │ ├── go3.png
│ │ │ ├── goruco.png
│ │ │ ├── hackathon.png
│ │ │ ├── hackathonCMU.png
│ │ │ ├── hackathonStanford.png
│ │ │ ├── honeybadger-brood.png
│ │ │ ├── honeybadger-brood2.png
│ │ │ ├── honeybadger-ko.png
│ │ │ ├── honeybadger-ko3.png
│ │ │ ├── honeybadger.png
│ │ │ ├── honeybadger3.png
│ │ │ ├── ko-best-design-2011.png
│ │ │ ├── ko-best-design-2012.png
│ │ │ ├── ko-champion-2011.png
│ │ │ ├── ko-champion-2012.png
│ │ │ ├── ko-contender-2011.png
│ │ │ ├── ko-contender-2012.png
│ │ │ ├── ko-judge-2011.png
│ │ │ ├── ko-judge-2012.png
│ │ │ ├── ko-most-complete-2011.png
│ │ │ ├── ko-most-complete-2012.png
│ │ │ ├── ko-most-innovative-2011.png
│ │ │ ├── ko-most-innovative-2012.png
│ │ │ ├── ko-most-useful-2011.png
│ │ │ ├── ko-most-useful-2012.png
│ │ │ ├── ko-most-votes-2011.png
│ │ │ ├── ko-most-votes-2012.png
│ │ │ ├── komododragon.png
│ │ │ ├── komododragon3.png
│ │ │ ├── labrador.png
│ │ │ ├── labrador3.png
│ │ │ ├── locked.png
│ │ │ ├── mayor.png
│ │ │ ├── mongoose.png
│ │ │ ├── mongoose3.png
│ │ │ ├── moongoose-rails.png
│ │ │ ├── more.png
│ │ │ ├── more2.png
│ │ │ ├── narwhal.png
│ │ │ ├── narwhal3.png
│ │ │ ├── neo4j-challenge.png
│ │ │ ├── neo4j-winner.png
│ │ │ ├── neo4j.png
│ │ │ ├── nephilakomaci.png
│ │ │ ├── nephilakomaci3.png
│ │ │ ├── octopussy.png
│ │ │ ├── philanthropist.png
│ │ │ ├── platypus.png
│ │ │ ├── platypus3.png
│ │ │ ├── python.png
│ │ │ ├── python3.png
│ │ │ ├── railsberry.png
│ │ │ ├── railscamp.png
│ │ │ ├── raven.png
│ │ │ ├── screencapture-achievements.png
│ │ │ ├── social-icons.png
│ │ │ ├── trex.png
│ │ │ ├── trex3.png
│ │ │ ├── velociraptor.png
│ │ │ ├── velociraptor3.png
│ │ │ ├── walrus.png
│ │ │ └── wrocloverb.png
│ │ ├── conference-room.png
│ │ ├── happy-cat.jpg
│ │ ├── live-banner.jpg
│ │ ├── live-banner.png
│ │ ├── offline-holder.png
│ │ └── pop.mp3
│ ├── javascripts
│ │ ├── analytics.js.coffee
│ │ ├── application_non_webpack.js.coffee
│ │ ├── application_static.js
│ │ ├── bsa.js.coffee
│ │ └── textarea_with_file_drop_support.js.coffee
│ └── stylesheets
│ │ ├── application_non_webpack.scss
│ │ ├── application_static.scss
│ │ ├── basscss.scss
│ │ ├── basscss
│ │ ├── _align.scss
│ │ ├── _background-colors.scss
│ │ ├── _background-images.scss
│ │ ├── _base-forms.scss
│ │ ├── _base-reset.scss
│ │ ├── _base-tables.scss
│ │ ├── _base-typography.scss
│ │ ├── _border-colors.scss
│ │ ├── _borders.scss
│ │ ├── _btn-outline.scss
│ │ ├── _btn-primary.scss
│ │ ├── _btn-sizes.scss
│ │ ├── _btn.scss
│ │ ├── _color-base.scss
│ │ ├── _color-forms-dark.scss
│ │ ├── _color-forms.scss
│ │ ├── _color-input-range.scss
│ │ ├── _color-progress.scss
│ │ ├── _color-tables.scss
│ │ ├── _colors.scss
│ │ ├── _defaults.scss
│ │ ├── _flex-object.scss
│ │ ├── _grid.scss
│ │ ├── _highlight.scss
│ │ ├── _input-range.scss
│ │ ├── _positions.scss
│ │ ├── _progress.scss
│ │ ├── _responsive-states.scss
│ │ ├── _responsive-white-space.scss
│ │ ├── _table-object.scss
│ │ ├── _type-scale.scss
│ │ ├── _ui-utility-groups.scss
│ │ ├── _utility-headings.scss
│ │ ├── _utility-layout.scss
│ │ ├── _utility-typography.scss
│ │ └── _white-space.scss
│ │ ├── content.scss
│ │ ├── dropdown.scss
│ │ ├── font-awesome.scss
│ │ ├── font-awesome
│ │ ├── _animated.scss
│ │ ├── _bordered-pulled.scss
│ │ ├── _core.scss
│ │ ├── _extras.scss
│ │ ├── _fixed-width.scss
│ │ ├── _icons.scss
│ │ ├── _larger.scss
│ │ ├── _list.scss
│ │ ├── _mixins.scss
│ │ ├── _path.scss
│ │ ├── _rotated-flipped.scss
│ │ ├── _stacked.scss
│ │ └── _variables.scss
│ │ ├── minimal.scss
│ │ └── twilight_theme.scss
├── controllers
│ ├── application_controller.rb
│ ├── application_record.rb
│ ├── badges_controller.rb
│ ├── comments_controller.rb
│ ├── concerns
│ │ └── .keep
│ ├── hooks_controller.rb
│ ├── job_subscriptions_controller.rb
│ ├── jobs_controller.rb
│ ├── likes_controller.rb
│ ├── pages_controller.rb
│ ├── pictures_controller.rb
│ ├── protips_controller.rb
│ ├── sponsors_controller.rb
│ ├── subscribers_controller.rb
│ ├── teams_controller.rb
│ └── users_controller.rb
├── helpers
│ ├── application_helper.rb
│ ├── font_awesome_helper.rb
│ ├── jobs_helper.rb
│ ├── likes_helper.rb
│ ├── protips_helper.rb
│ └── users_helper.rb
├── lib
│ ├── avatar_uploader.rb
│ ├── cloudfront_constraint.rb
│ ├── coderwall_flavored_markdown.rb
│ ├── legacy_badges.rb
│ ├── picture_uploader.rb
│ └── spaminator.rb
├── mailers
│ ├── application_mailer.rb
│ ├── comment_mailer.rb
│ └── user_mailer.rb
├── models
│ ├── .keep
│ ├── article.rb
│ ├── badge.rb
│ ├── category.rb
│ ├── comment.rb
│ ├── concerns
│ │ ├── .keep
│ │ ├── time_ago_in_words_cache_buster.rb
│ │ └── view_count_cache_buster.rb
│ ├── github.rb
│ ├── job.rb
│ ├── job_subscription.rb
│ ├── like.rb
│ ├── picture.rb
│ ├── protip.rb
│ ├── secure_reply_to.rb
│ ├── slack.rb
│ ├── sponsor.rb
│ ├── team.rb
│ └── user.rb
├── serializers
│ ├── badge_serializer.rb
│ ├── comment_serializer.rb
│ ├── current_user_serializer.rb
│ ├── job_serializer.rb
│ ├── picture_serializer.rb
│ ├── protip_serializer.rb
│ └── user_serializer.rb
├── services
│ └── notification.rb
└── views
│ ├── badges
│ └── _badge.html.haml
│ ├── clearance_mailer
│ ├── change_password.html.erb
│ └── change_password.text.erb
│ ├── comment_mailer
│ └── new_comment.html.erb
│ ├── comments
│ ├── _comment.html.haml
│ ├── _comment.json.jbuilder
│ ├── index.html.haml
│ ├── index.json.jbuilder
│ └── show.js.erb
│ ├── job_subscriptions
│ └── new.html.haml
│ ├── jobs
│ ├── _job.html.haml
│ ├── _mini.html.haml
│ ├── index.html.haml
│ └── new.html.haml
│ ├── layouts
│ ├── application.html.haml
│ └── minimal.html.haml
│ ├── pages
│ ├── faq.html.haml
│ ├── not_found.html.haml
│ ├── privacy.html.haml
│ ├── server_error.html.haml
│ ├── styleguide.html.erb
│ └── tos.html.haml
│ ├── passwords
│ ├── create.html.haml
│ ├── edit.html.haml
│ └── new.html.haml
│ ├── protips
│ ├── _protip.html.haml
│ ├── home.html.haml
│ ├── index.html.haml
│ ├── new.html.haml
│ └── show.html.haml
│ ├── sessions
│ └── new.html.haml
│ ├── shared
│ ├── _analytics.html.erb
│ ├── _header.html.haml
│ ├── _logo.html.erb
│ └── _tracking.html.erb
│ ├── teams
│ └── show.html.haml
│ ├── user_mailer
│ ├── destroy_email.text.erb
│ └── partnership_expired.text.erb
│ └── users
│ ├── edit.html.haml
│ ├── new.html.haml
│ └── show.html.haml
├── bin
├── bundle
├── rails
├── rake
├── setup
├── spring
└── update
├── client
├── .babelrc
├── .eslintrc
├── actions
│ ├── heartActions.js
│ ├── jobActions.js
│ └── protipActions.js
├── checkLinks.js
├── components
│ ├── Heart.jsx
│ ├── HeartButton.jsx
│ ├── Icon.jsx
│ ├── JobForm.jsx
│ ├── NewJob.jsx
│ ├── NewJobSubscription.jsx
│ ├── ProtipSubscribeButton.jsx
│ ├── Sponsors.jsx
│ ├── ToggleWithLabel.jsx
│ ├── TrackClick.jsx
│ └── __tests__
│ │ ├── JobForm-test.jsx
│ │ └── __snapshots__
│ │ └── JobForm-test.jsx.snap
├── lib
│ ├── apiAuthInjector.js
│ ├── createReducer.js
│ ├── loadImage.js
│ └── unauthorizedHandler.js
├── package.json
├── reducers
│ ├── commentsReducer.js
│ ├── currentProtipReducer.js
│ ├── currentUserReducer.js
│ ├── heartsReducer.js
│ ├── index.js
│ ├── jobReducer.js
│ └── protipsReducer.js
├── server-rails-hot.js
├── startup
│ ├── clientRegistration.jsx
│ ├── confirm.js
│ └── serverRegistration.jsx
├── stores
│ └── store.js
├── webpack.client.base.config.js
├── webpack.client.rails.build.config.js
├── webpack.client.rails.hot.config.js
├── webpack.server.rails.build.config.js
└── yarn.lock
├── config.ru
├── config
├── application.rb
├── boot.rb
├── cable.yml
├── database.yml
├── environment.rb
├── environments
│ ├── development.rb
│ ├── production.rb
│ └── test.rb
├── initializers
│ ├── application_controller_renderer.rb
│ ├── assets.rb
│ ├── backtrace_silencers.rb
│ ├── carrierwave.rb
│ ├── clearance.rb
│ ├── cookies_serializer.rb
│ ├── cors.rb
│ ├── filter_parameter_logging.rb
│ ├── inflections.rb
│ ├── invisible_captcha.rb
│ ├── meta_tags.rb
│ ├── mime_types.rb
│ ├── new_framework_defaults.rb
│ ├── puma_worker_killer.rb
│ ├── rack_mini_profiler.rb
│ ├── rack_timeout.rb
│ ├── sanitization.rb
│ ├── session_store.rb
│ ├── stripe.rb
│ ├── time_formats.rb
│ ├── webpack.rb
│ └── wrap_parameters.rb
├── letsencrypt_plugin.yml
├── locales
│ ├── clearance.en.yml
│ ├── en.yml
│ └── invisible_captcha.en.yml
├── puma.rb
├── routes.rb
├── secrets.yml
└── spring.rb
├── db
├── migrate
│ ├── 20160119204658_create_protips.rb
│ ├── 20160119204714_create_users.rb
│ ├── 20160120175334_add_clearance_to_users.rb
│ ├── 20160120200829_create_comments.rb
│ ├── 20160121012718_create_likes.rb
│ ├── 20160121013050_create_badges.rb
│ ├── 20160210220146_add_tags_and_count_columns.rb
│ ├── 20160210221757_add_color_to_user.rb
│ ├── 20160210222721_add_karma_to_user.rb
│ ├── 20160212233312_add_flagged_to_protip.rb
│ ├── 20160213012958_add_likes_cache.rb
│ ├── 20160213015810_remove_login_counts.rb
│ ├── 20160214213211_clean_up_likes.rb
│ ├── 20160219071138_improving_database_performane.rb
│ ├── 20160219190140_add_banned_users_for_migration.rb
│ ├── 20160223063803_add_more_indexes.rb
│ ├── 20160223064301_changing_user_to_citext.rb
│ ├── 20160223065728_create_team.rb
│ ├── 20160225042520_add_simple_user_id_index_on_like.rb
│ ├── 20160225070301_auto_like_content.rb
│ ├── 20160225171117_add_pictures.rb
│ ├── 20160227000445_add_missing_foreign_keys.rb
│ ├── 20160227010236_turn_fields_case_insensitive.rb
│ ├── 20160307195201_clean_up_orphan_pictures.rb
│ ├── 20160318212558_add_marketing_list_to_users.rb
│ ├── 20160422205835_add_view_counts_to_team.rb
│ ├── 20160422211004_create_jobs.rb
│ ├── 20160422213924_move_job_id_over_to_uuid.rb
│ ├── 20160422215652_trim_job.rb
│ ├── 20160422234923_add_publish_attributes_to_jobs.rb
│ ├── 20160425233554_create_job_views.rb
│ ├── 20160513032303_add_stream_key_to_users.rb
│ ├── 20160519204919_add_type_to_protips.rb
│ ├── 20160519233923_rename_protip_id_on_comments_to_article_id.rb
│ ├── 20160601015828_add_started_archived_fields_to_articles.rb
│ ├── 20160607202132_add_recording_id_to_protips.rb
│ ├── 20160608034824_add_recording_started_at_to_protips.rb
│ ├── 20160830184552_create_job_subscriptions.rb
│ ├── 20160909044024_add_indexes.rb
│ ├── 20160913165240_add_comment_unsubscribed_to_users.rb
│ ├── 20160916222644_add_subscribers_to_articles.rb
│ ├── 20160919171618_remove_unsubscribed_comment_emails_at_from_users.rb
│ ├── 20160922010312_create_letsencrypt_plugin_challenges.letsencrypt_plugin.rb
│ ├── 20160922010313_create_letsencrypt_plugin_settings.letsencrypt_plugin.rb
│ ├── 20160922185544_remove_job_views.rb
│ ├── 20160923195619_add_partner_info.rb
│ ├── 20161207222630_change_users_last_ip_from_integer_to_string.rb
│ ├── 20170109215300_add_spam_detected_at_to_protips.rb
│ ├── 20170110195008_add_spam_fields_to_protips.rb
│ ├── 20170220093535_remove_stream_key_from_users.rb
│ └── 20170328232725_add_bad_users_and_content.rb
├── schema.rb
└── seeds.rb
├── lib
└── tasks
│ ├── cache.rake
│ ├── clean.rake
│ ├── contributions.rake
│ ├── db.rake
│ ├── marketing.rake
│ ├── partners.rake
│ ├── port.rake
│ ├── report.rake
│ ├── restore.rake
│ ├── spam.rake
│ └── tags.rake
├── log
└── .keep
├── newrelic.yml
├── package.json
├── public
├── apple-touch-icon-152x152-precomposed.png
├── apple-touch-icon-precomposed.png
├── apple-touch-icon.png
├── fav128x128.png
├── fav32x32.png
├── fav64x64.png
├── favicon.ico
├── favicon.png
├── robots.txt
├── touch-icon-ipad-retina.png
├── touch-icon-ipad.png
├── touch-icon-iphone-retina.png
└── touch-icon-iphone.png
├── test
├── controllers
│ ├── comments_controller_test.rb
│ ├── protips_controller_test.rb
│ ├── subscribers_controller_test.rb
│ └── users_controller_test.rb
├── factories
│ ├── comment.rb
│ ├── protip.rb
│ └── user.rb
├── fixtures
│ └── jobs.yml
├── helpers
│ └── .keep
├── integration
│ └── .keep
├── mailers
│ └── comment_mailer_test.rb
├── models
│ ├── .keep
│ ├── badge_test.rb
│ ├── job_test.rb
│ └── user_test.rb
└── test_helper.rb
├── update-ssl.sh
└── vendor
└── assets
├── javascripts
└── .keep
└── stylesheets
└── .keep
/.env.sample:
--------------------------------------------------------------------------------
1 | AWS_ACCESS_ID=
2 | AWS_ACCESS_SECRET=
3 | AWS_BUCKET=
4 | AWS_REGION=
5 | GOOGLE_ANALYTICS_UA=UA-XXXXXXXX-X
6 | JOB_SUBSCRIPTION_CENTS=49900
7 | JWPLAYER_KEY=
8 | LEGACY_DB_URL=you_only_need_this_to_migrate
9 | LEGACY_REDIS_URL=you_only_need_this_to_migrate
10 | NEW_RELIC_APP_NAME=coderwall (development)
11 | NEW_RELIC_DEVELOPER_MODE=true
12 | NEW_RELIC_ERROR_COLLECTOR_IGNORE_ERRORS=ActiveRecord::RecordNotFound
13 | NEW_RELIC_LICENSE_KEY=
14 | PUSHER_APP_ID=
15 | PUSHER_KEY=
16 | PUSHER_SECRET=
17 | QUICKSTREAM_URL=
18 | REACT_ON_RAILS_ENV=HOT
19 | SLACK_API_TOKEN=
20 | SLACK_WEBHOOK_URL=https://hooks.slack.com/services/XXXX/XXXX/XXXX
21 | BSA_IDENTIFIER=
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2 | #
3 | # If you find yourself ignoring temporary files generated by your text editor
4 | # or operating system, you probably want to add a global ignore instead:
5 | # git config --global core.excludesfile '~/.gitignore_global'
6 |
7 | # Ignore bundler config.
8 | /.bundle
9 |
10 | # Ignore all logfiles and tempfiles.
11 | /log/*
12 | !/log/.keep
13 | /tmp
14 | .env
15 | public/uploads
16 | TODO
17 | info
18 | .DS_Store
19 | .byebug*
20 | coderwall-production.dump
21 | contributions.csv
22 | google.docs.config.json
23 | lib/tasks/recruiters.rake
24 |
25 | node_modules
26 | npm-debug.log
27 | client/node_modules
28 | client/npm-debug.log
29 | /app/assets/javascripts/application.js
30 | /app/assets/webpack/*
31 | server.crt
32 | server.key
33 | capybara-*.html
34 | debug.png
35 | scripts
36 | coderwall.com-*
37 |
38 | public/assets
39 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | Rails:
2 | Enabled: true
3 |
4 | Metrics/LineLength:
5 | Max: 120
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | rvm:
3 | - 2.4.0
4 | cache: bundler
5 | sudo: false
6 | addons:
7 | postgresql: '9.3'
8 | apt:
9 | sources:
10 | - ubuntu-toolchain-r-test
11 | packages:
12 | - g++-4.9
13 |
14 | env:
15 | global:
16 | - RAILS_ENV=test
17 | - CXX=g++-4.9
18 |
19 | install:
20 | - bundle install --without production
21 | - nvm install 6.4
22 | - nvm use 6.4
23 | - npm install
24 | - bin/rake db:setup
25 |
26 | script:
27 | - npm test
28 |
29 | notifications:
30 | slack:
31 | template:
32 | - "<%{build_url}|#%{build_number}> (<%{compare_url}|%{commit}>) of %{repository}@%{branch} by %{author} %{result} in %{duration}"
33 | - "%{commit_subject}"
34 | - "${commit_message}"
35 | secure: mpNLTpZPaQ9NmHTAm8uSsPfwL07Esh750yPYWJfCSJzGrNMoz8IDleY8ddPNwTVOLIhhV4rVK7QyF5aAin8+riIlTzJkeLViEL257vl/VY+Th9ryYLdJ1hpa+HaZ8AeDinS5BTdtyjZYClUk+ALKqiFCxe2mm3oODgcSFIPjdhZ40CJKmHAMlj+S2+ypFMYg1Qy9F1xlwb952ZV7PnjwT8kjnzkMmAWtgpEFlTIBJVjBlO4FGh9nCqHda6KT3TjUxMa49Kt8cRBmZCPgkteLciUnOo1rjPeyJX4pjL0pThoCHkHFtFVffw/BxJ0b4WdIc/LKz7iFqJTSF3HChO55lAKhC8bbTaus5kr1AT+McNeC7+hcstjncSIzUEUabcPN2oF/po1SV/A3wR203JsddHRPN3nIGi71izNoT4rFAY+qNeUZS0VeAa2YWNDq46FMLvoCE/+i//HFx/nWYF8D6dLqRWaIeqeJAUeSZHmqey88Ff4SuuybuB3k6ryqWkYS/K+YvrjtuFNZUouscB5vktOjuLiwDTLAVVLQ6ybPBJ2YEj6CpOi2GmazJty9YQcfcYmWlqEf4nBAbcTCRPA/n2306k/26fH4tZygW1g4Zm/BUfGZjrWaHQqA6f4uo10qKVTOktd4vjIJl74SED1vgvoGUbmvOpFKtkQ9RuhBaig=
36 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | We welcome ideas and contributions on Coderwall. If you want to contribute something, here's how:
4 |
5 | 1. Check the product [readme][readme] for the latest product direction and how to run the code locally.
6 |
7 | [readme]: https://github.com/coderwall/coderwall-next/compare/
8 |
9 | 2. For new feature ideas, we recommend creating an issue first that briefly describes the feature you want to add. This gives others involved with Coderwall an opportunity to discuss and provide feedback.
10 |
11 | 3. [Submit a pull request][pr] with your code and design changes.
12 |
13 | [pr]: https://github.com/coderwall/coderwall-next/compare/
14 |
15 |
16 | 3. Please give us up to a week to review the pull request and comment. We may suggest
17 | some changes or improvements or alternatives. If you don't receive a timely response you can escalate your PR by contacting support@coderwall.com
18 |
19 | ## Pull Request Guidelines
20 |
21 | Some things that will increase the chance that your pull request is accepted:
22 |
23 | * Write test for your changes and make sure all the tests pass.
24 | * Keep it as conventional and simple as possible. Coderwall serves 100,000 of devs each month on very minimal oversight. We want the product quick to support and easy to enhance. This includes being very thoughtful before adding external dependencies or deviating from the conventional vanilla rails project structure.
25 | * Use [basscss](http://www.basscss.com) for all css. It is a really really really good atomic class based CSS library. You should rarely have to add a new style or custom css but if you do, please only do so in application.scss.
26 | * Make any settings a configuration accessible through ENV with an example setting in .env.sample
27 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: bundle exec puma -C ./config/puma.rb --quiet
2 | hot-assets: sh -c 'rm app/assets/webpack/* || true && HOT_RAILS_PORT=3500 npm run hot-assets'
3 | rails-server-assets: sh -c 'npm run build:dev:server'
4 |
--------------------------------------------------------------------------------
/Procfile.dev:
--------------------------------------------------------------------------------
1 | web: bundle exec puma -C ./config/puma.rb
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Coderwall
2 |
3 | [](https://travis-ci.org/coderwall/coderwall-next)
4 |
5 | The codebase for [coderwall.com](https://coderwall.com). Coderwall is a developer community used by nearly half a million developers each month to learn and share programming tips.
6 |
7 | ## Prerequisites
8 |
9 | * Ruby
10 | * Postgres
11 | * Heroku Toolbelt (or foreman gem)
12 |
13 | ## Get Started
14 |
15 | ```bash
16 | cp .env.sample .env # (most settings are not required for core functionality)
17 | bundle install
18 | rake db:create db:migrate
19 | heroku local
20 | ```
21 |
22 | ## Updating SSL
23 |
24 | ```
25 | $ ./update-ssl.sh
26 | ```
27 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require File.expand_path('../config/application', __FILE__)
5 |
6 | Rails.application.load_tasks
7 |
8 | task default: :test
--------------------------------------------------------------------------------
/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link_tree ../images
2 | //= link_directory ../javascripts .js
3 | //= link_directory ../stylesheets .css
4 |
--------------------------------------------------------------------------------
/app/assets/fonts/font-awesome/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/fonts/font-awesome/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/app/assets/fonts/font-awesome/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/fonts/font-awesome/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/app/assets/fonts/font-awesome/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/fonts/font-awesome/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/app/assets/fonts/font-awesome/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/fonts/font-awesome/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/app/assets/images/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/.keep
--------------------------------------------------------------------------------
/app/assets/images/announcing-next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/announcing-next.png
--------------------------------------------------------------------------------
/app/assets/images/badges/1000lemming.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/1000lemming.png
--------------------------------------------------------------------------------
/app/assets/images/badges/100lemming.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/100lemming.png
--------------------------------------------------------------------------------
/app/assets/images/badges/24-continuous-sync.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/24-continuous-sync.png
--------------------------------------------------------------------------------
/app/assets/images/badges/24-participant.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/24-participant.png
--------------------------------------------------------------------------------
/app/assets/images/badges/altrustic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/altrustic.png
--------------------------------------------------------------------------------
/app/assets/images/badges/bear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/bear.png
--------------------------------------------------------------------------------
/app/assets/images/badges/bear3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/bear3.png
--------------------------------------------------------------------------------
/app/assets/images/badges/beaver-old.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/beaver-old.png
--------------------------------------------------------------------------------
/app/assets/images/badges/beaver.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/beaver.png
--------------------------------------------------------------------------------
/app/assets/images/badges/beaver3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/beaver3.png
--------------------------------------------------------------------------------
/app/assets/images/badges/changelogd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/changelogd.png
--------------------------------------------------------------------------------
/app/assets/images/badges/charity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/charity.png
--------------------------------------------------------------------------------
/app/assets/images/badges/coffee.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/coffee.png
--------------------------------------------------------------------------------
/app/assets/images/badges/comingsoon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/comingsoon.png
--------------------------------------------------------------------------------
/app/assets/images/badges/cub.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/cub.png
--------------------------------------------------------------------------------
/app/assets/images/badges/desertlocust.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/desertlocust.png
--------------------------------------------------------------------------------
/app/assets/images/badges/desertlocust3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/desertlocust3.png
--------------------------------------------------------------------------------
/app/assets/images/badges/earlyadopter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/earlyadopter.png
--------------------------------------------------------------------------------
/app/assets/images/badges/entrepreneur.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/entrepreneur.png
--------------------------------------------------------------------------------
/app/assets/images/badges/epidexipteryx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/epidexipteryx.png
--------------------------------------------------------------------------------
/app/assets/images/badges/epidexipteryx3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/epidexipteryx3.png
--------------------------------------------------------------------------------
/app/assets/images/badges/forked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/forked.png
--------------------------------------------------------------------------------
/app/assets/images/badges/forked1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/forked1.png
--------------------------------------------------------------------------------
/app/assets/images/badges/forked100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/forked100.png
--------------------------------------------------------------------------------
/app/assets/images/badges/forked20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/forked20.png
--------------------------------------------------------------------------------
/app/assets/images/badges/forked50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/forked50.png
--------------------------------------------------------------------------------
/app/assets/images/badges/general-skills.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/general-skills.png
--------------------------------------------------------------------------------
/app/assets/images/badges/github-gameoff-honorable-mention-2012.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/github-gameoff-honorable-mention-2012.png
--------------------------------------------------------------------------------
/app/assets/images/badges/github-gameoff-judge-2012.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/github-gameoff-judge-2012.png
--------------------------------------------------------------------------------
/app/assets/images/badges/github-gameoff-participant-2012.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/github-gameoff-participant-2012.png
--------------------------------------------------------------------------------
/app/assets/images/badges/github-gameoff-runner-up-2012.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/github-gameoff-runner-up-2012.png
--------------------------------------------------------------------------------
/app/assets/images/badges/github-gameoff-winner-2012.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/github-gameoff-winner-2012.png
--------------------------------------------------------------------------------
/app/assets/images/badges/go.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/go.png
--------------------------------------------------------------------------------
/app/assets/images/badges/go3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/go3.png
--------------------------------------------------------------------------------
/app/assets/images/badges/goruco.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/goruco.png
--------------------------------------------------------------------------------
/app/assets/images/badges/hackathon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/hackathon.png
--------------------------------------------------------------------------------
/app/assets/images/badges/hackathonCMU.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/hackathonCMU.png
--------------------------------------------------------------------------------
/app/assets/images/badges/hackathonStanford.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/hackathonStanford.png
--------------------------------------------------------------------------------
/app/assets/images/badges/honeybadger-brood.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/honeybadger-brood.png
--------------------------------------------------------------------------------
/app/assets/images/badges/honeybadger-brood2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/honeybadger-brood2.png
--------------------------------------------------------------------------------
/app/assets/images/badges/honeybadger-ko.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/honeybadger-ko.png
--------------------------------------------------------------------------------
/app/assets/images/badges/honeybadger-ko3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/honeybadger-ko3.png
--------------------------------------------------------------------------------
/app/assets/images/badges/honeybadger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/honeybadger.png
--------------------------------------------------------------------------------
/app/assets/images/badges/honeybadger3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/honeybadger3.png
--------------------------------------------------------------------------------
/app/assets/images/badges/ko-best-design-2011.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/ko-best-design-2011.png
--------------------------------------------------------------------------------
/app/assets/images/badges/ko-best-design-2012.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/ko-best-design-2012.png
--------------------------------------------------------------------------------
/app/assets/images/badges/ko-champion-2011.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/ko-champion-2011.png
--------------------------------------------------------------------------------
/app/assets/images/badges/ko-champion-2012.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/ko-champion-2012.png
--------------------------------------------------------------------------------
/app/assets/images/badges/ko-contender-2011.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/ko-contender-2011.png
--------------------------------------------------------------------------------
/app/assets/images/badges/ko-contender-2012.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/ko-contender-2012.png
--------------------------------------------------------------------------------
/app/assets/images/badges/ko-judge-2011.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/ko-judge-2011.png
--------------------------------------------------------------------------------
/app/assets/images/badges/ko-judge-2012.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/ko-judge-2012.png
--------------------------------------------------------------------------------
/app/assets/images/badges/ko-most-complete-2011.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/ko-most-complete-2011.png
--------------------------------------------------------------------------------
/app/assets/images/badges/ko-most-complete-2012.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/ko-most-complete-2012.png
--------------------------------------------------------------------------------
/app/assets/images/badges/ko-most-innovative-2011.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/ko-most-innovative-2011.png
--------------------------------------------------------------------------------
/app/assets/images/badges/ko-most-innovative-2012.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/ko-most-innovative-2012.png
--------------------------------------------------------------------------------
/app/assets/images/badges/ko-most-useful-2011.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/ko-most-useful-2011.png
--------------------------------------------------------------------------------
/app/assets/images/badges/ko-most-useful-2012.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/ko-most-useful-2012.png
--------------------------------------------------------------------------------
/app/assets/images/badges/ko-most-votes-2011.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/ko-most-votes-2011.png
--------------------------------------------------------------------------------
/app/assets/images/badges/ko-most-votes-2012.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/ko-most-votes-2012.png
--------------------------------------------------------------------------------
/app/assets/images/badges/komododragon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/komododragon.png
--------------------------------------------------------------------------------
/app/assets/images/badges/komododragon3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/komododragon3.png
--------------------------------------------------------------------------------
/app/assets/images/badges/labrador.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/labrador.png
--------------------------------------------------------------------------------
/app/assets/images/badges/labrador3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/labrador3.png
--------------------------------------------------------------------------------
/app/assets/images/badges/locked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/locked.png
--------------------------------------------------------------------------------
/app/assets/images/badges/mayor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/mayor.png
--------------------------------------------------------------------------------
/app/assets/images/badges/mongoose.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/mongoose.png
--------------------------------------------------------------------------------
/app/assets/images/badges/mongoose3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/mongoose3.png
--------------------------------------------------------------------------------
/app/assets/images/badges/moongoose-rails.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/moongoose-rails.png
--------------------------------------------------------------------------------
/app/assets/images/badges/more.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/more.png
--------------------------------------------------------------------------------
/app/assets/images/badges/more2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/more2.png
--------------------------------------------------------------------------------
/app/assets/images/badges/narwhal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/narwhal.png
--------------------------------------------------------------------------------
/app/assets/images/badges/narwhal3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/narwhal3.png
--------------------------------------------------------------------------------
/app/assets/images/badges/neo4j-challenge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/neo4j-challenge.png
--------------------------------------------------------------------------------
/app/assets/images/badges/neo4j-winner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/neo4j-winner.png
--------------------------------------------------------------------------------
/app/assets/images/badges/neo4j.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/neo4j.png
--------------------------------------------------------------------------------
/app/assets/images/badges/nephilakomaci.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/nephilakomaci.png
--------------------------------------------------------------------------------
/app/assets/images/badges/nephilakomaci3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/nephilakomaci3.png
--------------------------------------------------------------------------------
/app/assets/images/badges/octopussy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/octopussy.png
--------------------------------------------------------------------------------
/app/assets/images/badges/philanthropist.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/philanthropist.png
--------------------------------------------------------------------------------
/app/assets/images/badges/platypus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/platypus.png
--------------------------------------------------------------------------------
/app/assets/images/badges/platypus3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/platypus3.png
--------------------------------------------------------------------------------
/app/assets/images/badges/python.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/python.png
--------------------------------------------------------------------------------
/app/assets/images/badges/python3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/python3.png
--------------------------------------------------------------------------------
/app/assets/images/badges/railsberry.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/railsberry.png
--------------------------------------------------------------------------------
/app/assets/images/badges/railscamp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/railscamp.png
--------------------------------------------------------------------------------
/app/assets/images/badges/raven.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/raven.png
--------------------------------------------------------------------------------
/app/assets/images/badges/screencapture-achievements.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/screencapture-achievements.png
--------------------------------------------------------------------------------
/app/assets/images/badges/social-icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/social-icons.png
--------------------------------------------------------------------------------
/app/assets/images/badges/trex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/trex.png
--------------------------------------------------------------------------------
/app/assets/images/badges/trex3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/trex3.png
--------------------------------------------------------------------------------
/app/assets/images/badges/velociraptor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/velociraptor.png
--------------------------------------------------------------------------------
/app/assets/images/badges/velociraptor3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/velociraptor3.png
--------------------------------------------------------------------------------
/app/assets/images/badges/walrus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/walrus.png
--------------------------------------------------------------------------------
/app/assets/images/badges/wrocloverb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/badges/wrocloverb.png
--------------------------------------------------------------------------------
/app/assets/images/conference-room.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/conference-room.png
--------------------------------------------------------------------------------
/app/assets/images/happy-cat.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/happy-cat.jpg
--------------------------------------------------------------------------------
/app/assets/images/live-banner.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/live-banner.jpg
--------------------------------------------------------------------------------
/app/assets/images/live-banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/live-banner.png
--------------------------------------------------------------------------------
/app/assets/images/offline-holder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/offline-holder.png
--------------------------------------------------------------------------------
/app/assets/images/pop.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/assets/images/pop.mp3
--------------------------------------------------------------------------------
/app/assets/javascripts/analytics.js.coffee:
--------------------------------------------------------------------------------
1 | # https://developers.google.com/analytics/devguides/collection/analyticsjs/sending-hits
2 | document.addEventListener 'turbolinks:load', ->
3 | trackPageView()
4 | registerEventTracking()
5 | setTimeout registerBSATracking, 1500
6 |
7 | @trackPageView = ->
8 | if window.ga?
9 | ga('set', 'location', location.href.split('#')[0])
10 | ga('send', 'pageview', { "title": document.title })
11 |
12 | @registerEventTracking = ->
13 | # No JQuery, yay!
14 | document.querySelectorAll('a[ga-event-category]').forEach (item, i) ->
15 | item.addEventListener 'mousedown', (eventType) =>
16 | ga 'send', 'event',
17 | eventCategory: item.getAttribute("ga-event-category")
18 | eventAction: item.getAttribute("ga-event-action")
19 | eventLabel: item.getAttribute("ga-event-label")
20 | transport: 'beacon'
21 |
22 | return true
23 |
24 | @registerBSATracking = ->
25 | document.querySelectorAll('.bsap > a').forEach (item, i) ->
26 | item.addEventListener 'mousedown', (eventType) =>
27 | action = item.parentNode.parentNode.getAttribute('ga-location') + " - Banner"
28 | label = item.getAttribute("title") + ' - ' + item.getAttribute("id")
29 | ga 'send', 'event',
30 | eventCategory: 'Ads'
31 | eventAction: action
32 | eventLabel: label
33 | transport: 'beacon'
34 |
35 | return true
36 |
--------------------------------------------------------------------------------
/app/assets/javascripts/application_non_webpack.js.coffee:
--------------------------------------------------------------------------------
1 | # Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
2 | # about supported directives.
3 | #= require bsa
4 | #= require analytics
5 | #= require textarea_with_file_drop_support
6 |
7 | document.addEventListener 'turbolinks:load', ->
8 | els = document.getElementsByTagName('textarea')
9 | for el in els
10 | el.addEventListener 'input', resizeTextAreaForNewInput
11 |
12 | el = document.querySelector('.js-popout')
13 | if el
14 | el.addEventListener('click', openPopout)
15 |
16 | unless document.current_user_id?
17 | setUserId()
18 |
19 | @setUserId = ->
20 | userId = document.querySelector("meta[property='current_user:id']").content
21 | document.current_user_id = userId if userId?
22 |
23 | @promptUserSignInOn401 = (xhr) ->
24 | if xhr.status == 401
25 | window.location.replace('/signin')
26 | return
27 |
28 | @resizeTextAreaForNewInput = ->
29 | textarea_to_resize = this
30 | textarea_new_hight = textarea_to_resize.scrollHeight
31 | textarea_to_resize.style.cssText = 'height:auto;'
32 | textarea_to_resize.style.cssText = 'height:' + textarea_new_hight + 'px'
33 |
34 | openPopout = ->
35 | w = window.open(@href, @target || "_blank", 'menubar=no,toolbar=no,location=no,directories=no,status=no,scrollbars=no,resizable=no,dependent,width=400,height=600,left=0,top=0')
36 | return !w
37 |
--------------------------------------------------------------------------------
/app/assets/javascripts/application_static.js:
--------------------------------------------------------------------------------
1 | // This file is used in production to server generated JS assets. In development mode, we use the Webpack Dev Server
2 | // to provide assets. This allows for hot reloading of the JS and CSS.
3 | // See app/helpers/application_helper.rb for how the correct assets file is picked based on the Rails environment.
4 | // Those helpers are used here: app/views/layouts/application.html.erb
5 |
6 | // These assets are located in app/assets/webpack directory
7 |
8 | // Non-webpack assets incl turbolinks
9 | //= require application_non_webpack
10 |
--------------------------------------------------------------------------------
/app/assets/javascripts/bsa.js.coffee:
--------------------------------------------------------------------------------
1 | (->
2 | bsa = document.createElement('script')
3 | bsa.type = 'text/javascript'
4 | bsa.async = true
5 | bsa.src = document.location.protocol + '//s3.buysellads.com/ac/bsa.js'
6 | (document.getElementsByTagName('head')[0] or document.getElementsByTagName('body')[0]).appendChild(bsa)
7 |
8 | document.addEventListener 'turbolinks:load', ->
9 | if window._bsap?
10 | _bsap.reload()
11 | )()
12 |
--------------------------------------------------------------------------------
/app/assets/javascripts/textarea_with_file_drop_support.js.coffee:
--------------------------------------------------------------------------------
1 | document.addEventListener 'turbolinks:load', ->
2 | textarea = document.querySelector('textarea[dropped-files-url]')
3 | if textarea
4 | textarea.addEventListener 'drop', (e) ->
5 | e.preventDefault()
6 | url = textarea.getAttribute('dropped-files-url')
7 | files = e.target.files || e.dataTransfer.files
8 | file = files[0]
9 |
10 | addUploadPlaceholder(textarea, file)
11 | uploadFile url, file, (data)->
12 | replaceUploadPlaceholder(textarea, file, data)
13 |
14 | @uploadFile = (url, file, callback)->
15 | data = new FormData
16 | data.append 'file', file
17 |
18 | request = new XMLHttpRequest()
19 | request.open('POST', url, true)
20 | request.setRequestHeader('X-CSRF-Token', document.getElementsByName('csrf-token')[0].content)
21 | request.setRequestHeader('Accept', 'text/javascript')
22 | request.send(data)
23 | request.onload = ->
24 | if (request.status >= 200 && request.status < 400)
25 | data = JSON.parse(request.responseText)
26 | callback(data)
27 |
28 | @addUploadPlaceholder = (el, file) ->
29 | insertTextAtCursor(el, uploadPlaceholder(file.name))
30 |
31 | @insertTextAtCursor = (el, text)->
32 | originalText = el.value
33 | newText = originalText + "\n" + text
34 | el.value = newText
35 |
36 | @uploadPlaceholder = (name) ->
37 | "![Uploading... #{name}]()"
38 |
39 | @replaceUploadPlaceholder = (el, file, data) ->
40 | picture = data.picture
41 | placeholder = uploadPlaceholder(file.name)
42 | replacement = if picture.type.match(/image|pdf|png|psd/)
43 | ""
44 | else
45 | "[#{file.name}](#{picture.url})"
46 | el.value = el.value.replace(placeholder, replacement)
47 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/application_static.scss:
--------------------------------------------------------------------------------
1 | // Non-webpack assets
2 | @import 'application_non_webpack';
3 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/basscss.scss:
--------------------------------------------------------------------------------
1 | @import "basscss/defaults";
2 | @import "basscss/base-reset";
3 | @import "basscss/base-forms";
4 | @import "basscss/base-tables";
5 | @import "basscss/base-typography";
6 | @import "basscss/color-base";
7 | @import "basscss/color-forms";
8 | @import "basscss/color-tables";
9 | @import "basscss/btn";
10 | @import "basscss/btn-primary";
11 | @import "basscss/btn-outline";
12 | @import "basscss/type-scale";
13 | @import "basscss/utility-typography";
14 | @import "basscss/utility-layout";
15 | @import "basscss/align";
16 | @import "basscss/white-space";
17 | @import "basscss/positions";
18 | @import "basscss/responsive-states";
19 | @import "basscss/grid";
20 | @import "basscss/flex-object";
21 | @import "basscss/borders";
22 | @import "basscss/colors";
23 | @import "basscss/background-colors";
24 | @import "basscss/background-images";
25 | @import "basscss/border-colors";
26 | @import "basscss/btn-sizes";
27 | @import "basscss/color-forms-dark";
28 | @import "basscss/color-input-range";
29 | @import "basscss/color-progress";
30 | @import "basscss/highlight";
31 | @import "basscss/input-range";
32 | @import "basscss/progress";
33 | @import "basscss/responsive-white-space";
34 | @import "basscss/table-object";
35 | @import "basscss/ui-utility-groups";
36 | @import "basscss/utility-headings";
37 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/basscss/_align.scss:
--------------------------------------------------------------------------------
1 |
2 | // Converted Variables
3 |
4 |
5 | // Custom Media Query Variables
6 |
7 |
8 | /* Basscss Align */
9 |
10 | .align-baseline { vertical-align: baseline }
11 | .align-top { vertical-align: top }
12 | .align-middle { vertical-align: middle }
13 | .align-bottom { vertical-align: bottom }
--------------------------------------------------------------------------------
/app/assets/stylesheets/basscss/_background-images.scss:
--------------------------------------------------------------------------------
1 |
2 | // Converted Variables
3 |
4 |
5 | // Custom Media Query Variables
6 |
7 |
8 | /* Basscss Background Images */
9 |
10 | .bg-cover { background-size: cover }
11 | .bg-contain { background-size: contain }
12 |
13 | .bg-center { background-position: center }
14 | .bg-top { background-position: top }
15 | .bg-right { background-position: right }
16 | .bg-bottom { background-position: bottom }
17 | .bg-left { background-position: left }
--------------------------------------------------------------------------------
/app/assets/stylesheets/basscss/_base-reset.scss:
--------------------------------------------------------------------------------
1 |
2 | // Converted Variables
3 |
4 |
5 | // Custom Media Query Variables
6 |
7 |
8 | body { margin: 0 }
9 | img { max-width: 100% }
10 | svg { max-height: 100% }
--------------------------------------------------------------------------------
/app/assets/stylesheets/basscss/_positions.scss:
--------------------------------------------------------------------------------
1 |
2 | // Converted Variables
3 |
4 |
5 | // Custom Media Query Variables
6 |
7 |
8 | /* Basscss Positions */
9 |
10 | .relative { position: relative }
11 | .absolute { position: absolute }
12 | .fixed { position: fixed }
13 |
14 | .top-0 { top: 0 }
15 | .right-0 { right: 0 }
16 | .bottom-0 { bottom: 0 }
17 | .left-0 { left: 0 }
18 |
19 | .z1 { z-index: 1 }
20 | .z2 { z-index: 2 }
21 | .z3 { z-index: 3 }
22 | .z4 { z-index: 4 }
--------------------------------------------------------------------------------
/app/assets/stylesheets/basscss/_table-object.scss:
--------------------------------------------------------------------------------
1 |
2 | // Converted Variables
3 |
4 |
5 | // Custom Media Query Variables
6 |
7 | $breakpoint-sm: '(min-width: 40em)' !default;
8 | $breakpoint-md: '(min-width: 52em)' !default;
9 | $breakpoint-lg: '(min-width: 64em)' !default;
10 |
11 | /* Basscss Table Object */
12 |
13 | .table {
14 | display: table;
15 | width: 100%;
16 | }
17 | .table-cell {
18 | display: table-cell;
19 | vertical-align: middle;
20 | }
21 |
22 | .table-fixed { table-layout: fixed }
23 | @media #{$breakpoint-sm} {
24 |
25 | .sm-table {
26 | display: table;
27 | width: 100%;
28 | }
29 | .sm-table-cell {
30 | display: table-cell;
31 | vertical-align: middle;
32 | }
33 |
34 | }
35 | @media #{$breakpoint-md} {
36 |
37 | .md-table {
38 | display: table;
39 | width: 100%;
40 | }
41 | .md-table-cell {
42 | display: table-cell;
43 | vertical-align: middle;
44 | }
45 |
46 | }
47 | @media #{$breakpoint-lg} {
48 |
49 | .lg-table {
50 | display: table;
51 | width: 100%;
52 | }
53 | .lg-table-cell {
54 | display: table-cell;
55 | vertical-align: middle;
56 | }
57 |
58 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/basscss/_ui-utility-groups.scss:
--------------------------------------------------------------------------------
1 |
2 | // Converted Variables
3 |
4 | $border-width: 1px !default;
5 |
6 | // Custom Media Query Variables
7 |
8 |
9 | /* Basscss UI Utility Groups */
10 |
11 | .x-group-item { margin-left: -$border-width }
12 | .x-group-item:first-of-type { margin-left: 0 }
13 |
14 | .y-group-item { margin-top: -$border-width }
15 | .y-group-item:first-of-type { margin-top: 0 }
16 |
17 | .x-group-item:focus,
18 | .y-group-item:focus {
19 | position: relative;
20 | z-index: 1;
21 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/basscss/_utility-headings.scss:
--------------------------------------------------------------------------------
1 |
2 | // Converted Variables
3 |
4 | $h00: 4rem !default;
5 | $h0: 3rem !default;
6 | $h00-responsive: 8vw !default;
7 | $h0-responsive: 6vw !default;
8 | $h1-responsive: 4vw !default;
9 | $h00-responsive-max: 7.68rem !default;
10 | $h0-responsive-max: 5.76rem !default;
11 | $h1-responsive-max: 3.84rem !default;
12 |
13 | // Custom Media Query Variables
14 |
15 | $breakpoint-md: '(min-width: 52em)' !default;
16 | $breakpoint-xl: '(min-width: 96em)' !default;
17 |
18 | /* Basscss Utility Headings */
19 |
20 | .h00 { font-size: $h00 }
21 | .h0 { font-size: $h0 }
22 |
23 | @media #{$breakpoint-md} {
24 | .h00-responsive { font-size: $h00-responsive }
25 | .h0-responsive { font-size: $h0-responsive }
26 | .h1-responsive { font-size: $h1-responsive }
27 | }
28 |
29 | @media #{$breakpoint-xl} {
30 | .h00-responsive { font-size: $h00-responsive-max }
31 | .h0-responsive { font-size: $h0-responsive-max }
32 | .h1-responsive { font-size: $h1-responsive-max }
33 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/basscss/_utility-layout.scss:
--------------------------------------------------------------------------------
1 |
2 | // Converted Variables
3 |
4 |
5 | // Custom Media Query Variables
6 |
7 |
8 | /* Basscss Utility Layout */
9 |
10 | .inline { display: inline }
11 | .block { display: block }
12 | .inline-block { display: inline-block }
13 | .table { display: table }
14 | .table-cell { display: table-cell }
15 |
16 | .overflow-hidden { overflow: hidden }
17 | .overflow-scroll { overflow: scroll }
18 | .overflow-auto { overflow: auto }
19 |
20 | .overflow-y-scroll { overflow-y: scroll }
21 |
22 | .clearfix:before,
23 | .clearfix:after {
24 | content: " ";
25 | display: table
26 | }
27 | .clearfix:after { clear: both }
28 |
29 | .left { float: left }
30 | .right { float: right }
31 |
32 | @media #{$breakpoint-sm} {
33 | .sm-right {float: right; }
34 | }
35 |
36 | .fit { max-width: 100% }
37 |
38 | .border-box { box-sizing: border-box }
39 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/basscss/_utility-typography.scss:
--------------------------------------------------------------------------------
1 |
2 | // Converted Variables
3 |
4 |
5 | // Custom Media Query Variables
6 |
7 |
8 | /* Basscss Utility Typography */
9 |
10 | .bold { font-weight: $bold-font-weight /* Fallback value: bold */ }
11 | .regular { font-weight: normal }
12 | .italic { font-style: italic }
13 | .caps { text-transform: uppercase; letter-spacing: .2em; }
14 |
15 | .left-align { text-align: left }
16 | .center { text-align: center }
17 | .right-align { text-align: right }
18 | .justify { text-align: justify }
19 |
20 | .nowrap { white-space: nowrap }
21 | .break-word { word-wrap: break-word }
22 |
23 | .truncate {
24 | max-width: 100%;
25 | overflow: hidden;
26 | text-overflow: ellipsis;
27 | white-space: nowrap;
28 | }
29 |
30 | .list-reset {
31 | list-style: none;
32 | padding-left: 0;
33 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/content.scss:
--------------------------------------------------------------------------------
1 | .content{
2 | line-height: 25px;
3 |
4 | &.small{
5 | }
6 |
7 | a {
8 | word-break: break-all;
9 | }
10 |
11 | img{
12 | max-width: 100%;
13 | display: block;
14 | margin: 0 auto;
15 | text-align: center;
16 | }
17 |
18 | strong {
19 | font-weight: normal;
20 | }
21 |
22 | hr {
23 | border: 0;
24 | height: 2px;
25 | }
26 |
27 | em {
28 | font-weight: normal;
29 | font-style: normal;
30 | }
31 |
32 | pre {
33 | background-color: rgba(250,250,250,0.7);
34 |
35 | code{
36 | font-size: 13px;
37 | line-height: 1.4;
38 | }
39 | }
40 |
41 | code {
42 | // white-space:nowrap;
43 | a {
44 | }
45 | }
46 |
47 | p {
48 |
49 | &:last-child {
50 | margin-bottom: 0;
51 | }
52 | }
53 |
54 | h1, h2, h3, h4 {
55 |
56 | &:first-child {
57 | padding-top: 0;
58 | margin-top: 0;
59 | }
60 |
61 | a {
62 | }
63 | }
64 |
65 | blockquote {
66 | p {
67 | margin: 0;
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/dropdown.scss:
--------------------------------------------------------------------------------
1 | .dropdown {
2 | position: relative;
3 | display: inline-block;
4 | }
5 |
6 | .dropdown-content {
7 | display: none;
8 | position: absolute;
9 | }
10 |
11 | .dropdown:hover .dropdown-content {
12 | display: block;
13 | }
14 |
15 | .dropdown:hover .btn {
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/font-awesome.scss:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome
3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
4 | */
5 |
6 | @function fa-font-path($path) {
7 | @return font-path($path);
8 | }
9 |
10 | $fa-sass-asset-helper: true;
11 |
12 | @import "font-awesome/variables";
13 | @import "font-awesome/mixins";
14 | @import "font-awesome/path";
15 | @import "font-awesome/core";
16 | @import "font-awesome/larger";
17 | @import "font-awesome/fixed-width";
18 | @import "font-awesome/list";
19 | @import "font-awesome/bordered-pulled";
20 | @import "font-awesome/animated";
21 | @import "font-awesome/rotated-flipped";
22 | @import "font-awesome/stacked";
23 | @import "font-awesome/icons";
24 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/font-awesome/_animated.scss:
--------------------------------------------------------------------------------
1 | // Spinning Icons
2 | // --------------------------
3 |
4 | .#{$fa-css-prefix}-spin {
5 | -webkit-animation: fa-spin 2s infinite linear;
6 | animation: fa-spin 2s infinite linear;
7 | }
8 |
9 | .#{$fa-css-prefix}-pulse {
10 | -webkit-animation: fa-spin 1s infinite steps(8);
11 | animation: fa-spin 1s infinite steps(8);
12 | }
13 |
14 | @-webkit-keyframes fa-spin {
15 | 0% {
16 | -webkit-transform: rotate(0deg);
17 | transform: rotate(0deg);
18 | }
19 | 100% {
20 | -webkit-transform: rotate(359deg);
21 | transform: rotate(359deg);
22 | }
23 | }
24 |
25 | @keyframes fa-spin {
26 | 0% {
27 | -webkit-transform: rotate(0deg);
28 | transform: rotate(0deg);
29 | }
30 | 100% {
31 | -webkit-transform: rotate(359deg);
32 | transform: rotate(359deg);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/font-awesome/_bordered-pulled.scss:
--------------------------------------------------------------------------------
1 | // Bordered & Pulled
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix}-border {
5 | padding: .2em .25em .15em;
6 | border: solid .08em $fa-border-color;
7 | border-radius: .1em;
8 | }
9 |
10 | .#{$fa-css-prefix}-pull-left { float: left; }
11 | .#{$fa-css-prefix}-pull-right { float: right; }
12 |
13 | .#{$fa-css-prefix} {
14 | &.#{$fa-css-prefix}-pull-left { margin-right: .3em; }
15 | &.#{$fa-css-prefix}-pull-right { margin-left: .3em; }
16 | }
17 |
18 | /* Deprecated as of 4.4.0 */
19 | .pull-right { float: right; }
20 | .pull-left { float: left; }
21 |
22 | .#{$fa-css-prefix} {
23 | &.pull-left { margin-right: .3em; }
24 | &.pull-right { margin-left: .3em; }
25 | }
26 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/font-awesome/_core.scss:
--------------------------------------------------------------------------------
1 | // Base Class Definition
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix} {
5 | display: inline-block;
6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration
7 | font-size: inherit; // can't have font-size inherit on line above, so need to override
8 | text-rendering: auto; // optimizelegibility throws things off #1094
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 | }
12 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/font-awesome/_extras.scss:
--------------------------------------------------------------------------------
1 | /* EXTRAS
2 | * -------------------------- */
3 |
4 | /* Stacked and layered icon */
5 |
6 | /* Animated rotating icon */
7 | .#{$fa-css-prefix}-spin {
8 | -webkit-animation: spin 2s infinite linear;
9 | -moz-animation: spin 2s infinite linear;
10 | -o-animation: spin 2s infinite linear;
11 | animation: spin 2s infinite linear;
12 | }
13 |
14 | @-moz-keyframes spin {
15 | 0% { -moz-transform: rotate(0deg); }
16 | 100% { -moz-transform: rotate(359deg); }
17 | }
18 | @-webkit-keyframes spin {
19 | 0% { -webkit-transform: rotate(0deg); }
20 | 100% { -webkit-transform: rotate(359deg); }
21 | }
22 | @-o-keyframes spin {
23 | 0% { -o-transform: rotate(0deg); }
24 | 100% { -o-transform: rotate(359deg); }
25 | }
26 | @-ms-keyframes spin {
27 | 0% { -ms-transform: rotate(0deg); }
28 | 100% { -ms-transform: rotate(359deg); }
29 | }
30 | @keyframes spin {
31 | 0% { transform: rotate(0deg); }
32 | 100% { transform: rotate(359deg); }
33 | }
34 |
35 |
36 | // Icon rotations & flipping
37 | // -------------------------
38 |
39 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); }
40 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); }
41 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); }
42 |
43 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); }
44 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); }
45 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/font-awesome/_fixed-width.scss:
--------------------------------------------------------------------------------
1 | // Fixed Width Icons
2 | // -------------------------
3 | .#{$fa-css-prefix}-fw {
4 | width: (18em / 14);
5 | text-align: center;
6 | }
7 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/font-awesome/_larger.scss:
--------------------------------------------------------------------------------
1 | // Icon Sizes
2 | // -------------------------
3 |
4 | /* makes the font 33% larger relative to the icon container */
5 | .#{$fa-css-prefix}-lg {
6 | font-size: (4em / 3);
7 | line-height: (3em / 4);
8 | vertical-align: -15%;
9 | }
10 | .#{$fa-css-prefix}-2x { font-size: 2em; }
11 | .#{$fa-css-prefix}-3x { font-size: 3em; }
12 | .#{$fa-css-prefix}-4x { font-size: 4em; }
13 | .#{$fa-css-prefix}-5x { font-size: 5em; }
14 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/font-awesome/_list.scss:
--------------------------------------------------------------------------------
1 | // List Icons
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix}-ul {
5 | padding-left: 0;
6 | margin-left: $fa-li-width;
7 | list-style-type: none;
8 | > li { position: relative; }
9 | }
10 | .#{$fa-css-prefix}-li {
11 | position: absolute;
12 | left: -$fa-li-width;
13 | width: $fa-li-width;
14 | top: (2em / 14);
15 | text-align: center;
16 | &.#{$fa-css-prefix}-lg {
17 | left: -$fa-li-width + (4em / 14);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/font-awesome/_mixins.scss:
--------------------------------------------------------------------------------
1 | // Mixins
2 | // --------------------------
3 |
4 | @mixin fa-icon() {
5 | display: inline-block;
6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration
7 | font-size: inherit; // can't have font-size inherit on line above, so need to override
8 | text-rendering: auto; // optimizelegibility throws things off #1094
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 | }
12 |
13 | @mixin fa-icon-rotate($degrees, $rotation) {
14 | -webkit-transform: rotate($degrees);
15 | -ms-transform: rotate($degrees);
16 | transform: rotate($degrees);
17 | }
18 |
19 | @mixin fa-icon-flip($horiz, $vert, $rotation) {
20 | -webkit-transform: scale($horiz, $vert);
21 | -ms-transform: scale($horiz, $vert);
22 | transform: scale($horiz, $vert);
23 | }
24 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/font-awesome/_path.scss:
--------------------------------------------------------------------------------
1 | /* FONT PATH
2 | * -------------------------- */
3 |
4 | @font-face {
5 | font-family: 'FontAwesome';
6 | src: url(if($fa-sass-asset-helper, fa-font-path('#{$fa-font-path}#{$fa-font-name}.eot?v=#{$fa-version}'), '#{$fa-font-path}#{$fa-font-name}.eot?v=#{$fa-version}'));
7 | src: url(if($fa-sass-asset-helper, fa-font-path('#{$fa-font-path}#{$fa-font-name}.eot?v=#{$fa-version}#iefix'), '#{$fa-font-path}#{$fa-font-name}.eot?v=#{$fa-version}#iefix')) format('embedded-opentype'),
8 | url(if($fa-sass-asset-helper, fa-font-path('#{$fa-font-path}#{$fa-font-name}.woff2?v=#{$fa-version}'), '#{$fa-font-path}#{$fa-font-name}.woff2?v=#{$fa-version}')) format('woff2'),
9 | url(if($fa-sass-asset-helper, fa-font-path('#{$fa-font-path}#{$fa-font-name}.woff?v=#{$fa-version}'), '#{$fa-font-path}#{$fa-font-name}.woff?v=#{$fa-version}')) format('woff'),
10 | url(if($fa-sass-asset-helper, fa-font-path('#{$fa-font-path}#{$fa-font-name}.ttf?v=#{$fa-version}'), '#{$fa-font-path}#{$fa-font-name}.ttf?v=#{$fa-version}')) format('truetype'),
11 | url(if($fa-sass-asset-helper, fa-font-path('#{$fa-font-path}#{$fa-font-name}.svg?v=#{$fa-version}#fontawesomeregular'), '#{$fa-font-path}#{$fa-font-name}.svg?v=#{$fa-version}#fontawesomeregular')) format('svg');
12 | font-weight: normal;
13 | font-style: normal;
14 | }
15 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/font-awesome/_rotated-flipped.scss:
--------------------------------------------------------------------------------
1 | // Rotated & Flipped Icons
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); }
5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); }
6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); }
7 |
8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); }
9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); }
10 |
11 | // Hook for IE8-9
12 | // -------------------------
13 |
14 | :root .#{$fa-css-prefix}-rotate-90,
15 | :root .#{$fa-css-prefix}-rotate-180,
16 | :root .#{$fa-css-prefix}-rotate-270,
17 | :root .#{$fa-css-prefix}-flip-horizontal,
18 | :root .#{$fa-css-prefix}-flip-vertical {
19 | filter: none;
20 | }
21 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/font-awesome/_stacked.scss:
--------------------------------------------------------------------------------
1 | // Stacked Icons
2 | // -------------------------
3 |
4 | .#{$fa-css-prefix}-stack {
5 | position: relative;
6 | display: inline-block;
7 | width: 2em;
8 | height: 2em;
9 | line-height: 2em;
10 | vertical-align: middle;
11 | }
12 | .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x {
13 | position: absolute;
14 | left: 0;
15 | width: 100%;
16 | text-align: center;
17 | }
18 | .#{$fa-css-prefix}-stack-1x { line-height: inherit; }
19 | .#{$fa-css-prefix}-stack-2x { font-size: 2em; }
20 | .#{$fa-css-prefix}-inverse { color: $fa-inverse; }
21 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/minimal.scss:
--------------------------------------------------------------------------------
1 | html, body {
2 | height: 100%;
3 | }
4 |
5 | .full-height {
6 | height: 100%;
7 | }
8 |
--------------------------------------------------------------------------------
/app/controllers/application_record.rb:
--------------------------------------------------------------------------------
1 | class ApplicationRecord < ActiveRecord::Base
2 | self.abstract_class = true
3 | end
4 |
--------------------------------------------------------------------------------
/app/controllers/badges_controller.rb:
--------------------------------------------------------------------------------
1 | class BadgesController < ApplicationController
2 | end
3 |
--------------------------------------------------------------------------------
/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/controllers/concerns/.keep
--------------------------------------------------------------------------------
/app/controllers/hooks_controller.rb:
--------------------------------------------------------------------------------
1 | class HooksController < ApplicationController
2 | skip_before_action :verify_authenticity_token
3 |
4 | def sendgrid
5 | params[:_json].each do |data|
6 | puts data
7 | process_unsubscribe(data) if data['event'] == 'unsubscribe'
8 | end
9 |
10 | head(200)
11 | end
12 |
13 | # private
14 |
15 | def process_unsubscribe(data)
16 | User.where(email: data['email']).update_all(marketing_list: nil)
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/app/controllers/job_subscriptions_controller.rb:
--------------------------------------------------------------------------------
1 | class JobSubscriptionsController < ApplicationController
2 | def new
3 | @subscription = JobSubscription.new
4 | end
5 |
6 | def create
7 | @subscription = JobSubscription.new(subscription_params)
8 | if !@subscription.save
9 | render action: 'new'
10 | return
11 | end
12 |
13 | @subscription.charge!(params['stripeToken'])
14 |
15 | Slack.notify!(':moneybag:', "#{@subscription.company_name} (#{@subscription.contact_email}) just subscribed to post all jobs at #{@subscription.jobs_url}")
16 |
17 | flash[:notice] = "You're all set! You will receive a receipt and email shortly once we post your first jobs to Coderwall."
18 | redirect_to jobs_path
19 |
20 | rescue Stripe::CardError => e
21 | flash[:notice] = e.message
22 | redirect_to new_job_subscription_path(@subscription)
23 | end
24 |
25 | # private
26 |
27 | def subscription_params
28 | params.require(:job_subscription).permit(
29 | :jobs_url,
30 | :company_name,
31 | :contact_email,
32 | :stripe_customer_id,
33 | )
34 | end
35 |
36 | end
37 |
--------------------------------------------------------------------------------
/app/controllers/jobs_controller.rb:
--------------------------------------------------------------------------------
1 | class JobsController < ApplicationController
2 | def index
3 | if [:show_fulltime, :show_parttime, :show_contract].any?{|s| params[s].blank? }
4 | params[:show_fulltime] = 'true'
5 | params[:show_parttime] = 'true'
6 | params[:show_contract] = 'true'
7 | params[:show_remote] = 'false'
8 | end
9 | roles = []
10 | roles.push(Job::FULLTIME) if params[:show_fulltime] == 'true'
11 | roles.push(Job::PARTTIME) if params[:show_parttime] == 'true'
12 | roles.push(Job::CONTRACT) if params[:show_contract] == 'true'
13 | @jobs = Job.active.order(created_at: :desc)
14 | @jobs = @jobs.where('jobs.role_type in (?)', roles)
15 | @jobs = @jobs.where(location: 'Remote') if params[:show_remote] == 'true'
16 | @jobs = @jobs.where('jobs.location ilike :q or jobs.title ilike :q or jobs.company ilike :q', q: "%#{params[:q]}%") unless params[:q].blank?
17 |
18 | if params[:posted]
19 | @jobs = @jobs.where.not(id: params[:posted])
20 | @featured = Job.find(params[:posted])
21 | end
22 |
23 | respond_to :html
24 | end
25 |
26 | def new
27 | @job = Job.new
28 | end
29 |
30 | def create
31 | @job = Job.new(job_params)
32 | if !@job.save
33 | render action: 'new'
34 | return
35 | end
36 |
37 | @job.charge!(params['stripeToken'])
38 | render json: @job
39 |
40 | rescue Stripe::CardError => e
41 | render json: { error: e.message }, status: 400
42 | end
43 |
44 | # private
45 |
46 | def job_params
47 | params.require(:job).permit(
48 | :author_email,
49 | :author_name,
50 | :company_logo,
51 | :company_url,
52 | :company,
53 | :location,
54 | :role_type,
55 | :source,
56 | :title
57 | )
58 | end
59 |
60 | end
61 |
--------------------------------------------------------------------------------
/app/controllers/likes_controller.rb:
--------------------------------------------------------------------------------
1 | class LikesController < ApplicationController
2 | before_action :require_login, only: :create
3 |
4 | def create
5 | @likeable = find_likeable
6 | @likeable.likes.create(user: current_user) unless current_user.likes?(@likeable)
7 | @likeable.try(:subscribe!, current_user)
8 | respond_to do |format|
9 | format.json { render(json: @likeable.likes_count, status: :ok) }
10 | end
11 | end
12 |
13 | protected
14 | def find_likeable
15 | params.each do |name, value|
16 | if name =~ /(.+)_id$/
17 | klass = $1.classify.constantize
18 | if klass == Protip
19 | return klass.find_by_public_id!(value)
20 | else
21 | return klass.find(value)
22 | end
23 | end
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/app/controllers/pages_controller.rb:
--------------------------------------------------------------------------------
1 | class PagesController < ApplicationController
2 | skip_before_action :verify_authenticity_token
3 |
4 | def show
5 | args = params.permit(:page, :layout)
6 | status = 200
7 | status = 404 if args[:page].to_s == 'not_found'
8 | respond_to do |format|
9 | format.html { render(action: args[:page], status: status) }
10 | format.all { head(:not_found) }
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/app/controllers/pictures_controller.rb:
--------------------------------------------------------------------------------
1 | class PicturesController < ApplicationController
2 | before_action :require_login
3 |
4 | def create
5 | picture = current_user.pictures.create!(file: params[:file])
6 | render json: picture
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/app/controllers/sponsors_controller.rb:
--------------------------------------------------------------------------------
1 | class SponsorsController < ApplicationController
2 | def show
3 | @sponsors = Sponsor.ads_for(remote_ip)
4 | render json: @sponsors
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/app/controllers/subscribers_controller.rb:
--------------------------------------------------------------------------------
1 | class SubscribersController < ApplicationController
2 | # TODO: shouldn't need this, not sure why X-CSRF-Token header isn't working
3 | skip_before_action :verify_authenticity_token
4 |
5 | before_action :require_login, only: [:create, :destroy, :mute]
6 |
7 | def create
8 | @protip = Protip.find(params[:protip_id])
9 | @protip.subscribe!(current_user)
10 | render json: @protip, root: false
11 | end
12 |
13 | def destroy
14 | @protip = Protip.find(params[:protip_id])
15 | @protip.unsubscribe!(current_user)
16 | render json: @protip, root: false
17 | end
18 |
19 | def mute
20 | @protip = Protip.find_by_public_id!(params[:protip_id])
21 | if params[:signature] != current_user.unsubscribe_signature
22 | flash[:notice] = "Unsubscribe link is no longer valid"
23 | else
24 | @protip.unsubscribe!(current_user)
25 | flash[:notice] = "You will no longer receive new comment emails"
26 | end
27 | redirect_to seo_protip_path(@protip)
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/app/controllers/teams_controller.rb:
--------------------------------------------------------------------------------
1 | class TeamsController < ApplicationController
2 |
3 | def show
4 | if params[:slug] == 'random'
5 | @team = Team.order("random()").first
6 | else
7 | @team = Team.find_by_slug!(params[:slug])
8 | end
9 | end
10 |
11 | end
12 |
--------------------------------------------------------------------------------
/app/helpers/font_awesome_helper.rb:
--------------------------------------------------------------------------------
1 | module FontAwesomeHelper
2 | def icon(icon, text = nil, html_options = {})
3 | text, html_options = nil, text if text.is_a?(Hash)
4 |
5 | content_class = "fa fa-#{icon}"
6 | content_class << " #{html_options[:class]}" if html_options.key?(:class)
7 | html_options[:class] = content_class
8 |
9 | html = content_tag(:i, nil, html_options)
10 | html << ' ' << text.to_s unless text.blank?
11 | html
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/app/helpers/jobs_helper.rb:
--------------------------------------------------------------------------------
1 | module JobsHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/likes_helper.rb:
--------------------------------------------------------------------------------
1 | module LikesHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/users_helper.rb:
--------------------------------------------------------------------------------
1 | module UsersHelper
2 |
3 | def finishing_signup?
4 | params[:finish_signup] == true
5 | end
6 |
7 | def current_user_can_edit?(object)
8 | signed_in? && current_user.can_edit?(object)
9 | end
10 |
11 | def show_protips?
12 | !show_comments?
13 | end
14 |
15 | def show_comments?
16 | params[:comments].present?
17 | end
18 |
19 | def show_protips_active
20 | return 'active ' if show_protips?
21 | end
22 |
23 | def show_comments_active
24 | return 'active ' if show_comments?
25 | end
26 |
27 | def avatar_url(user)
28 | image_url user.avatar.url
29 | end
30 |
31 | def avatar_url_tag(user, options = {})
32 | image_tag(avatar_url(user), options) if user.avatar?
33 | end
34 |
35 | end
36 |
--------------------------------------------------------------------------------
/app/lib/avatar_uploader.rb:
--------------------------------------------------------------------------------
1 | class AvatarUploader < CarrierWave::Uploader::Base
2 | include CarrierWave::MiniMagick
3 |
4 | process resize_and_pad: [100, 100]
5 |
6 | def extension_white_list
7 | %w(jpg jpeg gif png)
8 | end
9 |
10 | def store_dir
11 | "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
12 | end
13 |
14 | end
15 |
--------------------------------------------------------------------------------
/app/lib/cloudfront_constraint.rb:
--------------------------------------------------------------------------------
1 | class CloudfrontConstraint
2 | def matches?(request)
3 | request.env['HTTP_USER_AGENT'] == 'Amazon CloudFront'
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/lib/picture_uploader.rb:
--------------------------------------------------------------------------------
1 | class PictureUploader < CarrierWave::Uploader::Base
2 | include CarrierWave::MiniMagick
3 |
4 | process :auto_orient
5 |
6 | def auto_orient
7 | manipulate! do |image|
8 | image.collapse!
9 | image.auto_orient
10 | image
11 | end
12 | end
13 |
14 | def extension_white_list
15 | %w(jpg jpeg gif png psd pdf)
16 | end
17 |
18 | def store_dir
19 | "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
20 | end
21 |
22 | end
23 |
--------------------------------------------------------------------------------
/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | class ApplicationMailer < ActionMailer::Base
2 | def prevent_delivery
3 | mail.perform_deliveries = false
4 | end
5 |
6 | def list_headers(reply_address, thread_parts, message_parts, archive_url)
7 | thread_id = thread_parts.join('/')
8 | thread_address = "<#{thread_id}@assembly.com>"
9 | message_id = "<#{message_parts.join('/')}@assembly.com>"
10 |
11 | {
12 | "Reply-To" => "#{thread_parts.join('/')} <#{reply_address}>",
13 |
14 | "Message-ID" => message_id,
15 | "In-Reply-To" => thread_address,
16 | "References" => thread_address,
17 |
18 | "List-ID" => "#{thread_id} <#{thread_parts.join('.')}.assembly.com>",
19 | "List-Archive" => archive_url,
20 | "List-Post" => "",
21 | "Precedence" => "list",
22 | }
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/app/mailers/comment_mailer.rb:
--------------------------------------------------------------------------------
1 | class CommentMailer < ApplicationMailer
2 | def new_comment(to, comment)
3 | @to = to
4 | @comment = comment
5 |
6 | return prevent_delivery if prevent_email?(@to)
7 |
8 | if rewrite = ENV['REWRITE_EMAILS']
9 | @to.email = rewrite
10 | end
11 |
12 | @author = @comment.user
13 | @article = @comment.article
14 | @reply = SecureReplyTo.new(Article, @article.id, @to.username)
15 |
16 | thread_parts = [@article.id]
17 | message_parts = [@comment.id]
18 | options = list_headers(
19 | @reply,
20 | thread_parts,
21 | message_parts,
22 | url_for(@comment.url_params)
23 | ).merge(
24 | from: "#{@author.display_name} ",
25 | to: "#{@to.display_name} <#{@to.email}>",
26 | subject: "New Comment [Re: #{@article.title}]"
27 | )
28 |
29 | mail(options) do |format|
30 | format.html { render layout: nil }
31 | end
32 | end
33 |
34 | protected
35 |
36 | def prevent_email?(user)
37 | user.banned_at? || user.email_invalid_at?
38 | end
39 | end
40 |
--------------------------------------------------------------------------------
/app/mailers/user_mailer.rb:
--------------------------------------------------------------------------------
1 | class UserMailer < ActionMailer::Base
2 | default from: "support@coderwall.com"
3 |
4 | def destroy_email(user)
5 | @user = user
6 | mail(to: 'support@coderwall.com', subject: "#{@user.username} deleted their account")
7 | end
8 |
9 | def partnership_expired(user)
10 | @user = user
11 | mail(to: user.partner_email, bcc: 'support@coderwall.com', subject: "Important Partner update on Coderwall")
12 | end
13 |
14 | end
15 |
--------------------------------------------------------------------------------
/app/models/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/models/.keep
--------------------------------------------------------------------------------
/app/models/badge.rb:
--------------------------------------------------------------------------------
1 | class Badge < ApplicationRecord
2 | belongs_to :user, required: true
3 |
4 | def path
5 | "badges/#{image_name}"
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/app/models/comment.rb:
--------------------------------------------------------------------------------
1 | class Comment < ApplicationRecord
2 | include TimeAgoInWordsCacheBuster
3 | paginates_per 10
4 |
5 | VIDEO_LAG = 25.seconds # TODO: measure the real lag value
6 |
7 | after_create :auto_like_article_for_author
8 |
9 | belongs_to :user, touch: true, required: true
10 | belongs_to :article, touch: true, required: true
11 | has_many :likes, as: :likable, dependent: :destroy
12 |
13 | validates :body, length: { minimum: 2 }
14 |
15 | scope :on_protips, -> { joins(:article).where(protips: {type: 'Protip'}) }
16 | scope :visible_to, ->(user) { where(bad_content: false) unless user.try(:bad_user) }
17 | scope :recently_created, ->(count=10) { order(created_at: :desc).limit(count)}
18 |
19 | def auto_like_article_for_author
20 | article.likes.create(user: user) unless user.likes?(article)
21 | end
22 |
23 | def dom_id
24 | ActionView::RecordIdentifier.dom_id(self)
25 | end
26 |
27 | def hearts_count
28 | likes_count
29 | end
30 |
31 | def url_params
32 | [article, anchor: dom_id]
33 | end
34 |
35 | def video_timestamp
36 | (created_at - VIDEO_LAG).to_i
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/app/models/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/app/models/concerns/.keep
--------------------------------------------------------------------------------
/app/models/concerns/time_ago_in_words_cache_buster.rb:
--------------------------------------------------------------------------------
1 | module TimeAgoInWordsCacheBuster
2 | extend ActiveSupport::Concern
3 |
4 | HOUR_IN_MINUTES = 60
5 | DAY_IN_MINUTES = HOUR_IN_MINUTES * 24
6 | WEEK_IN_MINUTES = DAY_IN_MINUTES * 7
7 | MONTH_IN_MINUTES = WEEK_IN_MINUTES * 4
8 |
9 | def cache_key
10 | "#{super}/t-#{time_interval_key}"
11 | end
12 |
13 | def time_interval_key
14 | minutes = ((Time.now - updated_at) / 60.0).round
15 | if minutes <= HOUR_IN_MINUTES
16 | (Time.now - updated_at) / 60.0
17 | elsif minutes <= DAY_IN_MINUTES
18 | (minutes / DAY_IN_MINUTES).round
19 | elsif minutes <= WEEK_IN_MINUTES
20 | (minutes / DAY_IN_MINUTES).round
21 | else
22 | (minutes / MONTH_IN_MINUTES).round
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/app/models/concerns/view_count_cache_buster.rb:
--------------------------------------------------------------------------------
1 | module ViewCountCacheBuster
2 | extend ActiveSupport::Concern
3 |
4 | def cache_key
5 | "#{super}/v-#{view_count_key}"
6 | end
7 |
8 | def view_count_key
9 | if views_count < 150
10 | bust_cache_every_three_views = views_count / 3
11 | else
12 | bust_cache_every_twenty_views = views_count / 20
13 | end
14 | end
15 |
16 | end
17 |
--------------------------------------------------------------------------------
/app/models/github.rb:
--------------------------------------------------------------------------------
1 | class Github
2 | class << self
3 | def user_comment_log
4 | fetch('/issues/comments').collect do |comment|
5 | {
6 | username: comment['user']['login'],
7 | user_id: comment['user']['id'],
8 | created_at: Time.parse(comment['created_at'])
9 | }
10 | end
11 | end
12 |
13 | def user_pr_log
14 | fetch('/pulls', state: 'all').collect do |pr|
15 | {
16 | username: pr['user']['login'],
17 | user_id: pr['user']['id'],
18 | created_at: Time.parse(pr['created_at'])
19 | }
20 | end
21 | end
22 |
23 | def user_issue_log
24 | fetch('/issues', state: 'all').collect do |pr|
25 | {
26 | username: pr['user']['login'],
27 | user_id: pr['user']['id'],
28 | created_at: Time.parse(pr['created_at'])
29 | }
30 | end
31 | end
32 |
33 | def fetch(path, options = {}, page = 1)
34 | repo = 'coderwall-next'
35 | owner = 'coderwall'
36 | connection = Faraday.new(url: "https://api.github.com")
37 | results = []
38 | while true
39 | puts "[GitHub] Fetch #{path}: #{page}"
40 | response = connection.get("/repos/#{owner}/#{repo}/#{path}", options.merge({page: page}))
41 | results << JSON.parse(response.body)
42 | break if (response.headers['link'].to_s =~ /next/) == nil
43 | page = page + 1
44 | end
45 | results.flatten
46 | end
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/app/models/job.rb:
--------------------------------------------------------------------------------
1 | class Job < ApplicationRecord
2 | CENTS_PER_MONTH = 29900
3 | COST = CENTS_PER_MONTH/100
4 | FULLTIME = 'Full Time'
5 | PARTTIME = 'Part Time'
6 | CONTRACT = 'Contract'
7 | ROLES = [FULLTIME, PARTTIME, CONTRACT]
8 |
9 | validates :author_email, presence: true
10 | validates :author_name, presence: true
11 | validates :company, presence: true
12 | validates :location, presence: true
13 | validates :role_type, presence: true
14 | validates :source, presence: true
15 | validates :title, presence: true
16 |
17 | scope :active, -> { where("expires_at > ?", Time.now) }
18 | scope :latest, ->(count=1) { order(created_at: :desc).limit(count) }
19 | scope :featured, ->(count=1) { active.order("RANDOM()").limit(count) }
20 |
21 | def charge!(token)
22 | charge = Stripe::Charge.create(
23 | amount: CENTS_PER_MONTH, # amount in cents, again
24 | currency: "usd",
25 | source: token,
26 | description: "coderwall.com job posting"
27 | )
28 |
29 | update!(
30 | stripe_charge: charge.id,
31 | expires_at: 1.month.from_now
32 | )
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/app/models/job_subscription.rb:
--------------------------------------------------------------------------------
1 | class JobSubscription < ApplicationRecord
2 | CENTS_PER_MONTH = (ENV['JOB_SUBSCRIPTION_CENTS'].try(:to_i))
3 |
4 | validates :jobs_url, presence: true
5 | validates :company_name, presence: true
6 | validates :contact_email, presence: true
7 |
8 | def charge!(token)
9 | customer = Stripe::Customer.create(
10 | source: token,
11 | plan: (ENV['JOBS_PLAN'] || 'jobs_monthly'),
12 | email: contact_email,
13 | )
14 |
15 | update!(
16 | stripe_customer_id: customer.id,
17 | subscribed_at: Time.now,
18 | )
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/app/models/like.rb:
--------------------------------------------------------------------------------
1 | class Like < ApplicationRecord
2 | belongs_to :user, required: true
3 | belongs_to :likable, polymorphic: true, counter_cache: true, touch: true, required: true
4 |
5 | def dom_id
6 | #Mimics ActionView::RecordIdentifier.dom_id without killing the database
7 | "#{temporarily_hacked_likable_type}_#{likable_id}".downcase
8 | end
9 |
10 | def temporarily_hacked_likable_type
11 | # the dom_id for these is protip, but in the database they're stored as Articles
12 | # this hack prevents hearting streams but that's ok for now
13 | likable_type == 'Article' ? 'Protip' : likable_type
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/models/picture.rb:
--------------------------------------------------------------------------------
1 | class Picture < ApplicationRecord
2 | mount_uploader :file, PictureUploader
3 |
4 | belongs_to :user, required: true
5 | end
6 |
--------------------------------------------------------------------------------
/app/models/protip.rb:
--------------------------------------------------------------------------------
1 | class Protip < Article
2 | end
3 |
--------------------------------------------------------------------------------
/app/models/secure_reply_to.rb:
--------------------------------------------------------------------------------
1 | require 'openssl'
2 |
3 | class SecureReplyTo
4 | attr_reader :object_type, :object_id, :user_id
5 |
6 | def initialize(object_type, object_id, user_id)
7 | @object_type, @object_id, @user_id = object_type.to_s, object_id, user_id
8 | @object_type = @object_type.underscore # it gets downcased somewhere in the pipe
9 | @user_id = @user_id.downcase
10 | @secret = ENV.fetch('REPLY_SECRET', 'r3ply_secr3t')
11 | end
12 |
13 | def self.parse(address)
14 | _, object_type, object_id, signature, user_id = address.split(/[@\+]/)
15 | address = new(object_type, object_id, user_id)
16 | raise 'Invalid Signature' if address.signature != signature
17 | address
18 | end
19 |
20 | def signature
21 | digest = OpenSSL::Digest.new('sha1')
22 | data = [object_id, user_id].join
23 | OpenSSL::HMAC.hexdigest(digest, @secret, data)
24 | end
25 |
26 | def find_thread!
27 | object_type.camelcase.constantize.find(object_id)
28 | end
29 |
30 | def to_s
31 | "reply+#{@object_type}+#{@object_id}+#{signature}+#{@user_id}@m.coderwall.com"
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/app/models/sponsor.rb:
--------------------------------------------------------------------------------
1 | Sponsor = Struct.new(:id, :title, :cta, :text, :click_url, :image_url, :pixel_urls) do
2 | HOST = "srv.buysellads.com"
3 | PATH = "/ads/#{ENV['BSA_IDENTIFIER']}.json"
4 |
5 | class << self
6 | def ads_for(ip)
7 | return [] unless ENV['BSA_IDENTIFIER'].present?
8 | params = { forwardedip: ip }
9 | params.merge!( testMode: true, ignore: true ) if Rails.env.development?
10 | uri = URI::HTTPS.build(host: HOST, path: PATH, query: params.to_query)
11 |
12 | error = nil
13 | results = begin
14 | start = Time.now
15 | response = Faraday.new(url: uri).get do |req|
16 | req.options.timeout = 2 # open/read timeout in seconds
17 | req.options.open_timeout = 1 # connection open timeout in seconds
18 | end
19 |
20 | JSON.parse(response.body) rescue nil
21 | rescue Faraday::TimeoutError, Net::OpenTimeout, Faraday::ConnectionFailed => e
22 | error = e
23 | nil
24 | end
25 | Rails.logger.info "sponsor=#{error ? 'fail' : 'ok'} seconds=#{"%.2f" % (Time.now - start)} error=#{error}"
26 |
27 | return [] if results.nil?
28 | results['ads'].select{|a| a['creativeid'] }.collect{ |data| build_sponsor(data) }
29 | end
30 |
31 | def build_sponsor(data)
32 | Sponsor.new(
33 | data['creativeid'],
34 | data['title'],
35 | data['callToAction'],
36 | data['description'],
37 | data['statlink'],
38 | data['image'],
39 | (data['pixel'] || '').split('||')
40 | )
41 | end
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/app/models/team.rb:
--------------------------------------------------------------------------------
1 | class Team < ApplicationRecord
2 |
3 | end
4 |
5 | # total_views ?
6 |
7 | # t.string "highlight_tags"
8 | # t.text "branding"
9 | # t.text "headline"
10 | # t.text "big_quote"
11 | # t.string "big_image"
12 | # t.string "featured_banner_image"
13 | # t.text "benefit_name_1"
14 | # t.text "benefit_description_1"
15 | # t.text "benefit_name_2"
16 | # t.text "benefit_description_2"
17 | # t.text "benefit_name_3"
18 | # t.text "benefit_description_3"
19 | # t.text "reason_name_1"
20 | # t.text "reason_description_1"
21 | # t.text "reason_name_2"
22 | # t.text "reason_description_2"
23 | # t.text "reason_name_3"
24 | # t.text "reason_description_3"
25 | # t.text "why_work_image"
26 | # t.text "organization_way"
27 | # t.text "organization_way_name"
28 | # t.text "organization_way_photo"
29 | # t.text "blog_feed"
30 | # t.text "our_challenge"
31 | # t.text "your_impact"
32 | # t.text "hiring_tagline"
33 | # t.text "link_to_careers_page"
34 | # t.text "stack_list", :default => t.string "office_photos", :default => t.text "interview_steps", :default =>
35 |
--------------------------------------------------------------------------------
/app/serializers/badge_serializer.rb:
--------------------------------------------------------------------------------
1 | class BadgeSerializer < ActiveModel::Serializer
2 | attributes :name,
3 | :description,
4 | :created_at,
5 | :badge
6 |
7 | protected
8 | def badge
9 | ActionController::Base.helpers.asset_path(object.path)
10 | end
11 |
12 | end
13 |
--------------------------------------------------------------------------------
/app/serializers/comment_serializer.rb:
--------------------------------------------------------------------------------
1 | class CommentSerializer < ActiveModel::Serializer
2 | attributes :id,
3 | :hearts,
4 | :heartableId
5 |
6 | protected
7 | def hearts
8 | object.hearts_count
9 | end
10 |
11 | def heartableId
12 | object.dom_id
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/serializers/current_user_serializer.rb:
--------------------------------------------------------------------------------
1 | class CurrentUserSerializer < UserSerializer
2 | attributes :email
3 | end
4 |
--------------------------------------------------------------------------------
/app/serializers/job_serializer.rb:
--------------------------------------------------------------------------------
1 | class JobSerializer < ActiveModel::Serializer
2 | attributes :id
3 | end
4 |
--------------------------------------------------------------------------------
/app/serializers/picture_serializer.rb:
--------------------------------------------------------------------------------
1 | class PictureSerializer < ActiveModel::Serializer
2 | attributes :name,
3 | :type,
4 | :created_at,
5 | :url
6 |
7 | protected
8 | def name
9 | object.file.file.filename
10 | end
11 |
12 | def type
13 | object.file.file.extension.downcase
14 | end
15 |
16 | def url
17 | object.file.url
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/serializers/protip_serializer.rb:
--------------------------------------------------------------------------------
1 | class ProtipSerializer < ActiveModel::Serializer
2 | include ActionView::Helpers
3 |
4 | attributes :id,
5 | :body,
6 | :created_at,
7 | :heartableId,
8 | :hearts,
9 | :html,
10 | :public_id,
11 | :subscribed,
12 | :tags,
13 | :title,
14 | :upvotes
15 |
16 | protected
17 | def title
18 | sanitize(object.title)
19 | end
20 |
21 | def body
22 | sanitize(object.body)
23 | end
24 |
25 | def html
26 | CoderwallFlavoredMarkdown.render_to_html(object.body)
27 | end
28 |
29 | def subscribed
30 | return false unless scope
31 |
32 | object.subscribers.include?(scope.id)
33 | end
34 |
35 | def heartableId
36 | object.dom_id
37 | end
38 |
39 | def hearts
40 | object.hearts_count
41 | end
42 |
43 | def upvotes
44 | object.hearts_count
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/app/serializers/user_serializer.rb:
--------------------------------------------------------------------------------
1 | class UserSerializer < ActiveModel::Serializer
2 | has_many :badges
3 | attributes :id,
4 | :username,
5 | :name,
6 | :location,
7 | :karma,
8 | :accounts,
9 | :about,
10 | :title,
11 | :company,
12 | :team,
13 | :thumbnail,
14 | :endorsements,
15 | :specialities
16 |
17 | protected
18 | def team
19 | object.team_id
20 | end
21 |
22 | #backwords compatibility
23 | def specialities
24 | []
25 | end
26 |
27 | #backwords compatibility
28 | def endorsements
29 | karma
30 | end
31 |
32 | def accounts
33 | { github: object.github, twitter: object.twitter }
34 | end
35 |
36 | def thumbnail
37 | object.avatar.url
38 | end
39 |
40 | end
41 |
--------------------------------------------------------------------------------
/app/services/notification.rb:
--------------------------------------------------------------------------------
1 | class Notification
2 | class LoggingClient
3 | def trigger(channel, event, data, options = {})
4 | Rails.logger.info "[Pusher] #{channel} #{event} #{data.inspect}"
5 | end
6 | end
7 |
8 | class << self
9 | def pusher
10 | return LoggingClient.new if Rails.env.test?
11 |
12 | Pusher
13 | end
14 |
15 | def comment_added!(article, json, socket_id = nil)
16 | trigger(article, 'new-comment', json, socket_id)
17 | end
18 |
19 | protected
20 |
21 | def trigger(model, event, payload, socket_id)
22 | channel = to_chan(model)
23 | Rails.logger.info "[Pusher] #{channel} #{event} #{payload.inspect}"
24 | pusher.trigger(channel, event, payload, socket_id: socket_id)
25 | end
26 |
27 | def to_chan(model)
28 | # Pusher don't like global ids as channel names
29 | # this will convert it to something we can use
30 | model.to_global_id.to_s.split('/')[3..-1].join(',')
31 | end
32 |
33 | def self.to_model(chan)
34 | # and then convert it back to a global id
35 | gid = "gid://#{GlobalID.app}/#{chan.split(',').join('/')}"
36 | GlobalID.find(gid)
37 | end
38 | end
39 | end
40 |
--------------------------------------------------------------------------------
/app/views/badges/_badge.html.haml:
--------------------------------------------------------------------------------
1 | .badge.clearfix.mb1
2 | .left.mr2=image_tag badge.path, width: 50, height: 50
3 | .overflow-hidden
4 | %h6.mt0="Unlocked #{badge.name}"
5 | .mt0=badge.description
6 |
--------------------------------------------------------------------------------
/app/views/clearance_mailer/change_password.html.erb:
--------------------------------------------------------------------------------
1 | <%= t(".opening") %>
2 |
3 |
4 | <%= link_to t(".link_text", default: "Change my password"),
5 | edit_user_password_url(@user, token: @user.confirmation_token.html_safe) %>
6 |
7 |
8 | <%= raw t(".closing") %>
9 |
--------------------------------------------------------------------------------
/app/views/clearance_mailer/change_password.text.erb:
--------------------------------------------------------------------------------
1 | <%= t(".opening") %>
2 |
3 | <%= edit_user_password_url(@user, token: @user.confirmation_token.html_safe) %>
4 |
5 | <%= raw t(".closing") %>
6 |
--------------------------------------------------------------------------------
/app/views/comment_mailer/new_comment.html.erb:
--------------------------------------------------------------------------------
1 |
10 |
11 | <%= sanitize(CoderwallFlavoredMarkdown.render_to_html(@comment.body)) %>
12 |
13 |
14 | —
15 | You are receiving this because you either wrote or commented on this protip.
16 |
17 | View on Coderwall ,
18 | or
19 | mute this thread.
20 |
21 |
22 |
34 |
--------------------------------------------------------------------------------
/app/views/comments/_comment.html.haml:
--------------------------------------------------------------------------------
1 | - cache ['v3', comment, current_user_can_edit?(comment)] do
2 | - style ||= :large
3 | .inline-block.py1.mb1[comment]{class: ('border-top' if style != :small), style: 'width: 100%'}
4 |
5 | .left.mt1.mr2.avatar.small{style:"background-color: #{comment.user.color};"}
6 | =avatar_url_tag(comment.user)
7 | .overflow-hidden.py0.mt0
8 | .clearfix
9 | .author[:author]
10 | %a.bold.black.no-hover[:alternateName]{href: profile_path(username: comment.user.username)}
11 | =comment.user.username
12 | .content.small[:text]= preserve(sanitize(CoderwallFlavoredMarkdown.render_to_html(comment.body)))
13 | - if style != :small
14 | .diminish.mt1
15 | ==#{time_ago_in_words_with_ceiling(comment.created_at)} ago
16 | -if current_user_can_edit?(comment)
17 | ·
18 | = button_to comment_path(comment), method: :delete, data: { confirm: "Are you sure you want to delete your comment?" }, form_class: "inline plain" do
19 | = icon('trash')
20 |
--------------------------------------------------------------------------------
/app/views/comments/_comment.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.extract! comment, :id
2 | json.created_at comment.video_timestamp
3 | json.authorUrl user_path(comment.user)
4 | json.authorUsername comment.user.username
5 | json.markup sanitize(CoderwallFlavoredMarkdown.render_to_html(comment.body))
6 |
--------------------------------------------------------------------------------
/app/views/comments/index.html.haml:
--------------------------------------------------------------------------------
1 | .container
2 | .clearfix
3 | .md-col.md-show.md-col-4
4 | .sm-col.sm-col.sm-col-12.md-col-8
5 | -@comments.each do |comment|
6 | %h6.mt1
7 | =link_to comment.article.title, seo_protip_path(comment.article)
8 | =render comment
9 | .bold.mb4
10 | =link_to("Delete #{comment.user.username} and their #{comment.user.comments.size} comments", user_path(comment.user), method: :delete, class: 'diminish mr1')
11 |
12 | .clearfix
13 | .btn.left= link_to_previous_page @comments, 'Previous'
14 | .btn.right= link_to_next_page @comments, 'Next'
15 | .md-col.md-show.md-col-4
16 |
--------------------------------------------------------------------------------
/app/views/comments/index.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.comments @comments, partial: 'comments/comment', as: :comment
2 |
--------------------------------------------------------------------------------
/app/views/comments/show.js.erb:
--------------------------------------------------------------------------------
1 | document.getElementById('comments').
2 | appendChild("<%= escape_javascript(render partial: 'comment', locals: { comment: @comment, style: :small } ) %>");
3 | var chat = document.getElementById('chat');
4 | chat.scrollTop = chat.scrollHeight;
5 |
--------------------------------------------------------------------------------
/app/views/jobs/_job.html.haml:
--------------------------------------------------------------------------------
1 | -cache ['v1', job, feature] do
2 | .job.card.clearfix.mb2[job]{ class: ('border' if feature) }
3 | .clearfix.p2
4 | .col.col-1.md-show
5 | - if job.company_logo.present?
6 | =image_tag(job.company_logo, width: 50)
7 | - else
8 |
9 |
10 | .col.col-8
11 | .ml1.mr1
12 | %h3.mt0
13 | %a.diminish-viewed[:title]{:href => job.source, rel: 'nofollow', target: '_blank', 'ga-event-category' => 'Jobs', 'ga-event-action' => 'Job Board - Job', 'ga-event-label' => "#{job.company} - #{job.id}"}=job.title
14 | .font-sm
15 | .bold.inline=link_to(truncate(job.company, length:20), job.company_url, rel: 'nofollow', target: '_blank', 'ga-event-category' => 'Jobs', 'ga-event-action' => 'Job Board - Job', 'ga-event-label' => "#{job.company} - #{job.id}")
16 | ·
17 | .diminish.inline=job.role_type
18 | ·
19 | .diminish.inline==posted #{time_ago_in_words(job.created_at)} ago
20 |
21 | .col.col-3
22 | .mt1.right-align.diminish
23 | =job.location
24 |
--------------------------------------------------------------------------------
/app/views/jobs/_mini.html.haml:
--------------------------------------------------------------------------------
1 | .clearfix.py1
2 | %a.link.no-hover.mt2{:href => job.source, rel: 'nofollow', target: '_blank', 'ga-event-category' => 'Jobs', 'ga-event-action' => "#{location} - Featured Job", 'ga-event-label' => "#{job.company} - #{job.id}"}
3 | .col.col-3.md-col-2{class: (job.company_logo.present? ? '' : 'hide')}
4 | =image_tag(job.company_logo, class: '') if job.company_logo.present?
5 | .overflow-hidden.pl2
6 | .blue.bold
7 | =job.title
8 | .font-sm.black
9 | .inline=link_to(truncate(job.company, length:18), job.company_url, rel: 'nofollow', target: '_blank', 'ga-event-category' => 'Jobs', 'ga-event-action' => "#{location} - Featured Job", 'ga-event-label' => "#{job.company} - #{job.id}")
10 | ·
11 | .inline=job.location
12 | ·
13 | .inline=job.role_type
14 |
--------------------------------------------------------------------------------
/app/views/jobs/new.html.haml:
--------------------------------------------------------------------------------
1 | -title 'Post a Job, find & hire great programmers'
2 | -description 'Need programming help to build something challenging? Post a job for 30 days for only $299.'
3 |
4 | %script(src="https://checkout.stripe.com/checkout.js")
5 |
6 | .container
7 | %h1 Find and hire great programmers
8 | .clearfix
9 | .sm-col.sm-col.sm-col-12.md-col-8
10 | .mb2.purple{style: "border-bottom:solid 5px;"}
11 | .card.p3
12 | %p
13 | Fill in your details about your job and we'll feature it to the entire Coderwall community for
14 | %strong 30 days for only $299.
15 |
16 | -@job.errors.full_messages.each do |error|
17 | %p.red.bold=error
18 |
19 | .mt2.diminish
20 | Coderwall securely accepts all major credit cards.
21 |
22 | .clearfix.mt2
23 | = link_to "Cancel", jobs_path
24 |
25 | .md-col.md-col-4.md-show
26 | .ml3
27 | .clearfix
28 | .bg-white.rounded.p2
29 | %p Need programming help to build something challenging? Post a job and we'll feature it to the best developers using Coderwall each month.
30 |
31 | %hr.mt1
32 |
33 | %h5.mt3.mb2
34 | =icon('smile-o', class: 'mr1')
35 | Guaranteed Happiness
36 |
37 | %p If you're not fully satisfied we'll give you a free listing or a full refund - your choice. Just let us know within 30 days after your listing expires.
38 |
39 | .clearfix.mt1
40 | %p.bold.p2
41 | Have questions?
42 | %a{href:'mailto:support@coderwall.com'} Contact us
43 |
--------------------------------------------------------------------------------
/app/views/layouts/minimal.html.haml:
--------------------------------------------------------------------------------
1 | !!!
2 | %html
3 | %head
4 | %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}
5 | %meta{property: 'current_user:id', content: current_user.try(:id)}
6 | = display_meta_tags(default_meta_tags)
7 | = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => 'reload'
8 | = stylesheet_link_tag 'minimal', media: 'all', 'data-turbolinks-track' => 'reload'
9 | = javascript_include_tag 'application', 'data-turbolinks-track' => 'reload'
10 | = javascript_include_tag 'https://content.jwplatform.com/libraries/pEaCoeG7.js'
11 | = csrf_meta_tags
12 | = render 'shared/analytics'
13 | = yield :head
14 | %body
15 | =yield
16 |
17 | -# gdpr disabled render 'shared/tracking'
18 |
--------------------------------------------------------------------------------
/app/views/pages/faq.html.haml:
--------------------------------------------------------------------------------
1 | - title "FAQ"
2 |
3 | .container.clearfix
4 | %h1 FAQ
5 | %h3.mt3= link_to 'How do I delete my account?', '#', 'name' => 'deleteaccount'
6 | %p
7 | You must be logged in to delete your account.
8 | Once you are logged in visit
9 | %a{href: 'https://coderwall.com/delete_account', rel: 'nofollow'} https://coderwall.com/delete_account
10 | and locate the trash icon next to the edit button. Please note this action is irreversible.
11 |
12 | %h3.mt3= link_to 'What happened to the badges?!', '#', 'name' => 'profileupdates'
13 | %p We miss them too! We're still hoping we'll get them back into the site one day.
14 |
15 | %h3.mt3= link_to 'Can I help Coderwall?', '#', 'name' => 'source'
16 | %p You sure can! You can find the [source on GitHub.](https://github.com/coderwall/coderwall-next]
17 |
18 |
19 | :javascript
20 | var h = window.screen.height;
21 | var w = window.screen.width;
22 | var r = w / h;
23 |
24 | var base = document.getElementById('base-resolution');
25 | var calc = document.getElementById('calc-resolution');
26 |
27 | base.textContent = w + 'x' + h;
28 | calc.textContent = r == 1.6 ? ('1280x800') : base.textContent;
29 |
--------------------------------------------------------------------------------
/app/views/pages/not_found.html.haml:
--------------------------------------------------------------------------------
1 | - title '404 : Unable to handle that url'
2 |
3 | .clearfix
4 | .col.col-12{style:'margin-top: 10%;'}
5 | .center
6 | %h1 404! Our feels when that url is used
7 | =icon('thumbs-o-down', style:'font-size:80px')
8 |
--------------------------------------------------------------------------------
/app/views/pages/server_error.html.haml:
--------------------------------------------------------------------------------
1 | - title "500 : Coderwall is having some server issues"
2 |
3 | .clearfix
4 | .col.col-12{style:'margin-top: 10%;'}
5 | .center
6 | %h1 I don't feel so good
7 | %p.mb2.font-lg Coderwall is having some temporary server issues, sorry.
8 | =icon('frown-o', style:'font-size:80px')
9 |
--------------------------------------------------------------------------------
/app/views/pages/styleguide.html.erb:
--------------------------------------------------------------------------------
1 | Protips
2 |
3 |
20 |
--------------------------------------------------------------------------------
/app/views/passwords/create.html.haml:
--------------------------------------------------------------------------------
1 | - title 'A page to tell you about your incoming email'
2 |
3 | %h2 Wow, such a fancy address
4 | %p
5 | %strong If we have email address for your account
6 | then within the next few minutes you will receive an email. It contains a link to change your password.
7 | .clearfix.mt2
8 | %a.btn.mt1.rounded.bg-green.white{href: sign_in_path} Sign In
9 |
--------------------------------------------------------------------------------
/app/views/passwords/edit.html.haml:
--------------------------------------------------------------------------------
1 | - title 'Change your password'
2 |
3 | %h2 Change your password
4 | %p Your password has been reset. Choose a new password below.
5 | = form_for :password_reset, |
6 | url: user_password_path(@user, token: @user.confirmation_token), |
7 | class: 'sm-col-6', |
8 | html: { method: :put } do |form|
9 | = form.label :password
10 | = form.password_field :password, class: 'block mb1 col-6 field'
11 | %button.btn.mt1.rounded.bg-green.white{type: 'submit'} Save password
12 |
--------------------------------------------------------------------------------
/app/views/passwords/new.html.haml:
--------------------------------------------------------------------------------
1 | - title "Reset your password"
2 |
3 | .container
4 | %h2 Reset your password
5 | .sm-col-6
6 | %p Enter the email address for your Coderwall account to be emailed a link so you can reset your password.
7 | = form_for :password, url: passwords_path do |form|
8 | = form.label :email
9 | = form.text_field :email, type: 'email', class: 'field block col-10 mb1'
10 | %button.btn.mt1.rounded.bg-green.white{type: 'submit'} Reset password
11 | .clearfix.mt2
12 | You just remembered it? Nice!
13 | = link_to "Sign In", sign_in_path
14 |
--------------------------------------------------------------------------------
/app/views/protips/_protip.html.haml:
--------------------------------------------------------------------------------
1 | -cache ['v4', protip, hide_on_profile] do
2 | .protip.card.clearfix.py2.likeable[protip]{id: dom_id(protip)}
3 | .col.col-1{class: hide_on_profile}
4 | .col.col-11.overflow-hidden
5 | %h3.mt0.mb0
6 | %a.diminish-viewed[:headline]{:href => seo_protip_path(protip)}=protip.title
7 | .font-sm
8 | %span{class: hide_on_profile}
9 | =link_to protip.user.try(:username), profile_path(username: protip.user.username)
10 | .diminish.inline ·
11 |
12 | .diminish.inline
13 | %a[:url]{href: slug_protips_url(id: protip.public_id, slug: protip.slug)}
14 | =pluralize(protip.comments.size, 'response')
15 | ·
16 | =protip.display_tags
17 |
--------------------------------------------------------------------------------
/app/views/sessions/new.html.haml:
--------------------------------------------------------------------------------
1 | - title "Sign in"
2 |
3 | .container
4 | %h2
5 | Sign In or
6 | = link_to "Join Coderwall", sign_up_path
7 | .sm-col-6
8 | =form_for :session, url: session_path do |form|
9 | .mb2.font-sm.diminish
10 | NOTE: If you previously signed in using your Twitter or GitHub account, you'll now need to
11 | = link_to "reset your password", new_password_path
12 | to get a new first time password to further access your account.
13 | = form.label :email, "Email or Username"
14 | = form.text_field :email, type: 'text', class: 'field block col-10 mb1'
15 | = form.label :password
16 | = form.password_field :password, class: 'field block col-10 mb1'
17 | %button.btn.mt1.rounded.bg-green.white{type: 'submit'} Sign In
18 | .clearfix.mt3
19 | = link_to "Reset a forgotten password", new_password_path
20 | .clearfix.mt2
21 | Don't have an account?
22 | .inline.bold= link_to "Sign Up", sign_up_path
23 |
--------------------------------------------------------------------------------
/app/views/shared/_analytics.html.erb:
--------------------------------------------------------------------------------
1 | <% if ENV['GOOGLE_ANALYTICS_UA'].present? %>
2 |
11 | <% else #LOG EVENTS DIRECTLY TO WEB CONSOLE %>
12 |
19 | <% end %>
20 |
--------------------------------------------------------------------------------
/app/views/shared/_logo.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/views/shared/_tracking.html.erb:
--------------------------------------------------------------------------------
1 | <% if Rails.env.production? %>
2 |
14 | <% end %>
15 |
--------------------------------------------------------------------------------
/app/views/teams/show.html.haml:
--------------------------------------------------------------------------------
1 | - title @team.name
2 |
3 | .container[@team]
4 | .clearfix
5 | .md-col.md-col-2.md-show
6 | .sm-col.sm-col.sm-col-12.md-col-8
7 | .card.p3{style: "border-top:solid 5px #{@team.color}"}
8 | %h1=@team.name
9 | .clearfix
10 | -if @team.twitter.present?
11 | =link_to icon("twitter", class: "fa-1x", style: "color: #{@team.color}"), "https://twitter.com/#{@team.twitter}"
12 | .hide_last_child.inline ·
13 | -if @team.github.present?
14 | =link_to icon("github", class: "fa-1x", style: "color: #{@team.color}"), "http://github.com/#{@team.github}"
15 | .hide_last_child.inline ·
16 | .content.mt1[:description]
17 | = sanitize CoderwallFlavoredMarkdown.render_to_html(@team.about)
18 | .md-col.md-col-2.md-show
19 |
--------------------------------------------------------------------------------
/app/views/user_mailer/destroy_email.text.erb:
--------------------------------------------------------------------------------
1 | Created: <%= time_ago_in_words(@user.created_at) %> ago.
2 | Protips: <%= @user.protips.count %>
3 | Comments: <%= @user.comments.count %>
4 | Email: <%= @user.email %>
5 | GitHub: <%= @user.github %>
6 |
--------------------------------------------------------------------------------
/app/views/user_mailer/partnership_expired.text.erb:
--------------------------------------------------------------------------------
1 | Hi <%= @user.username %>
2 |
3 | You are receiving this email because you were a member of Assembly and a Contributing Partner on Coderwall. Unfortunately your <%= @user.ownership %>% of Coderwall App Coins expired on <%= (@user.partner_last_contribution_at + 1.year).strftime("%m/%d/%Y") %>. This was because more then 1 year had passed since your made your last eligible contribution on <%= @user.partner_last_contribution_at.strftime("%m/%d/%Y")%>. Without Coderwall App Coins you are no longer eligible to receive proceeds from Coderwall's future earnings but you will still receive distributions of earnings for the months where you had valid Coderwall App Coins.
4 |
5 | If you would like to stay active and participate in Coderwall we'd like to extend to you a 90 day grace period from today. Any eligible contributions you make to Coderwall before <%= 90.days.from_now.strftime("%m/%d/%Y") %> will earn you back all of your expired Coderwall App Coins. Check out the latest guidelines[1] in the Coderwall Github Repository[2] to start contributing again.
6 |
7 | If you believe there was an error, have any questions, or would like to explore other ways to contribute you can contact us anytime at support@coderwall.com or by joining the Coderwall Partners' Slack Channel [3].
8 |
9 | Regards
10 | Matt, Dave, and the Coderwall Team
11 |
12 | [1] https://github.com/coderwall/coderwall-next/blob/master/CONTRIBUTING.md
13 | [2] https://github.com/coderwall/coderwall-next
14 | [3] http://slack.coderwall.com
15 |
--------------------------------------------------------------------------------
/app/views/users/new.html.haml:
--------------------------------------------------------------------------------
1 | - title 'Join today!'
2 |
3 | .container
4 | %h2 Join Coderwall Today
5 | .sm-col-6
6 | %p Become a better programmer. Discover helpful protips, unlock achievements, and connect with other developers
7 | -@user.errors.full_messages.each do |error|
8 | %p.red.bold=error
9 | = form_for @user do |form|
10 | = form.label :username
11 | = form.text_field :username, type: 'text', class: 'field block col-10 mb1'
12 | = form.label :email
13 | = form.text_field :email, type: 'email', class: 'field block col-10 mb1'
14 | = form.label :password
15 | = form.password_field :password, class: 'field block col-10 mb1'
16 | .g-recaptcha{"data-sitekey" => ENV['CAPTCHA_SITE_KEY']}
17 | %button.btn.mt1.rounded.bg-green.white{type: 'submit'} Sign Up
18 | .mt1.font-sm.diminish
19 | Creating an account means you’re okay with Coderwall's
20 | =link_to 'Terms of Service', tos_path
21 | .clearfix.mt3
22 | Already have an account?
23 | .inline.bold= link_to "Sign In", sign_in_path
24 |
25 |
26 |
--------------------------------------------------------------------------------
/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_PATH = File.expand_path('../config/application', __dir__)
3 | require_relative '../config/boot'
4 | require 'rails/commands'
5 |
--------------------------------------------------------------------------------
/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative '../config/boot'
3 | require 'rake'
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'pathname'
3 | require 'fileutils'
4 | include FileUtils
5 |
6 | # path to your application root.
7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
8 |
9 | def system!(*args)
10 | system(*args) || abort("\n== Command #{args} failed ==")
11 | end
12 |
13 | chdir APP_ROOT do
14 | # This script is a starting point to setup your application.
15 | # Add necessary setup steps to this file.
16 |
17 | puts '== Installing dependencies =='
18 | system! 'gem install bundler --conservative'
19 | system('bundle check') || system!('bundle install')
20 |
21 | # puts "\n== Copying sample files =="
22 | # unless File.exist?('config/database.yml')
23 | # cp 'config/database.yml.sample', 'config/database.yml'
24 | # end
25 |
26 | puts "\n== Preparing database =="
27 | system! 'bin/rails db:setup'
28 |
29 | puts "\n== Removing old logs and tempfiles =="
30 | system! 'bin/rails log:clear tmp:clear'
31 |
32 | puts "\n== Restarting application server =="
33 | system! 'bin/rails restart'
34 | end
35 |
--------------------------------------------------------------------------------
/bin/spring:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # This file loads spring without using Bundler, in order to be fast.
4 | # It gets overwritten when you run the `spring binstub` command.
5 |
6 | unless defined?(Spring)
7 | require 'rubygems'
8 | require 'bundler'
9 |
10 | if (match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m))
11 | Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq }
12 | gem 'spring', match[1]
13 | require 'spring/binstub'
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/bin/update:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'pathname'
3 | require 'fileutils'
4 | include FileUtils
5 |
6 | # path to your application root.
7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
8 |
9 | def system!(*args)
10 | system(*args) || abort("\n== Command #{args} failed ==")
11 | end
12 |
13 | chdir APP_ROOT do
14 | # This script is a way to update your development environment automatically.
15 | # Add necessary update steps to this file.
16 |
17 | puts '== Installing dependencies =='
18 | system! 'gem install bundler --conservative'
19 | system('bundle check') || system!('bundle install')
20 |
21 | puts "\n== Updating database =="
22 | system! 'bin/rails db:migrate'
23 |
24 | puts "\n== Removing old logs and tempfiles =="
25 | system! 'bin/rails log:clear tmp:clear'
26 |
27 | puts "\n== Restarting application server =="
28 | system! 'bin/rails restart'
29 | end
30 |
--------------------------------------------------------------------------------
/client/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "stage-0", "react"]
3 | }
4 |
--------------------------------------------------------------------------------
/client/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb",
3 | "parser": "babel-eslint",
4 |
5 | "rules": {
6 | "dot-location": ["error", "object"],
7 | "import/no-extraneous-dependencies": ["error", {"devDependencies": ["**/__tests__/**"]}],
8 | "import/no-named-as-default": 0,
9 | "no-global-assign": ["error", { exceptions: [] }],
10 | "quotes": 0,
11 | "react/jsx-closing-bracket-location": 0,
12 | "react/no-multi-comp": 0,
13 | "react/sort-comp": [2, {
14 | order: [
15 | 'static-methods',
16 | 'constructor',
17 | 'render',
18 | 'lifecycle',
19 | 'everything-else'
20 | ]
21 | }],
22 | "semi": [2, "never"]
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/client/actions/heartActions.js:
--------------------------------------------------------------------------------
1 | import { CALL_API } from 'redux-api-middleware'
2 |
3 | export const HEART_REQUEST = 'HEART_REQUEST'
4 | export const HEART_SUCCESS = 'HEART_SUCCESS'
5 | export const HEART_FAILURE = 'HEART_FAILURE'
6 |
7 | export function heart(endpoint, heartableId, userId) {
8 | return {
9 | [CALL_API]: {
10 | endpoint,
11 | method: 'POST',
12 | types: [
13 | { type: HEART_REQUEST, payload: { heartableId, userId } },
14 | HEART_SUCCESS,
15 | HEART_FAILURE,
16 | ],
17 | },
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/client/actions/jobActions.js:
--------------------------------------------------------------------------------
1 | /* global window */
2 | import { CALL_API } from 'redux-api-middleware'
3 |
4 | export const JOB_POST_REQUEST = 'JOB_POST_REQUEST'
5 | export const JOB_POST_SUCCESS = 'JOB_POST_SUCCESS'
6 | export const JOB_POST_FAILURE = 'JOB_POST_FAILURE'
7 |
8 | export function createPost(stripeToken, job) {
9 | return {
10 | [CALL_API]: {
11 | endpoint: `/jobs`,
12 | method: 'POST',
13 | body: JSON.stringify({ stripeToken, job }),
14 | types: [
15 | JOB_POST_REQUEST,
16 | JOB_POST_SUCCESS,
17 | JOB_POST_FAILURE,
18 | ],
19 | },
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/client/actions/protipActions.js:
--------------------------------------------------------------------------------
1 | import { CALL_API } from 'redux-api-middleware'
2 |
3 | export const PROTIP_MUTE_REQUEST = 'PROTIP_MUTE_REQUEST'
4 | export const PROTIP_MUTE_SUCCESS = 'PROTIP_MUTE_SUCCESS'
5 | export const PROTIP_MUTE_FAILURE = 'PROTIP_MUTE_FAILURE'
6 | export const PROTIP_SUBSCRIBE_REQUEST = 'PROTIP_SUBSCRIBE_REQUEST'
7 | export const PROTIP_SUBSCRIBE_SUCCESS = 'PROTIP_SUBSCRIBE_SUCCESS'
8 | export const PROTIP_SUBSCRIBE_FAILURE = 'PROTIP_SUBSCRIBE_FAILURE'
9 |
10 | export function mute(protipId, userId) {
11 | return {
12 | [CALL_API]: {
13 | endpoint: `/p/${protipId}/subscribers`,
14 | method: 'DELETE',
15 | types: [
16 | { type: PROTIP_MUTE_REQUEST, payload: { protipId, userId } },
17 | PROTIP_MUTE_SUCCESS,
18 | PROTIP_MUTE_FAILURE,
19 | ],
20 | },
21 | }
22 | }
23 |
24 | export function subscribe(protipId, userId) {
25 | return {
26 | [CALL_API]: {
27 | endpoint: `/p/${protipId}/subscribers`,
28 | method: 'POST',
29 | types: [
30 | { type: PROTIP_SUBSCRIBE_REQUEST, payload: { protipId, userId } },
31 | PROTIP_SUBSCRIBE_SUCCESS,
32 | PROTIP_SUBSCRIBE_FAILURE,
33 | ],
34 | },
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/client/checkLinks.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | /* eslint-disable */
3 | const roboto = require('roboto')
4 |
5 | const crawler = new roboto.Crawler({
6 | startUrls: [
7 | 'http://coderwall.dev:5000/',
8 | ],
9 | // We don't want it crawling outside links.
10 | constrainToRootDomains: true,
11 | })
12 |
13 | const deadLinks = []
14 | crawler.on('httpError', (statusCode, href, referer) => {
15 | if (statusCode === 404) {
16 | console.log('Dead link: %s found on page: %s', href, referer)
17 | deadLinks.push({
18 | href,
19 | referer,
20 | })
21 | }
22 | })
23 |
24 | crawler.on('finish', () => {
25 | for (let i = 0; i < deadLinks.length; i++) {
26 | const deadLink = deadLinks[i]
27 | console.log('Dead link: %s found on page: %s', deadLink.href, deadLink.referer)
28 | }
29 | })
30 |
31 | crawler.crawl()
32 |
--------------------------------------------------------------------------------
/client/components/Heart.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes as T } from 'react'
2 | import Icon from './Icon'
3 |
4 | const numberToHuman = (number) => {
5 | if (number > 0) {
6 | const s = ['', 'K', 'M']
7 | const e = Math.floor(Math.log(number) / Math.log(1000))
8 | return (number / Math.pow(1000, e)).toFixed(0) + s[e]
9 | }
10 |
11 | return 0
12 | }
13 |
14 | const renderCount = (cnt) => (
15 |
16 | {numberToHuman(cnt)}
17 |
18 | )
19 |
20 | const renderLabels = (hearted, [off, on]) => (
21 |
22 | {hearted ? on : off}
23 |
24 | )
25 |
26 | const Heart = ({ hearted, labels, count, onClick }) => {
27 | const icon = hearted ? 'heart' : 'heart-o'
28 | return (
29 |
30 |
31 |
32 |
33 |
34 |
35 | {labels ? renderLabels(hearted, labels) : renderCount(count)}
36 |
37 | )
38 | }
39 |
40 | Heart.propTypes = {
41 | count: T.number,
42 | hearted: T.bool,
43 | onClick: T.func,
44 | labels: T.arrayOf(T.string),
45 | }
46 |
47 | export default Heart
48 |
--------------------------------------------------------------------------------
/client/components/HeartButton.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes as T } from 'react'
2 | import { connect } from 'react-redux'
3 | import Heart from './Heart'
4 | import { heart } from '../actions/heartActions'
5 |
6 | class HeartButton extends React.Component {
7 | static propTypes = {
8 | count: T.number,
9 | currentUser: T.object,
10 | dispatch: T.func.isRequired,
11 | heartableId: T.string,
12 | hearted: T.bool,
13 | href: T.string.isRequired,
14 | labels: T.arrayOf(T.string),
15 | }
16 |
17 | render() {
18 | return (
19 | this.handleClick()}
23 | count={this.props.count} />
24 | )
25 | }
26 |
27 | handleClick() {
28 | if (this.props.hearted) { return }
29 |
30 | this.props.dispatch(
31 | heart(
32 | this.props.href,
33 | this.props.heartableId,
34 | this.props.currentUser && this.props.currentUser.id
35 | )
36 | )
37 | }
38 | }
39 |
40 | function mapStateToProps(state, ownProps) {
41 | const heartables = [
42 | ...(state.protips.items || []),
43 | ...(state.comments.items || []),
44 | state.currentProtip.item,
45 | ].filter(h => h)
46 |
47 | const heartable = heartables.find(p => p.heartableId === ownProps.heartableId)
48 |
49 | if (!heartable) { return {} }
50 |
51 | const hearts = state.hearts.items || []
52 | const hearted = hearts.indexOf(ownProps.heartableId) > -1
53 |
54 | return {
55 | hearted,
56 | count: heartable.hearts,
57 | }
58 | }
59 |
60 | export default connect(mapStateToProps)(HeartButton)
61 |
--------------------------------------------------------------------------------
/client/components/Icon.jsx:
--------------------------------------------------------------------------------
1 | // http://fontawesome.io/icons/
2 | import React, { PropTypes as T } from 'react'
3 | import classNames from 'classnames'
4 |
5 | const Icon = ({ icon, extraClasses }) => (
6 |
7 | )
8 |
9 | Icon.propTypes = {
10 | icon: T.string.isRequired,
11 | extraClasses: T.string,
12 | }
13 |
14 | export default Icon
15 |
--------------------------------------------------------------------------------
/client/components/NewJob.jsx:
--------------------------------------------------------------------------------
1 | /* global alert, window, document, Image, StripeCheckout */
2 | import React, { Component, PropTypes as T } from 'react'
3 | import { connect } from 'react-redux'
4 |
5 | import JobForm from './JobForm'
6 |
7 | import { createPost } from '../actions/jobActions'
8 |
9 | class NewJob extends Component {
10 | static propTypes = {
11 | cost: T.number.isRequired,
12 | dispatch: T.func.isRequired,
13 | error: T.string,
14 | job: T.object,
15 | stripePublishable: T.string.isRequired,
16 | }
17 |
18 | render() {
19 | return
20 | }
21 |
22 | componentDidUpdate(prevProps) {
23 | if (!prevProps.job && this.props.job && this.props.job.id) {
24 | window.location = `/jobs?posted=${this.props.job.id}`
25 | }
26 |
27 | if (!prevProps.error && this.props.error) {
28 | alert("Unable to charge this card. Please try again") // eslint-disable-line no-alert
29 | }
30 | }
31 |
32 | handleSubmit = (values) => new Promise((resolve) => {
33 | const onStripeTokenSet = token => this.props.dispatch(createPost(token.id, values))
34 |
35 | this.checkout = this.checkout || StripeCheckout.configure({
36 | key: this.props.stripePublishable,
37 | image: 'https://s3.amazonaws.com/stripe-uploads/A6CJ1PO8BNz85yiZbRZwpGOSsJc5yDvKmerchant-icon-356788-cwlogo.png',
38 | locale: 'auto',
39 | token: onStripeTokenSet,
40 | closed: resolve,
41 | })
42 |
43 | this.checkout.open({
44 | name: "Jobs @ coderwall.com",
45 | description: "30 day listing",
46 | amount: this.props.cost,
47 | })
48 | })
49 | }
50 |
51 | const mapStateToProps = (state) => ({
52 | job: state.job.item,
53 | error: state.job.error,
54 | })
55 |
56 | export default connect(mapStateToProps)(NewJob)
57 |
--------------------------------------------------------------------------------
/client/components/ProtipSubscribeButton.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes as T } from 'react'
2 | import { connect } from 'react-redux'
3 |
4 | import ToggleWithLabel from './ToggleWithLabel'
5 | import { subscribe, mute } from '../actions/protipActions'
6 |
7 | class ProtipSubscribeButton extends Component {
8 | static propTypes = {
9 | currentUser: T.object,
10 | dispatch: T.func.isRequired,
11 | protipId: T.number,
12 | subscribed: T.bool,
13 | }
14 |
15 | render() {
16 | return (
17 |
24 | )
25 | }
26 |
27 | handleClick = () => {
28 | const action = this.props.subscribed ? mute : subscribe
29 | this.props.dispatch(
30 | action(this.props.protipId, this.props.currentUser && this.props.currentUser.id)
31 | )
32 | }
33 | }
34 |
35 | function mapStateToProps(state) {
36 | const protip = state.currentProtip.item
37 | const subscribed = protip.subscribed
38 |
39 | return {
40 | protipId: protip.id,
41 | subscribed,
42 | }
43 | }
44 |
45 | export default connect(mapStateToProps)(ProtipSubscribeButton)
46 |
--------------------------------------------------------------------------------
/client/components/ToggleWithLabel.jsx:
--------------------------------------------------------------------------------
1 | import React, { PropTypes as T } from 'react'
2 | import Icon from './Icon'
3 |
4 | const ToggleWithLabel = (props) => {
5 | const { icon, label } = props.on ? {
6 | icon: props.iconOn,
7 | label: props.labelOn,
8 | } : {
9 | icon: props.iconOff,
10 | label: props.labelOff,
11 | }
12 | return (
13 |
14 |
15 | {label}
16 |
17 | )
18 | }
19 |
20 | ToggleWithLabel.propTypes = {
21 | iconOff: T.string.isRequired,
22 | iconOn: T.string.isRequired,
23 | labelOff: T.string.isRequired,
24 | labelOn: T.string.isRequired,
25 | on: T.bool.isRequired,
26 | onClick: T.func.isRequired,
27 | }
28 |
29 | export default ToggleWithLabel
30 |
--------------------------------------------------------------------------------
/client/components/TrackClick.jsx:
--------------------------------------------------------------------------------
1 | /* global ga */
2 | import React, { Component, PropTypes as T } from 'react'
3 |
4 | export default class TrackClick extends Component {
5 | static propTypes = {
6 | action: T.string.isRequired,
7 | category: T.string.isRequired,
8 | children: T.node.isRequired,
9 | label: T.string.isRequired,
10 | }
11 |
12 | render() {
13 | return {this.props.children}
14 | }
15 |
16 | handleClick = () => {
17 | ga('send', 'event', {
18 | eventCategory: this.props.category,
19 | eventAction: this.props.action,
20 | eventLabel: this.props.label,
21 | transport: 'beacon',
22 | })
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/client/components/__tests__/JobForm-test.jsx:
--------------------------------------------------------------------------------
1 | /* global test, expect */
2 | import React from 'react'
3 | import renderer from 'react-test-renderer'
4 | import { combineReducers, createStore } from 'redux'
5 | import { Provider } from 'react-redux'
6 | import { reduxForm, reducer as form } from 'redux-form'
7 |
8 | import { JobForm } from '../JobForm'
9 |
10 | test('renders correctly', () => {
11 | const store = createStore(
12 | combineReducers({ form }),
13 | { form: {} }
14 | )
15 |
16 | const Decorated = reduxForm({ form: 'testForm' })(JobForm)
17 | const tree = renderer.create(
18 |
19 |
20 |
21 | )
22 | expect(tree).toMatchSnapshot()
23 | })
24 |
--------------------------------------------------------------------------------
/client/lib/apiAuthInjector.js:
--------------------------------------------------------------------------------
1 | /* global document */
2 | import { CALL_API } from 'redux-api-middleware'
3 |
4 | export default () => next => action => {
5 | const callApi = action[CALL_API]
6 |
7 | // Check if this action is a redux-api-middleware action.
8 | if (callApi) {
9 | // Inject the CSRF token
10 | callApi.headers = {
11 | 'X-CSRF-Token': document.getElementsByName('csrf-token')[0].content,
12 | Accept: 'application/json',
13 | 'Content-Type': 'application/json',
14 | ...callApi.headers,
15 | }
16 | callApi.credentials = callApi.credentials || 'same-origin'
17 | }
18 |
19 | // Pass the FSA to the next action.
20 | return next(action)
21 | }
22 |
--------------------------------------------------------------------------------
/client/lib/createReducer.js:
--------------------------------------------------------------------------------
1 | export default function createReducer(initialState, handlers) {
2 | if (Object.keys(handlers).filter(k => !k || k.length === 0 || k === 'undefined').length > 0) {
3 | throw new Error("Tried to create reducer with empty keys")
4 | }
5 |
6 | return (state, action) => {
7 | const handler = handlers[action.type]
8 | if (handler) {
9 | return {
10 | ...state,
11 | ...handler(action, state, initialState),
12 | }
13 | }
14 |
15 | return state || initialState
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/client/lib/loadImage.js:
--------------------------------------------------------------------------------
1 | /* global Image, Promise */
2 | export default function loadImage(url, timeout = 5000) {
3 | return new Promise((resolve, reject) => {
4 | let timedOut = false
5 | let timer
6 | const img = new Image()
7 | img.onerror = img.onabort = () => {
8 | if (!timedOut) {
9 | clearTimeout(timer)
10 | reject()
11 | }
12 | }
13 | img.onload = () => {
14 | if (!timedOut) {
15 | clearTimeout(timer)
16 | resolve()
17 | }
18 | }
19 | img.src = url
20 | timer = setTimeout(() => {
21 | timedOut = true
22 | reject()
23 | }, timeout)
24 | })
25 | }
26 |
--------------------------------------------------------------------------------
/client/lib/unauthorizedHandler.js:
--------------------------------------------------------------------------------
1 | /* global window */
2 | export default () => next => action => {
3 | if (action.payload && action.payload.status === 401) {
4 | window.location = '/signin'
5 | return
6 | }
7 |
8 | next(action)
9 | }
10 |
--------------------------------------------------------------------------------
/client/reducers/commentsReducer.js:
--------------------------------------------------------------------------------
1 | import createReducer from '../lib/createReducer'
2 |
3 | import {
4 | HEART_REQUEST,
5 | } from '../actions/heartActions'
6 |
7 | const incHeart = (comments, id) => {
8 | if (!comments) { return null }
9 | const index = comments.findIndex(p => p.heartableId === id)
10 | if (index === -1) { return comments }
11 | const heartable = comments[index]
12 | return [
13 | ...comments.slice(0, index),
14 | { ...heartable, hearts: heartable.hearts + 1 },
15 | ...comments.slice(index + 1),
16 | ]
17 | }
18 |
19 | export default createReducer({
20 | items: null,
21 | }, {
22 | [HEART_REQUEST]: ({ payload: { heartableId } }, state) => ({
23 | items: incHeart(state.items, heartableId),
24 | }),
25 | })
26 |
--------------------------------------------------------------------------------
/client/reducers/currentProtipReducer.js:
--------------------------------------------------------------------------------
1 | import createReducer from '../lib/createReducer'
2 |
3 | import {
4 | PROTIP_MUTE_REQUEST,
5 | PROTIP_MUTE_FAILURE,
6 | PROTIP_SUBSCRIBE_REQUEST,
7 | PROTIP_SUBSCRIBE_FAILURE,
8 | } from '../actions/protipActions'
9 |
10 | import {
11 | HEART_REQUEST,
12 | } from '../actions/heartActions'
13 |
14 | const incHeart = (tip, heartableId) => {
15 | // console.log({tip, heartableId})
16 | if (!tip || tip.heartableId !== heartableId) { return tip }
17 |
18 | return {
19 | ...tip,
20 | hearts: tip.hearts + 1,
21 | subscribed: true,
22 | }
23 | }
24 |
25 | const setSubscribed = (subscribed) => (_, state) => ({
26 | item: { ...state.item, subscribed },
27 | })
28 |
29 | export default createReducer({
30 | item: null,
31 | }, {
32 | [PROTIP_SUBSCRIBE_REQUEST]: setSubscribed(true),
33 | [PROTIP_SUBSCRIBE_FAILURE]: setSubscribed(false),
34 | [PROTIP_MUTE_REQUEST]: setSubscribed(false),
35 | [PROTIP_MUTE_FAILURE]: setSubscribed(true),
36 |
37 | [HEART_REQUEST]: ({ payload: { heartableId } }, state) => ({
38 | item: incHeart(state.item, heartableId),
39 | }),
40 | })
41 |
--------------------------------------------------------------------------------
/client/reducers/currentUserReducer.js:
--------------------------------------------------------------------------------
1 | import createReducer from '../lib/createReducer'
2 |
3 | export default createReducer({
4 | item: null,
5 | }, {})
6 |
--------------------------------------------------------------------------------
/client/reducers/heartsReducer.js:
--------------------------------------------------------------------------------
1 | import createReducer from '../lib/createReducer'
2 |
3 | import {
4 | HEART_REQUEST,
5 | HEART_FAILURE,
6 | } from '../actions/heartActions'
7 |
8 | export default createReducer({
9 | items: [],
10 | }, {
11 | [HEART_REQUEST]: ({ payload: { heartableId } }, state) => ({
12 | items: [...state.items, heartableId],
13 | }),
14 | [HEART_FAILURE]: ({ payload: { heartableId } }, state) => ({
15 | items: state.items.filter(i => i !== heartableId),
16 | }),
17 | })
18 |
--------------------------------------------------------------------------------
/client/reducers/index.js:
--------------------------------------------------------------------------------
1 | import comments from './commentsReducer'
2 | import currentProtip from './currentProtipReducer'
3 | import currentUser from './currentUserReducer'
4 | import hearts from './heartsReducer'
5 | import job from './jobReducer'
6 | import protips from './protipsReducer'
7 |
8 | export default {
9 | comments,
10 | currentProtip,
11 | currentUser,
12 | hearts,
13 | job,
14 | protips,
15 | }
16 |
--------------------------------------------------------------------------------
/client/reducers/jobReducer.js:
--------------------------------------------------------------------------------
1 | import createReducer from '../lib/createReducer'
2 |
3 | import {
4 | JOB_POST_SUCCESS,
5 | JOB_POST_FAILURE,
6 | } from '../actions/jobActions'
7 |
8 |
9 | export default createReducer({
10 | error: null,
11 | item: null,
12 | }, {
13 | [JOB_POST_SUCCESS]: ({ payload }) => ({ item: payload.job }),
14 | [JOB_POST_FAILURE]: ({ error }) => ({ error }),
15 | })
16 |
--------------------------------------------------------------------------------
/client/reducers/protipsReducer.js:
--------------------------------------------------------------------------------
1 | import createReducer from '../lib/createReducer'
2 |
3 | import {
4 | HEART_REQUEST,
5 | } from '../actions/heartActions'
6 |
7 | const incHeart = (protips, id) => {
8 | if (!protips) { return null }
9 | const index = protips.findIndex(p => p.heartableId === id)
10 | if (index === -1) { return protips }
11 |
12 | const heartable = protips[index]
13 | return [
14 | ...protips.slice(0, index),
15 | { ...heartable, hearts: heartable.hearts + 1 },
16 | ...protips.slice(index + 1),
17 | ]
18 | }
19 |
20 | export default createReducer({
21 | items: null,
22 | }, {
23 | [HEART_REQUEST]: ({ payload: { heartableId } }, state) => ({
24 | items: incHeart(state.items, heartableId),
25 | }),
26 | })
27 |
--------------------------------------------------------------------------------
/client/server-rails-hot.js:
--------------------------------------------------------------------------------
1 | /* eslint no-var: 0, no-console: 0, import/no-extraneous-dependencies: 0 */
2 |
3 | import webpack from 'webpack'
4 | import WebpackDevServer from 'webpack-dev-server'
5 |
6 | import webpackConfig from './webpack.client.rails.hot.config'
7 |
8 | const hotRailsPort = process.env.HOT_RAILS_PORT || 3500
9 |
10 | const compiler = webpack(webpackConfig)
11 |
12 | const devServer = new WebpackDevServer(compiler, {
13 | contentBase: `http://lvh.me:${hotRailsPort}`,
14 | publicPath: webpackConfig.output.publicPath,
15 | hot: true,
16 | inline: true,
17 | historyApiFallback: true,
18 | quiet: false,
19 | noInfo: false,
20 | lazy: false,
21 | stats: {
22 | colors: true,
23 | hash: false,
24 | version: false,
25 | chunks: false,
26 | children: false,
27 | },
28 | })
29 |
30 | devServer.listen(hotRailsPort, 'localhost', err => {
31 | if (err) console.error(err)
32 | console.log(
33 | `=> 🔥 Webpack development server is running on port ${hotRailsPort}`
34 | )
35 | })
36 |
--------------------------------------------------------------------------------
/client/startup/clientRegistration.jsx:
--------------------------------------------------------------------------------
1 | /* global document, $ */
2 | // polyfills
3 | import 'whatwg-fetch'
4 | import 'pusher-js'
5 | import turbolinks from 'turbolinks'
6 |
7 | import { Provider } from 'react-redux'
8 | import React from 'react'
9 | import ReactOnRails from 'react-on-rails'
10 |
11 | import store from '../stores/store'
12 | import Heart from '../components/Heart'
13 | import HeartButton from '../components/HeartButton'
14 | import NewJob from '../components/NewJob'
15 | import NewJobSubscription from '../components/NewJobSubscription'
16 | import ProtipSubscribeButton from '../components/ProtipSubscribeButton'
17 | import Sponsors from '../components/Sponsors'
18 |
19 | turbolinks.start()
20 |
21 | ReactOnRails.setOptions({
22 | traceTurbolinks: TRACE_TURBOLINKS, // eslint-disable-line no-undef
23 | })
24 | ReactOnRails.registerStore({ store })
25 |
26 | function withStore(c) {
27 | return props => React.createElement(
28 | Provider,
29 | { store: ReactOnRails.getStore('store') },
30 | React.createElement(c, props)
31 | )
32 | }
33 |
34 | function registerContainers(containers) {
35 | const containersWithStore = Object.keys(containers).
36 | reduce((h, k) => ({ ...h, [k]: withStore(containers[k]) }), {})
37 | ReactOnRails.register(containersWithStore)
38 | }
39 |
40 | // Only container compoments need to be registered here
41 | // container components are rendered directly in view html
42 | // components that are children of containers don't need to be registered
43 | registerContainers({
44 | Heart,
45 | HeartButton,
46 | NewJob,
47 | NewJobSubscription,
48 | Sponsors,
49 | ProtipSubscribeButton,
50 | })
51 |
52 | require('./confirm')
53 |
--------------------------------------------------------------------------------
/client/startup/confirm.js:
--------------------------------------------------------------------------------
1 | /* global console, document, window */
2 | /* eslint no-alert: 0, no-console: 0 */
3 | class Confirm {
4 | constructor(el) {
5 | this.message = el.getAttribute('data-confirm')
6 | if (this.message) {
7 | el.form.addEventListener('submit', this.confirm.bind(this))
8 | } else {
9 | console.warn('No value specified in `data-confirm`', el)
10 | }
11 | }
12 |
13 | confirm(e) {
14 | if (!window.confirm(this.message)) {
15 | e.preventDefault()
16 | }
17 | }
18 | }
19 |
20 | document.addEventListener('turbolinks:load', () => {
21 | Array.from(document.querySelectorAll('[data-confirm]')).forEach((el) => new Confirm(el))
22 | })
23 |
--------------------------------------------------------------------------------
/client/startup/serverRegistration.jsx:
--------------------------------------------------------------------------------
1 | // TODO server rendering
2 |
3 | // import { Provider } from 'react-redux'
4 | // import React from 'react'
5 | // import ReactOnRails from 'react-on-rails'
6 | // import store from '../stores/store'
7 | // import HeartButton from '../components/HeartButton'
8 | //
9 | // ReactOnRails.registerStore({ store })
10 | //
11 | // function withStore(c) {
12 | // return props => React.createElement(
13 | // Provider,
14 | // { store: ReactOnRails.getStore('store') },
15 | // React.createElement(c, props)
16 | // )
17 | // }
18 | //
19 | // function registerContainers(containers) {
20 | // const containersWithStore = Object.keys(containers).
21 | // reduce((h, k) => ({ ...h, [k]: withStore(containers[k]) }), {})
22 | // ReactOnRails.register(containersWithStore)
23 | // }
24 | //
25 | // // Only container compoments need to be registered here
26 | // // container components are rendered directly in view html
27 | // // components that are children of containers don't need to be registered
28 | // registerContainers({
29 | // HeartButton,
30 | // })
31 |
--------------------------------------------------------------------------------
/client/stores/store.js:
--------------------------------------------------------------------------------
1 | /* global window */
2 | import { combineReducers, applyMiddleware, createStore, compose } from 'redux'
3 | import promise from 'redux-promise'
4 | import thunk from 'redux-thunk'
5 | import { apiMiddleware } from 'redux-api-middleware'
6 | import { reducer as form } from 'redux-form'
7 | import apiAuthInjector from '../lib/apiAuthInjector'
8 | import unauthorizedHandler from '../lib/unauthorizedHandler'
9 | import reducers from '../reducers'
10 |
11 | export const STATE_HYDRATED = 'STATE_HYDRATED'
12 |
13 | export default function configureStore(props) {
14 | const store = createStore(
15 | combineReducers({
16 | ...reducers,
17 | form,
18 | }),
19 | props,
20 | compose(
21 | applyMiddleware(
22 | thunk,
23 | promise,
24 | apiAuthInjector,
25 | apiMiddleware,
26 | unauthorizedHandler
27 | ),
28 | window.devToolsExtension ? window.devToolsExtension() : f => f
29 | )
30 | )
31 |
32 | store.dispatch({ type: STATE_HYDRATED, payload: store.getState() })
33 |
34 | return store
35 | }
36 |
--------------------------------------------------------------------------------
/client/webpack.client.rails.build.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack')
2 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
3 |
4 | const config = require('./webpack.client.base.config')
5 |
6 | const devBuild = process.env.NODE_ENV !== 'production'
7 |
8 | config.output = {
9 | filename: '[name]-bundle.js',
10 | path: '../app/assets/webpack',
11 | }
12 |
13 | // You can add entry points specific to rails here
14 | // config.entry.vendor.unshift(
15 | // 'es5-shim/es5-shim',
16 | // 'es5-shim/es5-sham'
17 | // )
18 |
19 | // See webpack.common.config for adding modules common to both the webpack dev server and rails
20 |
21 | config.module.loaders.push(
22 | {
23 | test: /\.jsx?$/,
24 | loader: 'babel-loader',
25 | exclude: /node_modules/,
26 | },
27 | {
28 | test: /\.css$/,
29 | loader: ExtractTextPlugin.extract(
30 | 'style',
31 | 'css?minimize&modules&importLoaders=1&localIdentName=[name]__[local]__[hash:base64:5]' +
32 | '!postcss'
33 | ),
34 | },
35 | {
36 | test: /\.scss$/,
37 | loader: ExtractTextPlugin.extract(
38 | 'style',
39 | 'css?minimize&modules&importLoaders=3&localIdentName=[name]__[local]__[hash:base64:5]' +
40 | '!postcss' +
41 | '!sass' +
42 | '!sass-resources'
43 | ),
44 | }
45 | )
46 |
47 | config.plugins.push(
48 | new ExtractTextPlugin('[name]-bundle.css', { allChunks: true }),
49 | new webpack.optimize.DedupePlugin()
50 | )
51 |
52 | if (devBuild) {
53 | console.error('Webpack dev build for Rails') // eslint-disable-line no-console
54 | config.devtool = 'eval-source-map'
55 | } else {
56 | console.error('Webpack production build for Rails') // eslint-disable-line no-console
57 | config.devtool = 'source-map'
58 | }
59 |
60 | module.exports = config
61 |
--------------------------------------------------------------------------------
/client/webpack.server.rails.build.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack')
2 | const path = require('path')
3 |
4 | const devBuild = process.env.NODE_ENV !== 'production'
5 | const nodeEnv = devBuild ? 'development' : 'production'
6 |
7 | module.exports = {
8 |
9 | // the project dir
10 | context: __dirname,
11 | entry: [
12 | 'babel-polyfill',
13 | './startup/serverRegistration',
14 | ],
15 | output: {
16 | filename: 'server-bundle.js',
17 | path: '../app/assets/webpack',
18 | },
19 | resolve: {
20 | extensions: ['', '.js', '.jsx'],
21 | alias: {
22 | libs: path.join(process.cwd(), 'app', 'libs'),
23 | },
24 | },
25 | plugins: [
26 | new webpack.DefinePlugin({
27 | 'process.env': {
28 | NODE_ENV: JSON.stringify(nodeEnv),
29 | },
30 | }),
31 | ],
32 | module: {
33 | loaders: [
34 | { test: /\.jsx?$/, loader: 'babel-loader', exclude: /node_modules/ },
35 | {
36 | test: /\.css$/,
37 | loaders: [
38 | 'css/locals?modules&importLoaders=0&localIdentName=[name]__[local]__[hash:base64:5]',
39 | ],
40 | },
41 | {
42 | test: /\.scss$/,
43 | loaders: [
44 | 'css/locals?modules&importLoaders=2&localIdentName=[name]__[local]__[hash:base64:5]',
45 | 'sass',
46 | 'sass-resources',
47 | ],
48 | },
49 | ],
50 | },
51 |
52 | sassResources: ['./app/assets/styles/app-variables.scss'],
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require ::File.expand_path('../config/environment', __FILE__)
4 | run Rails.application
5 |
--------------------------------------------------------------------------------
/config/application.rb:
--------------------------------------------------------------------------------
1 | require_relative 'boot'
2 |
3 | require 'rails/all'
4 |
5 | # Require the gems listed in Gemfile, including any gems
6 | # you've limited to :test, :development, or :production.
7 | Bundler.require(*Rails.groups)
8 |
9 | module CoderwallNext
10 | class Application < Rails::Application
11 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
12 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
13 | # config.time_zone = 'Central Time (US & Canada)'
14 |
15 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
16 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
17 | # config.i18n.default_locale = :de
18 |
19 | # Do not swallow errors in after_commit/after_rollback callbacks.
20 | # config.active_record.raise_in_transactional_callbacks = true
21 | config.assets.precompile += %w(.png .svg)
22 | config.exceptions_app = self.routes
23 | config.encoding = 'utf-8'
24 |
25 | config.lograge.enabled = true
26 | config.lograge.custom_options = lambda do |event|
27 | {
28 | params: event.payload[:params].reject { |k| %w(controller action).include?(k) }
29 | }
30 | end
31 |
32 | config.log_tags = [:uuid]
33 | config.log_level = ENV['LOG_LEVEL'] || :debug
34 |
35 | config.middleware.delete ActiveRecord::Migration::CheckPending
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/config/boot.rb:
--------------------------------------------------------------------------------
1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
2 |
3 | require 'bundler/setup' # Set up gems listed in the Gemfile.
4 |
--------------------------------------------------------------------------------
/config/cable.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: async
3 |
4 | test:
5 | adapter: async
6 |
7 | production:
8 | adapter: redis
9 | url: redis://localhost:6379/1
10 |
--------------------------------------------------------------------------------
/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative 'application'
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/config/initializers/application_controller_renderer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # ApplicationController.renderer.defaults.merge!(
4 | # http_host: 'example.org',
5 | # https: false
6 | # )
7 |
--------------------------------------------------------------------------------
/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = '1.0'
5 |
6 | # Add additional assets to the asset load path
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 |
9 | # Precompile additional assets.
10 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
11 | # Rails.application.config.assets.precompile += %w( search.js )
12 |
--------------------------------------------------------------------------------
/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/config/initializers/carrierwave.rb:
--------------------------------------------------------------------------------
1 | CarrierWave.configure do |config|
2 | if Rails.env.test?
3 | config.enable_processing = false
4 | config.storage = :file
5 | elsif Rails.env.development?
6 | config.enable_processing = true
7 | config.storage = :file
8 | config.asset_host = ActionController::Base.asset_host
9 | else
10 | config.root = Rails.root.join('tmp')
11 | config.cache_dir = "#{Rails.root}/tmp/uploads"
12 | config.enable_processing = true
13 | config.storage = :aws
14 | config.asset_host = "https://#{ENV['AWS_BUCKET']}.s3.amazonaws.com"
15 | config.aws_acl = 'public-read'
16 | config.aws_bucket = ENV['AWS_BUCKET']
17 | config.aws_credentials = {
18 | access_key_id: ENV['AWS_ACCESS_ID'],
19 | secret_access_key: ENV['AWS_ACCESS_SECRET'],
20 | region: ENV['AWS_REGION']
21 | }
22 | config.aws_attributes = {
23 | expires: 1.week.from_now.httpdate,
24 | cache_control: 'max-age=604800'
25 | }
26 | config.aws_authenticated_url_expiration = 60 * 60 * 24 * 7
27 | end
28 | end
29 |
30 | CarrierWave::SanitizedFile.sanitize_regexp = /[^[:word:]\.\-\+]/
31 |
--------------------------------------------------------------------------------
/config/initializers/clearance.rb:
--------------------------------------------------------------------------------
1 | # https://github.com/thoughtbot/clearance
2 | Clearance.configure do |config|
3 | config.httponly = true #cookies only accessed from server
4 | config.routes = false #disable clearance routes
5 | config.mailer_sender = "support@coderwall.com"
6 | config.cookie_expiration = ->(cookies){ 2.years.from_now.utc }
7 | config.rotate_csrf_on_sign_in = true
8 |
9 | if Rails.env.development?
10 | config.cookie_domain = 'localhost'
11 | elsif Rails.env.production?
12 | config.cookie_domain = '.coderwall.com'
13 | config.secure_cookie = true
14 | end
15 |
16 | end
17 |
--------------------------------------------------------------------------------
/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Specify a serializer for the signed and encrypted cookie jars.
4 | # Valid options are :json, :marshal, and :hybrid.
5 | Rails.application.config.action_dispatch.cookies_serializer = :json
6 |
--------------------------------------------------------------------------------
/config/initializers/cors.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Avoid CORS issues when API is called from the frontend app.
4 | # Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.
5 |
6 | # Read more: https://github.com/cyu/rack-cors
7 |
8 | Rails.application.config.middleware.insert_before 0, Rack::Cors do
9 | allow do
10 | origins '*'
11 |
12 | resource '/assets/*',
13 | headers: :any,
14 | methods: [:get, :options, :head]
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [
5 | :password,
6 | :password_confirmation,
7 | :username,
8 | :email,
9 | :name,
10 | :avatar,
11 | :title,
12 | :country,
13 | :city,
14 | :state_name,
15 | :company,
16 | :about,
17 | :team_id,
18 | :last_ip
19 | ]
20 |
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/config/initializers/invisible_captcha.rb:
--------------------------------------------------------------------------------
1 | InvisibleCaptcha.setup do |config|
2 | config.honeypots = [
3 | :city,
4 | :description,
5 | :subtitle,
6 | :website,
7 | :zip,
8 | ]
9 | config.visual_honeypots = false
10 | config.timestamp_threshold = 15
11 | config.timestamp_enabled = true
12 | end
13 |
--------------------------------------------------------------------------------
/config/initializers/meta_tags.rb:
--------------------------------------------------------------------------------
1 | MetaTags.configure do |config|
2 | config.title_limit = 70
3 | config.description_limit = 160
4 | config.keywords_limit = 255
5 | config.keywords_separator = ', '
6 | end
7 |
--------------------------------------------------------------------------------
/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 |
--------------------------------------------------------------------------------
/config/initializers/new_framework_defaults.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 | #
3 | # This file contains migration options to ease your Rails 5.0 upgrade.
4 | #
5 | # Once upgraded flip defaults one by one to migrate to the new default.
6 | #
7 | # Read the Guide for Upgrading Ruby on Rails for more info on each option.
8 |
9 | # Enable per-form CSRF tokens. Previous versions had false.
10 | Rails.application.config.action_controller.per_form_csrf_tokens = false
11 |
12 | # Enable origin-checking CSRF mitigation. Previous versions had false.
13 | Rails.application.config.action_controller.forgery_protection_origin_check = false
14 |
15 | # Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`.
16 | # Previous versions had false.
17 | ActiveSupport.to_time_preserves_timezone = false
18 |
19 | # Require `belongs_to` associations by default. Previous versions had false.
20 | Rails.application.config.active_record.belongs_to_required_by_default = false
21 |
22 | # Do not halt callback chains when a callback returns false. Previous versions had true.
23 | ActiveSupport.halt_callback_chains_on_return_false = true
24 |
--------------------------------------------------------------------------------
/config/initializers/puma_worker_killer.rb:
--------------------------------------------------------------------------------
1 | # https://github.com/schneems/puma_worker_killer
2 | if Rails.env.production?
3 | PumaWorkerKiller.config do |config|
4 | config.ram = Integer(ENV['MAX_RAM'] || 512) # mb
5 | config.frequency = 20 # seconds
6 | config.percent_usage = 0.95
7 | config.rolling_restart_frequency = 12 * 3600 # 12 hours in seconds
8 | end
9 | PumaWorkerKiller.start
10 | end
11 |
--------------------------------------------------------------------------------
/config/initializers/rack_mini_profiler.rb:
--------------------------------------------------------------------------------
1 | if ENV['ENABLE_PROFILER']
2 | require 'rack-mini-profiler'
3 |
4 | # initialization is skipped so trigger it
5 | Rack::MiniProfilerRails.initialize!(Rails.application)
6 | end
7 |
--------------------------------------------------------------------------------
/config/initializers/rack_timeout.rb:
--------------------------------------------------------------------------------
1 | # Rack::Timeout.service_timeout = ENV.fetch('RACK_TIMEOUT', 5).to_i
2 |
--------------------------------------------------------------------------------
/config/initializers/sanitization.rb:
--------------------------------------------------------------------------------
1 | Rails.application.config.after_initialize do
2 | # Allow links to have rel=nofollow to discourage spam
3 | ActionView::Base.sanitized_allowed_attributes << 'rel'
4 | end
5 |
--------------------------------------------------------------------------------
/config/initializers/session_store.rb:
--------------------------------------------------------------------------------
1 | Rails.application.config.session_store :cookie_store,
2 | key: '_coderwall-next_session',
3 | :expire_after => 2.years
4 |
--------------------------------------------------------------------------------
/config/initializers/stripe.rb:
--------------------------------------------------------------------------------
1 | Rails.configuration.stripe = {
2 | publishable_key: ENV['STRIPE_PUBLISHABLE_KEY'],
3 | secret_key: ENV['STRIPE_SECRET_KEY']
4 | }
5 |
6 | Stripe.api_key = Rails.configuration.stripe[:secret_key]
7 |
--------------------------------------------------------------------------------
/config/initializers/time_formats.rb:
--------------------------------------------------------------------------------
1 | Time::DATE_FORMATS[:explicitly_bold] = "%B %Y"
2 | Time::DATE_FORMATS[:seo] = "%B %d, %Y"
3 |
--------------------------------------------------------------------------------
/config/initializers/webpack.rb:
--------------------------------------------------------------------------------
1 | Rails.application.config.assets.paths << Rails.root.join("app", "assets", "webpack")
2 | Rails.application.config.assets.precompile += %w( minimal.css live-banner.jpg happy-cat.jpg conference-room.png offline-holder.png server-bundle.js)
3 | Rails.application.config.assets.compile = true
4 |
5 | type = ENV["REACT_ON_RAILS_ENV"] == "HOT" ? "non_webpack" : "static"
6 | Rails.application.config.assets.precompile += [
7 | "application_#{type}.js",
8 | "application_#{type}.css"
9 | ]
10 |
11 | # suppress annoying asset 404s
12 | if Rails.env.development?
13 | class ActionDispatch::DebugExceptions
14 | alias_method :old_log_error, :log_error
15 | def log_error(env, wrapper)
16 | if wrapper.exception.is_a? ActionController::RoutingError
17 | return
18 | else
19 | old_log_error env, wrapper
20 | end
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json]
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/config/letsencrypt_plugin.yml:
--------------------------------------------------------------------------------
1 | production:
2 | endpoint: 'https://acme-v01.api.letsencrypt.org/'
3 | email: 'matt@assemblymade.com'
4 | domain: coderwall.com www.coderwall.com
5 | private_key_in_db: true
6 |
--------------------------------------------------------------------------------
/config/locales/invisible_captcha.en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | invisible_captcha:
3 | sentence_for_humans: "Leave this field blank"
4 | timestamp_error_message: "Sorry, that was too quick! Please resubmit."
5 |
--------------------------------------------------------------------------------
/config/puma.rb:
--------------------------------------------------------------------------------
1 | workers Integer(ENV['WEB_CONCURRENCY'] || 2)
2 | threads_count = Integer(ENV['MAX_THREADS'] || 5)
3 | threads threads_count, threads_count
4 |
5 | worker_timeout 15
6 | worker_shutdown_timeout 8
7 |
8 | preload_app!
9 |
10 | port ENV['PORT'] || 3000
11 | environment ENV['RACK_ENV'] || 'development'
12 |
13 | on_worker_boot do
14 | ActiveRecord::Base.establish_connection
15 | end
16 |
--------------------------------------------------------------------------------
/config/secrets.yml:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Your secret key is used for verifying the integrity of signed cookies.
4 | # If you change this key, all old signed cookies will become invalid!
5 |
6 | # Make sure the secret is at least 30 characters and all random,
7 | # no regular words or you'll be exposed to dictionary attacks.
8 | # You can use `rake secret` to generate a secure secret key.
9 |
10 | # Make sure the secrets in this file are kept private
11 | # if you're sharing your code publicly.
12 |
13 | development:
14 | secret_key_base: 692564d1b453ed072faad3ef208ee6e1350ef0d0c84e4e96991690a353610a37a884e2181bc8fb2f8b963f3d053189dc962ef126aa9cd6128475e276db5f4c89
15 |
16 | test:
17 | secret_key_base: 5af0ed63c7b9c73a2d2d2e4ae54028c79096e0b6b5e465db6f04d940e0cb2b3bb135e54327e37db1512c3749d00eb7ba1755c204f7e39bb2ce2bfbdc1e2e2485
18 |
19 | # Do not keep production secrets in the repository,
20 | # instead read values from the environment.
21 | production:
22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
23 |
--------------------------------------------------------------------------------
/config/spring.rb:
--------------------------------------------------------------------------------
1 | %w(
2 | .ruby-version
3 | .rbenv-vars
4 | tmp/restart.txt
5 | tmp/caching-dev.txt
6 | ).each { |path| Spring.watch(path) }
7 |
--------------------------------------------------------------------------------
/db/migrate/20160119204658_create_protips.rb:
--------------------------------------------------------------------------------
1 | class CreateProtips < ActiveRecord::Migration
2 |
3 | # Omitted
4 | # kind :string(255)
5 | # created_by :string(255) default("self")
6 | # featured :boolean default(FALSE)
7 | # upvotes_value_cache :integer default(0), not null
8 | # inappropriate :integer default(0)
9 | # likes_count :integer default(0)
10 | # user_name :string(255)
11 | # user_email :string(255)
12 | # user_agent :string(255)
13 | # user_ip :inet
14 | # spam_reports_count :integer default(0)
15 | # state :string(255) default("active")
16 |
17 | def change
18 | create_table :protips do |t|
19 | t.string :public_id, :title, :slug
20 | t.text :body
21 | t.integer :user_id
22 | t.float :score, :boost_factor
23 | t.timestamp :featured_at
24 | t.timestamps null: false
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/db/migrate/20160119204714_create_users.rb:
--------------------------------------------------------------------------------
1 | class CreateUsers < ActiveRecord::Migration
2 | def up
3 | enable_extension("citext")
4 |
5 | create_table :users do |t|
6 | t.string :name, :avatar, :title, :location, :country, :city, :state_name
7 | t.string :company
8 | t.text :about
9 | t.integer :team_id
10 | t.string :api_key
11 | t.boolean :admin
12 | t.boolean :receive_newsletter, default: true
13 | t.boolean :receive_weekly_digest, default: true
14 | t.integer :login_count
15 | t.integer :last_ip
16 | t.timestamp :banned_at
17 | t.timestamp :last_email_sent, :last_request_at
18 | t.timestamps null: false
19 | end
20 |
21 | add_column :users, :username, :citext
22 | add_column :users, :email, :citext
23 | end
24 |
25 | def down
26 | drop_table :users
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/db/migrate/20160120175334_add_clearance_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddClearanceToUsers < ActiveRecord::Migration
2 | def self.up
3 | change_table :users do |t|
4 | t.string :encrypted_password, limit: 128
5 | t.string :confirmation_token, limit: 128
6 | t.string :remember_token, limit: 128
7 | end
8 |
9 | add_index :users, :email
10 | add_index :users, :remember_token
11 |
12 | users = select_all("SELECT id FROM users WHERE remember_token IS NULL")
13 |
14 | users.each do |user|
15 | update <<-SQL
16 | UPDATE users
17 | SET remember_token = '#{Clearance::Token.new}'
18 | WHERE id = '#{user['id']}'
19 | SQL
20 | end
21 | end
22 |
23 | def self.down
24 | change_table :users do |t|
25 | t.remove :encrypted_password, :confirmation_token, :remember_token
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/db/migrate/20160120200829_create_comments.rb:
--------------------------------------------------------------------------------
1 | class CreateComments < ActiveRecord::Migration
2 | def change
3 | create_table :comments do |t|
4 | t.text :body
5 | t.integer :protip_id, :user_id
6 | t.timestamps null: false
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20160121012718_create_likes.rb:
--------------------------------------------------------------------------------
1 | class CreateLikes < ActiveRecord::Migration
2 | def change
3 | create_table :likes do |t|
4 | t.integer :likable_id, :user_id, :value
5 | t.string :likable_type
6 | t.timestamps null: false
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20160121013050_create_badges.rb:
--------------------------------------------------------------------------------
1 | class CreateBadges < ActiveRecord::Migration
2 | def change
3 | create_table :badges do |t|
4 | t.integer :user_id
5 | t.string :name, :description, :why, :image_name, :provider
6 | t.timestamps null: false
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20160210220146_add_tags_and_count_columns.rb:
--------------------------------------------------------------------------------
1 | class AddTagsAndCountColumns < ActiveRecord::Migration
2 | def change
3 |
4 | change_table :protips do |t|
5 | t.string :tags, array: true, default: []
6 | t.integer :likes_count, default: 0
7 | t.integer :views_count, default: 0
8 | end
9 |
10 | add_index :protips, :tags, using: 'gin'
11 |
12 | change_table :users do |t|
13 | t.string :skills, array: true, default: []
14 | t.string :github_id, :twitter_id, :github, :twitter
15 | end
16 |
17 | add_index :users, :skills, using: 'gin'
18 |
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/db/migrate/20160210221757_add_color_to_user.rb:
--------------------------------------------------------------------------------
1 | class AddColorToUser < ActiveRecord::Migration
2 | def change
3 | add_column :users, :color, :string, default: '#111'
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20160210222721_add_karma_to_user.rb:
--------------------------------------------------------------------------------
1 | class AddKarmaToUser < ActiveRecord::Migration
2 | def change
3 | add_column :users, :karma, :integer, default: 1
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20160212233312_add_flagged_to_protip.rb:
--------------------------------------------------------------------------------
1 | class AddFlaggedToProtip < ActiveRecord::Migration
2 | def change
3 | add_column :protips, :flagged, :boolean, default: false
4 | remove_column :protips, :boost_factor
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/db/migrate/20160213012958_add_likes_cache.rb:
--------------------------------------------------------------------------------
1 | class AddLikesCache < ActiveRecord::Migration
2 | def change
3 | add_column :comments, :likes_count, :integer, default: 0
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20160213015810_remove_login_counts.rb:
--------------------------------------------------------------------------------
1 | class RemoveLoginCounts < ActiveRecord::Migration
2 | def change
3 | remove_column(:users, :login_count)
4 | remove_column(:users, :banned_at)
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/db/migrate/20160214213211_clean_up_likes.rb:
--------------------------------------------------------------------------------
1 | class CleanUpLikes < ActiveRecord::Migration
2 | def change
3 | remove_column :likes, :value
4 | add_index :likes, [:user_id, :likable_type, :likable_id], :unique => true
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/db/migrate/20160219071138_improving_database_performane.rb:
--------------------------------------------------------------------------------
1 | class ImprovingDatabasePerformane < ActiveRecord::Migration
2 | def change
3 | add_index "users", "username", unique: true
4 | add_index "protips", "public_id", unique: true
5 | add_index "protips", "score", unique: false
6 | add_index "comments", "protip_id", unique: false
7 | add_index "badges", "user_id", unique: false
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20160219190140_add_banned_users_for_migration.rb:
--------------------------------------------------------------------------------
1 | class AddBannedUsersForMigration < ActiveRecord::Migration
2 | def change
3 | add_column :users, :banned_at, :datetime
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20160223063803_add_more_indexes.rb:
--------------------------------------------------------------------------------
1 | class AddMoreIndexes < ActiveRecord::Migration
2 | def change
3 | add_index :comments, :user_id, unique: false
4 | add_index :protips, :user_id, unique: false
5 | add_index :protips, :created_at, unique: false
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20160223064301_changing_user_to_citext.rb:
--------------------------------------------------------------------------------
1 | class ChangingUserToCitext < ActiveRecord::Migration
2 | def change
3 | enable_extension :citext
4 | change_column :users, :email, :citext
5 | change_column :users, :username, :citext
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20160223065728_create_team.rb:
--------------------------------------------------------------------------------
1 | class CreateTeam < ActiveRecord::Migration
2 | def change
3 | create_table :teams do |t|
4 | t.string :name
5 | t.string :avatar
6 | t.citext :slug
7 | t.string :website, :twitter, :facebook, :github
8 | t.string :youtube_url, :blog_feed
9 | t.string :location
10 | t.text :about
11 | t.string :color
12 | t.timestamps
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/db/migrate/20160225042520_add_simple_user_id_index_on_like.rb:
--------------------------------------------------------------------------------
1 | class AddSimpleUserIdIndexOnLike < ActiveRecord::Migration
2 | def change
3 | add_index :likes, :user_id, unique: false
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20160225070301_auto_like_content.rb:
--------------------------------------------------------------------------------
1 | class AutoLikeContent < ActiveRecord::Migration
2 | def up
3 | Protip.find_each do |protip|
4 | protip.likes.create(user: protip.user) unless protip.user.likes?(protip)
5 | end
6 | Comment.find_each do |comment|
7 | comment.protip.likes.create(user: comment.user) unless comment.user.likes?(comment.protip)
8 | end
9 | end
10 |
11 | def down
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/db/migrate/20160225171117_add_pictures.rb:
--------------------------------------------------------------------------------
1 | class AddPictures < ActiveRecord::Migration
2 | def up
3 | create_table :pictures do |t|
4 | t.integer :user_id
5 | t.string :file
6 | t.timestamps
7 | end
8 |
9 | add_index :pictures, :user_id, unique: false
10 | end
11 |
12 | def down
13 | drop_table :pictures
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/db/migrate/20160227000445_add_missing_foreign_keys.rb:
--------------------------------------------------------------------------------
1 | class AddMissingForeignKeys < ActiveRecord::Migration
2 | def change
3 | add_foreign_key "badges", "users", name: "badges_user_id_fk"
4 | add_foreign_key "comments", "protips", name: "comments_protip_id_fk"
5 | add_foreign_key "comments", "users", name: "comments_user_id_fk"
6 | add_foreign_key "likes", "users", name: "likes_user_id_fk"
7 | add_foreign_key "protips", "users", name: "protips_user_id_fk"
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20160227010236_turn_fields_case_insensitive.rb:
--------------------------------------------------------------------------------
1 | class TurnFieldsCaseInsensitive < ActiveRecord::Migration
2 | def change
3 | enable_extension :citext
4 | change_column :users, :username, :citext
5 | change_column :users, :email, :citext
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20160307195201_clean_up_orphan_pictures.rb:
--------------------------------------------------------------------------------
1 | class CleanUpOrphanPictures < ActiveRecord::Migration
2 | def change
3 |
4 | count = Picture.where("user_id NOT IN (select id from users)").count
5 | puts "Picture: deleting #{count} orphans"
6 | puts Picture.where("user_id NOT IN (select id from users)").delete_all
7 |
8 | add_foreign_key "pictures", "users", name: "pictures_user_id_fk"
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/db/migrate/20160318212558_add_marketing_list_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddMarketingListToUsers < ActiveRecord::Migration
2 | def change
3 | add_column :users, :marketing_list, :text
4 | add_column :users, :email_invalid_at, :datetime
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/db/migrate/20160422205835_add_view_counts_to_team.rb:
--------------------------------------------------------------------------------
1 | class AddViewCountsToTeam < ActiveRecord::Migration
2 | def change
3 | add_column "teams", "views_count", :integer, default: 0
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20160422211004_create_jobs.rb:
--------------------------------------------------------------------------------
1 | class CreateJobs < ActiveRecord::Migration
2 | def change
3 | create_table :jobs do |t|
4 | t.timestamps null: false
5 | t.string :role_type
6 | t.string :title
7 | t.string :location
8 | t.string :source
9 | t.text :description
10 | t.text :how_to_apply
11 | t.string :company
12 | t.string :company_url
13 | t.string :company_logo
14 | t.string :author_name
15 | t.string :author_email
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/db/migrate/20160422213924_move_job_id_over_to_uuid.rb:
--------------------------------------------------------------------------------
1 | class MoveJobIdOverToUuid < ActiveRecord::Migration
2 | def change
3 | enable_extension 'uuid-ossp'
4 |
5 | add_column :jobs, :uuid, :uuid, default: "uuid_generate_v4()", null: false
6 |
7 | change_table :jobs do |t|
8 | t.remove :id
9 | t.rename :uuid, :id
10 | end
11 |
12 | execute "ALTER TABLE jobs ADD PRIMARY KEY (id);"
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/db/migrate/20160422215652_trim_job.rb:
--------------------------------------------------------------------------------
1 | class TrimJob < ActiveRecord::Migration
2 | def change
3 | remove_column :jobs, :description
4 | remove_column :jobs, :how_to_apply
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/db/migrate/20160422234923_add_publish_attributes_to_jobs.rb:
--------------------------------------------------------------------------------
1 | class AddPublishAttributesToJobs < ActiveRecord::Migration
2 | def change
3 | add_column :jobs, :expires_at, :datetime
4 | add_column :jobs, :stripe_charge, :text
5 |
6 | add_index :jobs, :expires_at
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/db/migrate/20160425233554_create_job_views.rb:
--------------------------------------------------------------------------------
1 | class CreateJobViews < ActiveRecord::Migration
2 | def change
3 | create_table :job_views do |t|
4 | # required
5 | t.datetime :created_at, null: false
6 | t.uuid :job_id, null: false
7 |
8 | # optional
9 | t.integer :user_id
10 | t.text :ip
11 |
12 | t.foreign_key :jobs
13 | t.foreign_key :users
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/db/migrate/20160513032303_add_stream_key_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddStreamKeyToUsers < ActiveRecord::Migration
2 | def change
3 | add_column :users, :stream_key, :text
4 |
5 | add_index :users, :stream_key, unique: true
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20160519204919_add_type_to_protips.rb:
--------------------------------------------------------------------------------
1 | class AddTypeToProtips < ActiveRecord::Migration
2 | def up
3 | add_column :protips, :type, :text
4 | Article.update_all(type: Protip.name)
5 | change_column :protips, :type, :text, null: false
6 | add_index :protips, :type
7 | end
8 |
9 | def down
10 | remove_column :protips, :type, :text
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/db/migrate/20160519233923_rename_protip_id_on_comments_to_article_id.rb:
--------------------------------------------------------------------------------
1 | class RenameProtipIdOnCommentsToArticleId < ActiveRecord::Migration
2 | def change
3 | rename_column :comments, :protip_id, :article_id
4 | Like.where(likable_type: 'Protip').update_all(likable_type: 'Article')
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/db/migrate/20160601015828_add_started_archived_fields_to_articles.rb:
--------------------------------------------------------------------------------
1 | class AddStartedArchivedFieldsToArticles < ActiveRecord::Migration
2 | def change
3 | add_column :protips, :published_at, :datetime
4 | add_column :protips, :archived_at, :datetime
5 | add_column :protips, :save_recording, :bool
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20160607202132_add_recording_id_to_protips.rb:
--------------------------------------------------------------------------------
1 | class AddRecordingIdToProtips < ActiveRecord::Migration
2 | def change
3 | add_column :protips, :recording_id, :text
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20160608034824_add_recording_started_at_to_protips.rb:
--------------------------------------------------------------------------------
1 | class AddRecordingStartedAtToProtips < ActiveRecord::Migration
2 | def change
3 | add_column :protips, :recording_started_at, :datetime
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20160830184552_create_job_subscriptions.rb:
--------------------------------------------------------------------------------
1 | class CreateJobSubscriptions < ActiveRecord::Migration
2 | def change
3 | create_table :job_subscriptions, id: :uuid do |t|
4 | t.timestamps null: false
5 | t.string :jobs_url, null: false
6 | t.string :company_name, null: false
7 | t.string :contact_email, null: false
8 | t.string :stripe_customer_id
9 | t.string :subscribed_at
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/db/migrate/20160909044024_add_indexes.rb:
--------------------------------------------------------------------------------
1 | class AddIndexes < ActiveRecord::Migration
2 | def change
3 | add_index :protips, :views_count
4 | add_index :users, :receive_newsletter
5 | add_index :users, :marketing_list
6 | add_index :users, :email_invalid_at
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/db/migrate/20160913165240_add_comment_unsubscribed_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddCommentUnsubscribedToUsers < ActiveRecord::Migration
2 | def change
3 | add_column :users, :unsubscribed_comment_emails_at, :datetime
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20160916222644_add_subscribers_to_articles.rb:
--------------------------------------------------------------------------------
1 | class AddSubscribersToArticles < ActiveRecord::Migration
2 | def change
3 | add_column :protips, :subscribers, :int, array: true, default: [], null: false
4 | Protip.includes(:comments, :likes).find_each do |p|
5 | commentors = p.comments.map(&:user_id).uniq
6 | likers = p.likes.map(&:user_id).uniq
7 | subscribers = ([p.user_id] | commentors | likers)
8 | p.update_columns(subscribers: subscribers)
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/db/migrate/20160919171618_remove_unsubscribed_comment_emails_at_from_users.rb:
--------------------------------------------------------------------------------
1 | class RemoveUnsubscribedCommentEmailsAtFromUsers < ActiveRecord::Migration
2 | def change
3 | remove_column :users, :unsubscribed_comment_emails_at
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20160922010312_create_letsencrypt_plugin_challenges.letsencrypt_plugin.rb:
--------------------------------------------------------------------------------
1 | # This migration comes from letsencrypt_plugin (originally 20151206135029)
2 | class CreateLetsencryptPluginChallenges < ActiveRecord::Migration
3 | def change
4 | create_table :letsencrypt_plugin_challenges do |t|
5 | t.text :response
6 |
7 | t.timestamps null: false
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/db/migrate/20160922010313_create_letsencrypt_plugin_settings.letsencrypt_plugin.rb:
--------------------------------------------------------------------------------
1 | # This migration comes from letsencrypt_plugin (originally 20160412195212)
2 | class CreateLetsencryptPluginSettings < ActiveRecord::Migration
3 | def change
4 | create_table :letsencrypt_plugin_settings do |t|
5 | t.text :private_key
6 |
7 | t.timestamps null: false
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/db/migrate/20160922185544_remove_job_views.rb:
--------------------------------------------------------------------------------
1 | class RemoveJobViews < ActiveRecord::Migration
2 | def change
3 | drop_table :job_views
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20160923195619_add_partner_info.rb:
--------------------------------------------------------------------------------
1 | class AddPartnerInfo < ActiveRecord::Migration
2 | def change
3 | add_column :users, :partner_last_contribution_at, :datetime
4 | add_column :users, :partner_asm_username, :string
5 | add_column :users, :partner_slack_username, :string
6 | add_column :users, :partner_email, :string
7 | add_column :users, :partner_coins, :integer
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/db/migrate/20161207222630_change_users_last_ip_from_integer_to_string.rb:
--------------------------------------------------------------------------------
1 | class ChangeUsersLastIpFromIntegerToString < ActiveRecord::Migration
2 | def change
3 | change_column :users, :last_ip, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20170109215300_add_spam_detected_at_to_protips.rb:
--------------------------------------------------------------------------------
1 | class AddSpamDetectedAtToProtips < ActiveRecord::Migration
2 | def change
3 | add_column :protips, :spam_detected_at, :datetime
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20170110195008_add_spam_fields_to_protips.rb:
--------------------------------------------------------------------------------
1 | class AddSpamFieldsToProtips < ActiveRecord::Migration
2 | def change
3 | add_column :protips, :user_ip, :string
4 | add_column :protips, :user_agent, :string
5 | add_column :protips, :referrer, :string
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20170220093535_remove_stream_key_from_users.rb:
--------------------------------------------------------------------------------
1 | class RemoveStreamKeyFromUsers < ActiveRecord::Migration[5.0]
2 | def change
3 | remove_column :users, :stream_key
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20170328232725_add_bad_users_and_content.rb:
--------------------------------------------------------------------------------
1 | class AddBadUsersAndContent < ActiveRecord::Migration[5.0]
2 | def change
3 | add_column :users, :bad_user, :bool, null: false, default: false
4 | add_column :comments, :bad_content, :bool, null: false, default: false
5 | rename_column :protips, :flagged, :bad_content
6 |
7 | add_index :users, :bad_user
8 | add_index :protips, :bad_content
9 | add_index :comments, :bad_content
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # This file should contain all the record creation needed to seed the database with its default values.
2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
3 | #
4 | # Examples:
5 | #
6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
7 | # Mayor.create(name: 'Emanuel', city: cities.first)
8 |
--------------------------------------------------------------------------------
/lib/tasks/cache.rake:
--------------------------------------------------------------------------------
1 | namespace :cache do
2 |
3 | task :clear => :environment do
4 | Rails.cache.clear
5 | end
6 |
7 | namespace :score do
8 | task :recalculate => :environment do
9 | ActiveRecord::Base.logger.level = Logger::INFO #hide sql output
10 | Protip.order(created_at: :asc).find_each(batch_size: 100) do |p|
11 | score = p.calculate_score
12 | p.update_column(:score, score)
13 | sleep 0.1
14 | end
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/lib/tasks/db.rake:
--------------------------------------------------------------------------------
1 | namespace :db do
2 | desc 'Quiet ActiveRecord!'
3 | task :mute => :environment do
4 | ActiveRecord::Base.logger = nil
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/lib/tasks/partners.rake:
--------------------------------------------------------------------------------
1 | namespace :partners do
2 |
3 | task :load => :environment do
4 | require 'csv'
5 | require 'open-uri'
6 | open("") do |file|
7 | CSV.parse(file, :headers => true) do |row|
8 | username = row[0]
9 | user = User.find_by_username(username)
10 | user.partner_asm_username = row[1]
11 | user.partner_slack_username = row[2]
12 | user.partner_email = row[3]
13 | user.partner_last_contribution_at = Date.strptime(row[4], "%m/%d/%Y")
14 | user.partner_coins = row[5]
15 | user.save!
16 | end
17 | end
18 | end
19 |
20 | task :update => :environment do
21 | flatten_to_latest(Github.user_pr_log).each do |username, contribution_date|
22 | if user = User.where(github: username).first
23 | user.partner_last_contribution_at = contribution_date
24 | user.save!
25 | end
26 | end
27 | end
28 |
29 | def flatten_to_latest(results)
30 | results.inject({}) do |users, row|
31 | user_id = row[:username]
32 | if users[user_id].blank? || users[user_id] < row[:created_at]
33 | users[user_id] = row[:created_at]
34 | end
35 | users
36 | end
37 | end
38 |
39 | task :email => :environment do
40 | User.where("partner_coins IS NOT NULL AND partner_last_contribution_at < ?", 1.year.ago).all.each do |user|
41 | UserMailer.partnership_expired(user).deliver_now
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/lib/tasks/report.rake:
--------------------------------------------------------------------------------
1 | namespace :report do
2 |
3 | task :revenue => :environment do
4 | # https://github.com/stomita/heroku-buildpack-phantomjs
5 | require 'capybara/poltergeist'
6 | Capybara.register_driver :poltergeist do |app|
7 | Capybara::Poltergeist::Driver.new(app, js_errors: false)
8 | end
9 | Capybara.default_driver = :poltergeist
10 | browser = Capybara.current_session
11 |
12 | browser.visit 'https://www.newsletterdirectory.co/log-in/'
13 | browser.find('#email').set('matt@assemblymade.com')
14 | browser.find('#password').set(ENV['SPONSOR_PWD'])
15 | sleep 2
16 | browser.find("#logInButton").trigger('click')
17 | sleep 2
18 | browser.visit "https://www.newsletterdirectory.co/manage-text-adzone/?adzone=11384"
19 |
20 | labels = browser.all('.adzonestats .grid_4').collect{|div| div.text}
21 | values = browser.all('.adzonestats .grid_2').collect{|div| div.text}
22 | report = [
23 | "Sponsor Revenue Performance",
24 | "#{values[0]} Avg CTR",
25 | "#{values[1]} Pending Monthly Earnings",
26 | "#{values[2]} Last Month's Earnings"
27 | ].join("\n")
28 |
29 | Slack.notify!(':moneybag:', report) if Rails.env.production?
30 | puts report
31 | end
32 |
33 | end
34 |
--------------------------------------------------------------------------------
/lib/tasks/restore.rake:
--------------------------------------------------------------------------------
1 | namespace :db do
2 |
3 | task restore: [
4 | 'db:download:generate',
5 | 'db:download:latest',
6 | 'db:drop',
7 | 'db:create',
8 | 'db:download:load',
9 | 'db:download:clean',
10 | 'db:migrate'
11 | ]
12 |
13 | namespace :download do
14 | def db_dump_file
15 | "coderwall-production.dump"
16 | end
17 |
18 | desc 'Create a production database backup'
19 | task :generate do
20 | Bundler.with_clean_env do
21 | cmd = "heroku pg:backups capture DATABASE_URL --app coderwall-next"
22 | sh(cmd)
23 | end
24 | end
25 |
26 | desc 'Download latest database backup'
27 | task :latest do
28 | unless File.exists?(db_dump_file)
29 | Bundler.with_clean_env do
30 | sh("curl `heroku pg:backups public-url --app coderwall-next` -o #{db_dump_file}")
31 | end
32 | end
33 | end
34 |
35 | desc 'Load local database backup into dev'
36 | task load: :environment do
37 | raise 'local dump not found' unless File.exists?(db_dump_file)
38 | puts 'Loading Production database locally'
39 | `pg_restore --verbose --clean --no-acl --no-owner -h localhost -d coderwall-next_development #{db_dump_file}`
40 | end
41 |
42 | task :clean do
43 | `rm #{db_dump_file}`
44 | end
45 | end
46 |
47 | end
48 |
--------------------------------------------------------------------------------
/lib/tasks/spam.rake:
--------------------------------------------------------------------------------
1 | namespace :spam do
2 | task :sweep => :environment do
3 | since = 30.days.ago
4 |
5 | protips = Protip.where('created_at > ?', since).where(bad_content: false); nil
6 | good_protips = []
7 | protips.each do |p|
8 | flags = Spaminator.new.protip_flags(p)
9 | if flags.any? && p.hearts_count <= 1
10 | Rails.logger.debug "#{p.id} – #{p.title} – #{p.body[0..100].gsub("\n", '')}"
11 | Rails.logger.debug "#{flags.inspect}" if flags.any?
12 | Rails.logger.debug
13 |
14 | p.bad_content = true
15 | p.user.bad_user = true
16 | p.save
17 | else
18 | good_protips << "https://coderwall.com/p/#{p.public_id} – #{p.title}"
19 | end
20 | end; nil
21 |
22 | good_users = []
23 | users = User.where('created_at > ?', since).where(bad_user: false); nil
24 | users.each do |u|
25 | flags = Spaminator.new.user_flags(u)
26 | if flags.any?
27 | Rails.logger.debug "#{u.id} – #{u.username} – #{(u.about || '')[0..100].gsub("\n", '')}"
28 | Rails.logger.debug "#{flags.inspect}" if flags.any?
29 | Rails.logger.debug
30 |
31 | u.bad_user!
32 | else
33 | good_users << "https://coderwall.com/#{u.username}"
34 | end
35 | end; nil
36 |
37 | ["Good Users", good_users, "Good Protips", good_protips].flatten.each do |e|
38 | Rails.logger.debug e
39 | end
40 |
41 | Rails.logger.info("spam-sweep bad-users=#{users.size - good_users.size}/#{users.size} bad-protips=#{protips.size - good_protips.size}/#{protips.size}")
42 | end
43 | end
--------------------------------------------------------------------------------
/lib/tasks/tags.rake:
--------------------------------------------------------------------------------
1 | namespace :tags do
2 | desc "Replace tags with canonical usages"
3 | task :clean do
4 | replacements = {
5 | 'javascript' => [
6 | '#javascript',
7 | '.js',
8 | 'javascripts',
9 | ],
10 | 'nodejs' => [
11 | 'javascript. node.js',
12 | 'node js',
13 | 'node-js',
14 | 'node.js node',
15 | 'node.js',
16 | ],
17 | 'rails' => [
18 | 'ruby rails',
19 | 'ruby on rails',
20 | 'ruby in rails',
21 | 'ruby-on-rails',
22 | 'rubyonrails',
23 | ],
24 | }
25 |
26 | replacements.each do |canonical, tags|
27 | Protip.with_any_tagged(tags).each do |tip|
28 | clean = tip.tags.map{|t| tags.find {|wrong| t == wrong } ? canonical : t }.uniq
29 | tip.update_columns(tags: clean)
30 | end
31 | end
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/log/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/log/.keep
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "coderwall",
3 | "description": "Programming tips, tools, and projects from our developer community.",
4 | "engines": {
5 | "node": "6.4.0",
6 | "npm": "3.10.3"
7 | },
8 | "scripts": {
9 | "postinstall": "cd client && npm install",
10 | "test": "rake test && npm run lint && npm run test:client",
11 | "test:client": "(cd client && npm run test --silent)",
12 | "lint": "(cd client && npm run lint --silent)",
13 | "build:clean": "rm app/assets/webpack/*",
14 | "build:production": "(cd client && npm run build:production --silent)",
15 | "build:production:client": "(cd client && npm run build:production:client --silent)",
16 | "build:production:server": "(cd client && npm run build:production:server --silent)",
17 | "build:client": "(cd client && npm run build:client --silent)",
18 | "build:server": "(cd client && npm run build:server --silent)",
19 | "build:dev:client": "(cd client && npm run build:dev:client --silent)",
20 | "build:dev:server": "(cd client && npm run build:dev:server --silent)",
21 | "hot-assets": "(cd client && npm run hot-assets)",
22 | "start": "(cd client && npm run start --silent)"
23 | },
24 | "repository": {
25 | "type": "git",
26 | "url": "https://github.com/coderwall/coderwall-next"
27 | },
28 | "author": "mdeiters",
29 | "license": "AGPL-3.0",
30 | "bugs": {
31 | "url": "https://github.com/coderwall/coderwall-next/issues"
32 | },
33 | "homepage": "https://coderwall.com",
34 | "cacheDirectories": [
35 | "node_modules",
36 | "client/node_modules"
37 | ]
38 | }
39 |
--------------------------------------------------------------------------------
/public/apple-touch-icon-152x152-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/public/apple-touch-icon-152x152-precomposed.png
--------------------------------------------------------------------------------
/public/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/public/apple-touch-icon-precomposed.png
--------------------------------------------------------------------------------
/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/fav128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/public/fav128x128.png
--------------------------------------------------------------------------------
/public/fav32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/public/fav32x32.png
--------------------------------------------------------------------------------
/public/fav64x64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/public/fav64x64.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/public/favicon.ico
--------------------------------------------------------------------------------
/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/public/favicon.png
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2 | #
3 | # To ban all spiders from the entire site uncomment the next two lines:
4 | # User-agent: *
5 | # Disallow: /
6 |
--------------------------------------------------------------------------------
/public/touch-icon-ipad-retina.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/public/touch-icon-ipad-retina.png
--------------------------------------------------------------------------------
/public/touch-icon-ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/public/touch-icon-ipad.png
--------------------------------------------------------------------------------
/public/touch-icon-iphone-retina.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/public/touch-icon-iphone-retina.png
--------------------------------------------------------------------------------
/public/touch-icon-iphone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/public/touch-icon-iphone.png
--------------------------------------------------------------------------------
/test/controllers/protips_controller_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class ProtipsControllerTest < ActionController::TestCase
4 | test "show signed in" do
5 | protip = create(:protip)
6 | sign_in
7 | get :show, params: { id: protip.public_id, slug: protip.slug }
8 | assert_response :success
9 | end
10 |
11 | test "show signed out" do
12 | protip = create(:protip)
13 | get :show, params: { id: protip.public_id, slug: protip.slug }
14 | assert_response :success
15 | end
16 |
17 | test "create protip" do
18 | sign_in
19 | post :create, params: { protip: {editable_tags: %w[socker duby], body: 'Hey there', title: 'First!'} }
20 | assert_response :success
21 | end
22 |
23 | test "don't show bad content to signed out users" do
24 | create(:protip, bad_content: true)
25 | get :index
26 | assert_response :success
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/test/controllers/subscribers_controller_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class SubscribersControllerTest < ActionController::TestCase
4 | test "create" do
5 | subscriber = create(:user)
6 | protip = create(:protip)
7 | sign_in_as subscriber
8 |
9 | assert_difference ->{ protip.reload.subscribers.size }, 1 do
10 | post :create, params: { protip_id: protip.id, format: :json }
11 | end
12 |
13 | assert_includes assigns(:protip).subscribers, subscriber.id
14 | end
15 |
16 | test "destroy" do
17 | subscriber = create(:user)
18 | protip = create(:protip, subscribers: [subscriber.id])
19 |
20 | sign_in_as subscriber
21 |
22 | assert_difference ->{ protip.reload.subscribers.size }, -1 do
23 | delete :destroy, params: { protip_id: protip.id, format: :json }
24 | end
25 |
26 | assert_not_includes assigns(:protip).subscribers, subscriber.id
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/test/controllers/users_controller_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class UsersControllerTest < ActionController::TestCase
4 | test "profile" do
5 | user = create(:user)
6 |
7 | get :show, params: { username: user.username }
8 | assert_response :success
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/test/factories/comment.rb:
--------------------------------------------------------------------------------
1 | FactoryGirl.define do
2 | factory :comment do
3 | association :article, factory: :protip
4 | user
5 | body { Faker::Lorem.words }
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/test/factories/protip.rb:
--------------------------------------------------------------------------------
1 | FactoryGirl.define do
2 | factory :protip do
3 | user
4 | title { Faker::Lorem.words }
5 | body { Faker::Lorem.paragraphs }
6 | tags { (1..5).map{|i| Faker::Lorem.word } }
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/test/factories/user.rb:
--------------------------------------------------------------------------------
1 | FactoryGirl.define do
2 | factory :user do
3 | sequence(:username) {|i| "user_#{i}" }
4 | email { Faker::Internet.email }
5 | password { Faker::Internet.password }
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/test/fixtures/jobs.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 |
3 | # This model initially had no columns defined. If you add columns to the
4 | # model remove the '{}' from the fixture names and add the columns immediately
5 | # below each fixture, per the syntax in the comments below
6 | #
7 | one: {}
8 | # column: value
9 | #
10 | two: {}
11 | # column: value
12 |
--------------------------------------------------------------------------------
/test/helpers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/test/helpers/.keep
--------------------------------------------------------------------------------
/test/integration/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/test/integration/.keep
--------------------------------------------------------------------------------
/test/mailers/comment_mailer_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class CommentMailerTest < ActionMailer::TestCase
4 | test 'new comment' do
5 | user = create(:user)
6 | comment = create(:comment)
7 | article = comment.article
8 |
9 | email = CommentMailer.new_comment(user, comment)
10 |
11 | assert_emails 1 do
12 | email.deliver_now
13 | end
14 |
15 | assert_equal ["notifications@coderwall.com"], email.from
16 | assert_equal [user.email], email.to
17 | assert_equal "New Comment [Re: #{article.title}]", email.subject
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/test/models/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/test/models/.keep
--------------------------------------------------------------------------------
/test/models/badge_test.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path("../../test_helper", __FILE__)
2 |
3 | class BadgeTest < ActiveSupport::TestCase
4 | should belong_to(:user)
5 | end
6 |
--------------------------------------------------------------------------------
/test/models/job_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class JobTest < ActiveSupport::TestCase
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/test/models/user_test.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path("../../test_helper", __FILE__)
2 |
3 | class UserTest < ActiveSupport::TestCase
4 | should have_many(:likes)
5 | should have_many(:pictures)
6 | should have_many(:protips)
7 | should have_many(:comments)
8 | should have_many(:badges)
9 |
10 | should validate_presence_of(:username)
11 | should validate_presence_of(:email)
12 | end
13 |
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | ENV.delete('CAPTCHA_SECRET') # TODO: investigate this. Does rails test automatically pull in .env now??
2 |
3 | ENV['RAILS_ENV'] ||= 'test'
4 | require File.expand_path('../../config/environment', __FILE__)
5 | require 'rails/test_help'
6 | require "clearance/test_unit"
7 |
8 | class ActiveSupport::TestCase
9 | include FactoryGirl::Syntax::Methods
10 |
11 | setup do
12 | ReactOnRails::TestHelper.ensure_assets_compiled
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/update-ssl.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -e
3 | FILE=/tmp/coderwall-certs.txt
4 |
5 | extract_cert() {
6 | sed -n "/$1/,/END CERTIFICATE/p" $FILE | tail -n +2
7 | }
8 |
9 | heroku run rake letsencrypt_plugin > $FILE
10 | extract_cert coderwall.com-cert.pem > /tmp/coderwall.com-cert.pem
11 | extract_cert coderwall.com-key.pem > /tmp/coderwall.com-key.pem
12 | heroku certs:update /tmp/coderwall.com-cert.pem /tmp/coderwall.com-key.pem --confirm coderwall-next
13 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/vendor/assets/javascripts/.keep
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coderwall/coderwall-next/5488c293178f4b1c7f2b3600c27733f1b4ad4eaf/vendor/assets/stylesheets/.keep
--------------------------------------------------------------------------------