{"id":1160,"date":"2015-08-05T08:18:14","date_gmt":"2015-08-05T08:18:14","guid":{"rendered":"http:\/\/www.honobono-life.info\/wpeng\/?p=1160"},"modified":"2015-08-05T08:18:14","modified_gmt":"2015-08-05T08:18:14","slug":"show-follower-stats-with-angularjs","status":"publish","type":"post","link":"http:\/\/www.honobono-life.info\/wpeng\/show-follower-stats-with-angularjs\/","title":{"rendered":"Ruby on Rails Tutorial show follower stats with AngularJS"},"content":{"rendered":"<p><strong>(1)Rails model, association, validation<\/strong><\/p>\r\n\r\n<p><strong>1)&quot;Relationship&quot; model<\/strong><\/p>\r\n\r\n<p>$ rails generate model Relationship follower_id:integer followed_id:integer<\/p>\r\n\r\n<p><strong>2)add indices for the relationships table<\/strong><\/p>\r\n\r\n<p>$ vi db\/migrate\/&#8230;._create_relationships.rb<\/p>\r\n\r\n<pre class=\"prettyprint\">\r\nclass CreateRelationships &lt; ActiveRecord::Migration\r\n  def change\r\n    create_table :relationships do |t|\r\n      t.integer :follower_id\r\n      t.integer :followed_id\r\n\r\n      t.timestamps\r\n    end\r\n    add_index :relationships, :follower_id\r\n    add_index :relationships, :followed_id\r\n    add_index :relationships, [:follower_id, :followed_id], unique: true\r\n  end\r\nend<\/pre>\r\n\r\n<p><strong>3)migrate<\/strong><\/p>\r\n\r\n<p>$ bundle exec rake db:migrate<\/p>\r\n\r\n<p><strong>4)validation<\/strong><\/p>\r\n\r\n<p>$ vi app\/models\/relationship.rb<\/p>\r\n\r\n<pre class=\"prettyprint\">\r\nvalidates :follower_id, presence: true\r\nvalidates :followed_id, presence: true<\/pre>\r\n\r\n<p><strong>(2)User\/followed_users associations<\/strong><\/p>\r\n\r\n<p><strong>1)&quot;relationship&quot; model<\/strong><\/p>\r\n\r\n<p>$ vi app\/models\/relationship.rb<\/p>\r\n\r\n<pre class=\"prettyprint\">\r\nbelongs_to :follower, class_name: &quot;User&quot;\r\nbelongs_to :followed, class_name: &quot;User&quot;<\/pre>\r\n\r\n<p><strong>2)&quot;user&quot; model<\/strong><\/p>\r\n\r\n<p>$ vi app\/models\/user.rb<\/p>\r\n\r\n<pre class=\"prettyprint\">\r\nhas_many :relationships, foreign_key: &quot;follower_id&quot;, dependent: :destroy\r\nhas_many :followed_users, through: :relationships, source: :followed<\/pre>\r\n\r\n<p><strong>3)Utility methods for following<\/strong><\/p>\r\n\r\n<p>$ vi app\/models\/user.rb<\/p>\r\n\r\n<pre class=\"prettyprint\">\r\n  def following?(other_user)\r\n    relationships.find_by(followed_id: other_user.id)\r\n  end\r\n\r\n  def follow!(other_user)\r\n    relationships.create!(followed_id: other_user.id)\r\n  end\r\n\r\n  def unfollow!(other_user)\r\n    relationships.find_by(followed_id: other_user.id).destroy\r\n  end<\/pre>\r\n\r\n<p><strong>(3)User\/followers associations<\/strong><\/p>\r\n\r\n<p><strong>1)&quot;relationship&quot; model<\/strong><\/p>\r\n\r\n<p>$ more app\/models\/relationship.rb<\/p>\r\n\r\n<pre class=\"prettyprint\">\r\nbelongs_to :follower, class_name: &quot;User&quot;\r\nbelongs_to :followed, class_name: &quot;User&quot;<\/pre>\r\n\r\n<p><strong>2)&quot;user&quot; model<\/strong><\/p>\r\n\r\n<p>$ vi app\/models\/user.rb<\/p>\r\n\r\n<pre class=\"prettyprint\">\r\n  has_many :reverse_relationships, foreign_key: &quot;followed_id&quot;,\r\n                                   class_name:  &quot;Relationship&quot;,\r\n                                   dependent:   :destroy\r\n  has_many :followers, through: :reverse_relationships, source: :follower<\/pre>\r\n\r\n<p><strong>(3)check by console<\/strong><\/p>\r\n\r\n<p><strong>1.user instance<\/strong><\/p>\r\n\r\n<p>&gt; user2=User.find_by(id: 2)<br \/>\r\n&gt; user6=User.find_by(id: 6)<\/p>\r\n\r\n<p><strong>2.&quot;following?&quot;<\/strong><\/p>\r\n\r\n<p>&gt; user2.following?(user6)<br \/>\r\n&nbsp;=&gt; nil<br \/>\r\n&gt; user6.following?(user2)<br \/>\r\n&nbsp;=&gt; nil<\/p>\r\n\r\n<p><strong>3.&quot;follow!&quot;<\/strong><\/p>\r\n\r\n<p>&gt; user2.follow!(user6)<br \/>\r\n&nbsp; &nbsp;(0.2ms) &nbsp;begin transaction<br \/>\r\n&nbsp; SQL (0.3ms) &nbsp;INSERT INTO &quot;relationships&quot; (&quot;created_at&quot;, &quot;followed_id&quot;, &quot;follower_id&quot;, &quot;updated_at&quot;) VALUES (?, ?, ?, ?) &nbsp;[[&quot;created_at&quot;, &quot;2015-07-24 08:17:13.716171&quot;], [&quot;followed_id&quot;, 6], [&quot;follower_id&quot;, 2], [&quot;updated_at&quot;, &quot;2015-07-24 08:17:13.716171&quot;]]<br \/>\r\n&nbsp; &nbsp;(3.4ms) &nbsp;commit transaction<br \/>\r\n&nbsp;=&gt; #&lt;Relationship id: 1, follower_id: 2, followed_id: 6, created_at: &quot;2015-07-24 08:17:13&quot;, updated_at: &quot;2015-07-24 08:17:13&quot;&gt;<\/p>\r\n\r\n<p>&gt; user6.follow!(user2)<\/p>\r\n\r\n<p><strong>4.&quot;relationships&quot;, &quot;reverse_relationships&quot; association<\/strong><\/p>\r\n\r\n<p>&gt; user2.relationships<br \/>\r\n&nbsp;=&gt; #&lt;ActiveRecord::Associations::CollectionProxy [#&lt;Relationship id: 1, follower_id: 2, followed_id: 6, created_at: &quot;2015-07-24 08:17:13&quot;, updated_at: &quot;2015-07-24 08:17:13&quot;&gt;]&gt;<\/p>\r\n\r\n<p>&gt; user2.reverse_relationships<br \/>\r\n&nbsp; Relationship Load (0.2ms) &nbsp;SELECT &quot;relationships&quot;.* FROM &quot;relationships&quot; &nbsp;WHERE &quot;relationships&quot;.&quot;followed_id&quot; = ? &nbsp;[[&quot;followed_id&quot;, 2]]<br \/>\r\n&nbsp;=&gt; #&lt;ActiveRecord::Associations::CollectionProxy [#&lt;Relationship id: 2, follower_id: 6, followed_id: 2, created_at: &quot;2015-07-24 08:17:54&quot;, updated_at: &quot;2015-07-24 08:17:54&quot;&gt;]&gt;<\/p>\r\n\r\n<p>&gt; Relationship.all<br \/>\r\n&nbsp; Relationship Load (0.2ms) &nbsp;SELECT &quot;relationships&quot;.* FROM &quot;relationships&quot;<br \/>\r\n&nbsp;=&gt; #&lt;ActiveRecord::Relation [#&lt;Relationship id: 1, follower_id: 2, followed_id: 6, created_at: &quot;2015-07-24 08:17:13&quot;, updated_at: &quot;2015-07-24 08:17:13&quot;&gt;, #&lt;Relationship id: 2, follower_id: 6, followed_id: 2, created_at: &quot;2015-07-24 08:17:54&quot;, updated_at: &quot;2015-07-24 08:17:54&quot;&gt;]&gt;<\/p>\r\n\r\n<p>&gt; user2.relationships.find_by(followed_id: user6)<br \/>\r\n&nbsp; Relationship Load (0.1ms) &nbsp;SELECT &nbsp;&quot;relationships&quot;.* FROM &quot;relationships&quot; &nbsp;WHERE &quot;relationships&quot;.&quot;follower_id&quot; = ? AND &quot;relationships&quot;.&quot;followed_id&quot; = 6 LIMIT 1 &nbsp;[[&quot;follower_id&quot;, 2]]<br \/>\r\n&nbsp;=&gt; #&lt;Relationship id: 1, follower_id: 2, followed_id: 6, created_at: &quot;2015-07-24 08:17:13&quot;, updated_at: &quot;2015-07-24 08:17:13&quot;&gt;<\/p>\r\n\r\n<p><strong>5.&quot;followed_users&quot;, &quot;followers&quot; association<\/strong><\/p>\r\n\r\n<p>&gt; user2.followed_users.count<br \/>\r\n&nbsp;=&gt; 1<br \/>\r\n&gt; user2.followers.count<br \/>\r\n&nbsp;=&gt; 1<br \/>\r\n&gt; user6.followed_users.count<br \/>\r\n&nbsp;=&gt; 1<br \/>\r\n&gt; user6.followers.count<br \/>\r\n&nbsp;=&gt; 1<br \/>\r\n&gt; user2.followers.all<\/p>\r\n\r\n<p>&gt; user2.followed_users.all<\/p>\r\n\r\n<p><strong>(4)show following and followers<\/strong><\/p>\r\n\r\n<p><strong>1)Rails controller<\/strong><\/p>\r\n\r\n<p>$ vi app\/controllers\/sessions_controller.rb<\/p>\r\n\r\n<pre class=\"prettyprint\">\r\n  def create\r\n    user = User.find_by(email: session_params[:email].downcase)\r\n    if user &amp;&amp; user.authenticate(session_params[:password])\r\n      remember_token = User.new_remember_token\r\n      cookies.permanent[:remember_token] = remember_token\r\n      user.update_attribute(:remember_token, User.encrypt(remember_token))\r\n      gravatar_id = Digest::MD5::hexdigest(user.email.downcase)\r\n      microposts = user.microposts\r\n      @user_info = {\r\n        user: user,\r\n        gravatar_url: &quot;https:\/\/secure.gravatar.com\/avatar\/#{gravatar_id}&quot;,\r\n        microposts: microposts,\r\n        feed: user.feed,\r\n        followed_users: user.followed_users,\r\n        followers:  user.followers\r\n      }\r\n      render json: @user_info, status: :accepted, location: user\r\n    else\r\n......\r\n  def current_user\r\n    remember_token = User.encrypt(cookies[:remember_token])\r\n    current_user ||= User.find_by(remember_token: remember_token)\r\n    if current_user\r\n      gravatar_id = Digest::MD5::hexdigest(current_user.email.downcase)\r\n      microposts = current_user.microposts\r\n      @user_info = {\r\n        user: current_user,\r\n        gravatar_url: &quot;https:\/\/secure.gravatar.com\/avatar\/#{gravatar_id}&quot;,\r\n        microposts: microposts,\r\n        feed: current_user.feed,\r\n        followed_users: current_user.followed_users,\r\n        followers:  current_user.followers\r\n      }\r\n      render json: @user_info, status: :accepted\r\n    else\r\n      head :no_content\r\n    end\r\n  end<\/pre>\r\n\r\n<p><strong>2)AngularJS template view<\/strong><\/p>\r\n\r\n<p>$ vi app\/assets\/templates\/static_pages\/home.html.erb<\/p>\r\n\r\n<div ng-non-bindable><pre class=\"prettyprint\">\r\n&lt;div ng-controller=&quot;HomeCtrl&quot;&gt;\r\n  &lt;div ng-show=&quot;chkSignin().user.id &gt; 0&quot; class=&quot;row&quot;&gt;\r\n......\r\n    &lt;div class=&quot;col-sm-4&quot;&gt;\r\n.........\r\n      &lt;div class=&quot;stats&quot;&gt;\r\n        &lt;a href=&quot;#&quot;&gt;\r\n          &lt;strong id=&quot;following&quot; class=&quot;stat&quot;&gt;\r\n            {{chkSignin().followed_users.length}}\r\n          &lt;\/strong&gt;\r\n          following\r\n        &lt;\/a&gt;\r\n        &lt;a href=&quot;#&quot;&gt;\r\n          &lt;strong id=&quot;followers&quot; class=&quot;stat&quot;&gt;\r\n            {{chkSignin().followers.length}}\r\n          &lt;\/strong&gt;\r\n          followers\r\n        &lt;\/a&gt;\r\n      &lt;\/div&gt;<\/pre><\/div>\r\n\r\n<p><strong>3)css<\/strong><\/p>\r\n\r\n<p>$ vi app\/assets\/stylesheets\/custom.css.scss<\/p>\r\n\r\n<pre class=\"prettyprint\">\r\n.stats {\r\n  overflow: auto;\r\n  border-top: 1px solid $grayLighter;\r\n  a {\r\n    float: left;\r\n    padding: 0 10px;\r\n    border-left: 1px solid $grayLighter;\r\n    color: gray;\r\n    &amp;:first-child {\r\n      padding-left: 0;\r\n      border: 0;\r\n    }\r\n    &amp;:hover {\r\n      text-decoration: none;\r\n      color: blue;\r\n    }\r\n  }\r\n  strong {\r\n    display: block;\r\n  }\r\n}\r\n<\/pre>","protected":false},"excerpt":{"rendered":"<p>(1)Rails model, association, validation 1)&quot;Relationship&quot; model $ rails generate model Relationship follower_id:integer followed_id:integer 2)add indices for the relationships table $ vi db\/migrate\/&#8230;._create_relationships.rb class CreateRelationships &lt; ActiveRecord::Migration def change create_table :relationships do |t| t.integer :follower_id t.integer :followed_id t.timestamps end add_index :relationships, :follower_id add_index :relationships, :followed_id add_index :relationships, [:follower_id, :followed_id], unique: true end end 3)migrate $ bundle [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[137],"tags":[35,184,183],"amp_enabled":true,"_links":{"self":[{"href":"http:\/\/www.honobono-life.info\/wpeng\/wp-json\/wp\/v2\/posts\/1160"}],"collection":[{"href":"http:\/\/www.honobono-life.info\/wpeng\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.honobono-life.info\/wpeng\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.honobono-life.info\/wpeng\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.honobono-life.info\/wpeng\/wp-json\/wp\/v2\/comments?post=1160"}],"version-history":[{"count":1,"href":"http:\/\/www.honobono-life.info\/wpeng\/wp-json\/wp\/v2\/posts\/1160\/revisions"}],"predecessor-version":[{"id":1161,"href":"http:\/\/www.honobono-life.info\/wpeng\/wp-json\/wp\/v2\/posts\/1160\/revisions\/1161"}],"wp:attachment":[{"href":"http:\/\/www.honobono-life.info\/wpeng\/wp-json\/wp\/v2\/media?parent=1160"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.honobono-life.info\/wpeng\/wp-json\/wp\/v2\/categories?post=1160"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.honobono-life.info\/wpeng\/wp-json\/wp\/v2\/tags?post=1160"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}