{"id":13818927,"url":"https://github.com/vorpus/acebook","last_synced_at":"2025-05-16T04:31:56.686Z","repository":{"id":72033461,"uuid":"75445082","full_name":"vorpus/acebook","owner":"vorpus","description":"Clone of a popular social network. Rails + PostgreSQL + JS + React!","archived":false,"fork":false,"pushed_at":"2017-12-18T01:46:17.000Z","size":30972,"stargazers_count":39,"open_issues_count":1,"forks_count":15,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-11-19T18:44:58.686Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://spbk.herokuapp.com","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vorpus.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-12-03T01:52:43.000Z","updated_at":"2024-08-21T05:18:49.000Z","dependencies_parsed_at":"2023-02-23T15:00:47.542Z","dependency_job_id":null,"html_url":"https://github.com/vorpus/acebook","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vorpus%2Facebook","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vorpus%2Facebook/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vorpus%2Facebook/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vorpus%2Facebook/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vorpus","download_url":"https://codeload.github.com/vorpus/acebook/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254469012,"owners_count":22076409,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-04T08:00:35.883Z","updated_at":"2025-05-16T04:31:51.674Z","avatar_url":"https://github.com/vorpus.png","language":"Ruby","funding_links":[],"categories":["Happy Exploring 🤘"],"sub_categories":[],"readme":"# Acebook\n\n![acebook](/docs/demo-pics/logo.png \"acebook\")\n\n[LIVE](https://spbk.herokuapp.com)\n\nAcebook is a clone of Facebook featuring profiles from professional poker players. The single page frontend is built using React.js/Redux; the backend is powered by Ruby on Rails sitting on a PostgreSQL database. Try it out using the 'Guest Acct' button or create a profile and join the conversation!\n\n\n![acebook](/docs/demo-pics/login.png)\n\n## Features\n\n### New user \u0026 logging in\n#### Validation\nLogin inputs are validated on both the front-end and back-end. Client side validations check for password length, unique email addresses, and more. Passwords are hashed using [BCrypt](https://en.wikipedia.org/wiki/Bcrypt) before being stored on the server. Plaintext passwords are never stored.\n\nServer side validation occures at both the model and the database. These redundancies are useful to ensure the integrity of data stored within the database and generally considered best practice.\n\n```ruby\n# Ruby - app/models/user.rb\nvalidates :firstname, :lastname, :birthday, :gender, :session_token, :password_digest, presence: true\nvalidates :email, presence: true, uniqueness: true\nvalidates :password, length: { minimum: 6 }, allow_nil: true\n\nafter_initialize :ensure_session_token\n```\n\n```ruby\n# Ruby - db/schema.rb\ncreate_table \"users\", force: :cascade do |t|\n  t.string   \"firstname\",               null: false\n  t.string   \"lastname\",                null: false\n  t.string   \"email\",                   null: false\n  t.string   \"password_digest\",         null: false\n  t.string   \"session_token\",           null: false\n  ## ...\n\n  t.index [\"email\"], name: \"index_users_on_email\", unique: true, using: :btree\nend\n\n```\n\n#### Authentication\nA session token is stored in both the `users` table and as a cookie on the user's machine. These tokens are compared to find the active user and retrieve the relevant information. On log out, the cookie is cleared and the session token on the database is reset.\n```ruby\n# Ruby - app/controllers/application_controller.rb\ndef current_user\n  @user ||= User.find_by(session_token: session[:session_token])\nend\n```\n\nOn the frontend, the user's session is stored in the React-Redux `store`. If a user refreshes the single page app, a bootstrapped `currentUser` is placed on the window to keep the user logged in.\n```\n# Ruby - app/views/static_pages/root.html.erb\n\u003c% if logged_in %\u003e\n  window.currentUser = \u003c%= render(\n    \"api/users/user.json.jbuilder\",\n    user: current_user\n  ).html_safe %\u003e\n\u003c% end %\u003e\n```\n\n### Simulated latency\nAn artificial delay was put on the server to simulate latency and demonstrate the loading states.\n```javascript\n// JavaScript - frontend/actions/post_actions.js\nexport const fetchPosts = (userId) =\u003e {\n  return (dispatch) =\u003e {\n    dispatch(requestPosts()); //adds a 'loading' status to state\n    return APIUtil.getPosts(userId).then(\n      (success) =\u003e dispatch(receivePosts(success)),\n      (err) =\u003e dispatch(receiveErrors(err))\n    ); //on receipt of results, 'loading' state is reverted\n  };\n};\n```\n![acebook](/docs/demo-pics/guest-login.gif)\n\n### News feed \u0026 infinite scroll\nA user's news feed is curated by only displaying posts belonging to that user's friends. These posts are fetched using several chained ActiveRecord queries. An includes method is chained to the ActiveRecord relationship to prevent an inefficient N+1 query. Pagination is implemented with the [Kaminari](https://github.com/amatsuda/kaminari) gem. jQuery events are used to create infinite scroll.\n\n```ruby\n# Ruby - app/controllers/api/posts_controller.rb\nPost.where(\"tagged_user = ? or (tagged_user IS NULL and author_id = ?)\", params[:user_id], params[:user_id])\n              .where(:author_id =\u003e Friend.active_friendships(current_user))\n              .order(\"created_at DESC\")\n              .page(params[:page]).per(3)\n              .includes(:author, :tagged, {likes: [:user]}, {comments: [:author]})\n```\n\n![acebook](/docs/demo-pics/infinite-scroll.gif)\n\n\n### Friend requests\nUsers are notified of friend requests in the header pane and can accept or reject the requests without leaving the page the user is currently browsing. Friendships are maintained with a join table that includes a status column - users are not allowed to view posts belonging to users with whom they are not yet friends. Creating and searching for a A:B 'friendship' uses ActiveRecord queries to ensure the commutative relationship B:A does not already exist.\n\n![acebook](/docs/demo-pics/friend-accept.gif)\n\n### Real-time search\nThe header searchbar listens for change events and performs searches as the user inputs their query. Searches are case-insensitive and conducted using ActiveRecord queries and regular expressions.\n\n![acebook](/docs/demo-pics/rts.gif)\n\n### Likes and comments\nComments and likes can be added and removed from posts with a single keystroke/click. Comments have an 'enter/return' listener to know when to submit the form. The like and comment tables are similar on the back-end: both belong to an author and a post.\n\n![acebook](/docs/demo-pics/like-comment.gif)\n\n### Post editing\nPosts can be edited and removed with an action pane available to the author of the post. The frontend accomplishes this during render by comparing the post author and the session user.\n\n![acebook](/docs/demo-pics/post-edit.gif)\n\n### Photo upload\nPhoto upload uses the [paperclip gem](https://github.com/thoughtbot/paperclip) to manage attachments. Uploaded pictures are stored in an [Amazon AWS S3](https://aws.amazon.com/) server.\n\n```javascript\n// javascript - frontend/components/userprofile.jsx\nuploadNewPhoto(e) {\n  var file = e.currentTarget.files[0];\n  var formData = new FormData();\n  formData.append(\"user[profilepic]\", file);\n  this.props.updateUser(formData);\n}\n```\n\n![acebook](/docs/demo-pics/post-photo-upload.gif)\n\nAttachments can be associated with both users (cover photo, profile picture) and posts (post attachment).\n\n![acebook](/docs/demo-pics/profile-pic-update.gif)\n\n### Responsive layout\nCSS is used to make the app accessible on devices with both and small displays.\n```css\n/*  CSS - app/assets/stylesheets/main-body.css  */\n@media only screen and (max-width: 1012px) {\n  /*  ...  */\n}\n```\n\n![acebook](/docs/demo-pics/responsive-size.gif)\n\n\n## Future features\n- [ ] 'Like' reactions - in addition to liking a post, users will be able to convey 'love', 'laugh', and 'sad' emotions\n- [ ] Messenger - real time chat between friends using websockets\n- [ ] Photo albums - to show off a user's latest vegas vacation\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvorpus%2Facebook","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvorpus%2Facebook","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvorpus%2Facebook/lists"}