From cb8a2714dede609bc4dfff934092405f5f53c377 Mon Sep 17 00:00:00 2001 From: Islam Wazery Date: Thu, 14 Aug 2014 21:40:05 +0300 Subject: [PATCH] Added rating functionality to the online store application --- Gemfile | 14 +- Gemfile.lock | 59 ++- app/assets/images/another/star_on2.png | Bin 0 -> 3291 bytes app/assets/images/big-star.png | Bin 0 -> 8089 bytes app/assets/images/cancel-off.png | Bin 0 -> 699 bytes app/assets/images/cancel-on.png | Bin 0 -> 715 bytes app/assets/images/mid-star.png | Bin 0 -> 6335 bytes app/assets/images/star-half.png | Bin 0 -> 667 bytes app/assets/images/star-off.png | Bin 0 -> 685 bytes app/assets/images/star-on.png | Bin 0 -> 631 bytes app/assets/javascripts/jquery.raty.js | 464 ++++++++++++++++++ app/assets/javascripts/letsrate.js.erb | 50 ++ app/assets/javascripts/ratyrate.js.erb | 62 +++ app/controllers/rater_controller.rb | 13 + app/models/average_cache.rb | 4 + app/models/movie.rb | 2 + app/models/overall_average.rb | 4 + app/models/rate.rb | 7 + app/models/rating_cache.rb | 3 + app/models/user.rb | 2 + app/views/movies/show.html.erb | 22 + bin/rails | 4 - bin/rake | 4 - bin/spring | 18 - config/database.yml | 59 +-- config/routes.rb | 1 + db/migrate/20140706144643_create_rates.rb | 20 + .../20140814135421_create_rating_caches.rb | 19 + .../20140814135422_create_average_caches.rb | 17 + .../20140814135423_create_overall_averages.rb | 16 + db/schema.rb | 48 +- 31 files changed, 822 insertions(+), 90 deletions(-) create mode 100644 app/assets/images/another/star_on2.png create mode 100644 app/assets/images/big-star.png create mode 100644 app/assets/images/cancel-off.png create mode 100644 app/assets/images/cancel-on.png create mode 100644 app/assets/images/mid-star.png create mode 100644 app/assets/images/star-half.png create mode 100644 app/assets/images/star-off.png create mode 100644 app/assets/images/star-on.png create mode 100644 app/assets/javascripts/jquery.raty.js create mode 100644 app/assets/javascripts/letsrate.js.erb create mode 100644 app/assets/javascripts/ratyrate.js.erb create mode 100644 app/controllers/rater_controller.rb create mode 100644 app/models/average_cache.rb create mode 100644 app/models/overall_average.rb create mode 100644 app/models/rate.rb create mode 100644 app/models/rating_cache.rb delete mode 100755 bin/spring create mode 100644 db/migrate/20140706144643_create_rates.rb create mode 100644 db/migrate/20140814135421_create_rating_caches.rb create mode 100644 db/migrate/20140814135422_create_average_caches.rb create mode 100644 db/migrate/20140814135423_create_overall_averages.rb diff --git a/Gemfile b/Gemfile index cba6eed..3aa2aeb 100644 --- a/Gemfile +++ b/Gemfile @@ -1,10 +1,9 @@ -source 'https://rubygems.org' - +source 'http://rubygems.org' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '4.1.1' # Use mysql as the database for Active Record -gem 'mysql2' +gem 'sqlite3' # Use SCSS for stylesheets gem 'sass-rails', '~> 4.0.3' # Use Uglifier as compressor for JavaScript assets @@ -26,6 +25,15 @@ gem 'devise' # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring', group: :development +# Add rating functionality +gem 'ratyrate', :github => 'wazery/ratyrate' +# gem 'ratyrate', :path => '~/Sources/ratyrate' +# gem 'ratyrate' +gem 'quiet_assets', group: :development +gem "better_errors" +gem 'pry-rails', :group => :development +gem 'pry-stack_explorer', '~> 0.4.9.1' + # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' diff --git a/Gemfile.lock b/Gemfile.lock index db0caf6..95e0d32 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,5 +1,11 @@ +GIT + remote: git://github.com/wazery/ratyrate.git + revision: ffadfe2cfce144823eae6378a5a9badc5ff955c4 + specs: + ratyrate (1.2.0.alpha) + GEM - remote: https://rubygems.org/ + remote: http://rubygems.org/ specs: actionmailer (4.1.1) actionpack (= 4.1.1) @@ -29,14 +35,21 @@ GEM tzinfo (~> 1.1) arel (5.0.1.20140414130214) bcrypt (3.1.7) + better_errors (1.1.0) + coderay (>= 1.0.0) + erubis (>= 2.6.6) + binding_of_caller (0.7.2) + debug_inspector (>= 0.0.1) builder (3.2.2) + coderay (1.1.0) coffee-rails (4.0.1) coffee-script (>= 2.2.0) railties (>= 4.0.0, < 5.0) - coffee-script (2.2.0) + coffee-script (2.3.0) coffee-script-source execjs - coffee-script-source (1.7.0) + coffee-script-source (1.7.1) + debug_inspector (0.0.2) devise (3.2.4) bcrypt (~> 3.0) orm_adapter (~> 0.1) @@ -45,12 +58,12 @@ GEM warden (~> 1.2.3) erubis (2.7.0) execjs (2.2.1) - foundation-rails (5.3.0.1) + foundation-rails (5.3.3.0) railties (>= 3.1.0) sass (>= 3.2.0) hike (1.2.3) - i18n (0.6.9) - jbuilder (2.1.1) + i18n (0.6.11) + jbuilder (2.1.3) activesupport (>= 3.0.0, < 5) multi_json (~> 1.2) jquery-rails (3.1.1) @@ -60,12 +73,23 @@ GEM mail (2.5.4) mime-types (~> 1.16) treetop (~> 1.4.8) + method_source (0.8.2) mime-types (1.25.1) - minitest (5.3.5) + minitest (5.4.0) multi_json (1.10.1) - mysql2 (0.3.16) orm_adapter (0.5.0) polyglot (0.3.5) + pry (0.10.0) + coderay (~> 1.1.0) + method_source (~> 0.8.1) + slop (~> 3.4) + pry-rails (0.3.2) + pry (>= 0.9.10) + pry-stack_explorer (0.4.9.1) + binding_of_caller (>= 0.7) + pry (>= 0.9.11) + quiet_assets (1.0.3) + railties (>= 3.1, < 5.0) rack (1.5.2) rack-test (0.6.2) rack (>= 1.0) @@ -93,9 +117,10 @@ GEM sass (~> 3.2.0) sprockets (~> 2.8, <= 2.11.0) sprockets-rails (~> 2.0) - sdoc (0.4.0) - json (~> 1.8) - rdoc (~> 4.0, < 5.0) + sdoc (0.4.1) + json (~> 1.7, >= 1.7.7) + rdoc (~> 4.0) + slop (3.6.0) spring (1.1.3) sprockets (2.11.0) hike (~> 1.2) @@ -106,6 +131,7 @@ GEM actionpack (>= 3.0) activesupport (>= 3.0) sprockets (~> 2.8) + sqlite3 (1.3.9) thor (0.19.1) thread_safe (0.3.4) tilt (1.4.1) @@ -114,9 +140,9 @@ GEM polyglot (>= 0.3.1) turbolinks (2.2.2) coffee-rails - tzinfo (1.2.1) + tzinfo (1.2.2) thread_safe (~> 0.1) - uglifier (2.5.1) + uglifier (2.5.3) execjs (>= 0.3.0) json (>= 1.8.0) warden (1.2.3) @@ -126,15 +152,20 @@ PLATFORMS ruby DEPENDENCIES + better_errors coffee-rails (~> 4.0.0) devise foundation-rails jbuilder (~> 2.0) jquery-rails - mysql2 + pry-rails + pry-stack_explorer (~> 0.4.9.1) + quiet_assets rails (= 4.1.1) + ratyrate! sass-rails (~> 4.0.3) sdoc (~> 0.4.0) spring + sqlite3 turbolinks uglifier (>= 1.3.0) diff --git a/app/assets/images/another/star_on2.png b/app/assets/images/another/star_on2.png new file mode 100644 index 0000000000000000000000000000000000000000..b2477679d575c28fdc5d20fbc60c2439e6ab8e88 GIT binary patch literal 3291 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 literal 0 HcmV?d00001 diff --git a/app/assets/images/big-star.png b/app/assets/images/big-star.png new file mode 100644 index 0000000000000000000000000000000000000000..9304638ad3b533ca1ea2417d3e013808f61c0de1 GIT binary patch literal 8089 zcmV;KA7KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000!kNkl$(Hray!Dmh@1c}(_60o&r8d!8(`0h^kn$&H4o ze)aZ89Nn%(dqwZ(Ly_| zPpq~GteYDSV2(LrpLgo?lWXntlh~Oj(dq4Lfp-;JX5QIJ0eD$nc`(nC)n={L1-#sF zzr6WR2i$1v{W|kBT6-Fuc^aL02Ca=;1gvU`CZxoQn{;^jrFYrl+!CwJg7uT(0p6>R z-1h_2cdXF*FdB@Y!Du|{!+ofJs0C`%-z`Ph9gwzVA<4MriH zM57uS)ghdQa183h6>M->vG;~DuRo^^T<6AXjz6U;yO)RqOm-)xFv!sMH2>70@!)}^ZL7wcwxsm zS|3L1;}Fioa)5%`23S=a|E*E{o^Twkk3xO)5}>Q~`z7Z9q#&x;!{GWGt}mM~q)se5 z&2>I``LVC}?C2`OF^HxzPRzFEXw`xSsI8&l6dI19^^v`R_q00GGX34dWuYLhbqewa z&WXpYTm8cI@&(faU(z)<--w+!$e1h>FyOThx> z@(cgyElxPxNjQc^Q%Ou4wMk?r&dr9X4PxsJ#?jhv4>tO6x%E;IqU}L(ZUUBq1b7~- z3pnTf)yDPf2kNIt;>5((z=lX0u-XXAV!+sGG?;{71g$-D4sdX@loa^_*|Qv43E0x; z05I=9WB+p{*x?Y2A<-0Q9T7`$y(?cEi={zr9imCJK8n_MxYS24Pv1MMEbY>gOSieZ zsHylHuAgfbRs`0$>ivgp8tNW9T5H2-G!a{E5_%P$vnD`Anzh%4aZDUeKv2QXxTi&t zJ+ku!pSTC0{+kbK@uBOITdunhkd((d(k?Nx&zkP)d*AkwLpB|<`b9(ia!6lC?T{{S z>>z4Sq4i-TnnpDMEJk!Tdst|>ah_PP2_u8rIucHyVn7u~!*A9eeDi(k^=^wIdw*o@ zwg(=5`k|Md^0_buevvMR=E_cjERD|3JpBb-=biDQ7Y(fJIJB!OFRr@bM&Ahz7Ohn@ zoJPYbqF@5T32c1=!YQn+fejF~h{O<0GLe#|Vx(z2ia4|T!~@4eOcmTt#O(&R3voNa zbRupQj6b8M@_>rJ4ddPyS>5*3Gm}5t|K*!!C>QC{BEUkzrJVsO*I#-}@9{^j-MFI1 zKeXc7jh?A*awRwjTXTtOU~6FOXjG52(bmwgjGjNNCGp4EGcZ0 z1RuslOa+_@m`d{82je5g1LK2uh?DNGgev~SYW!^&?|!Sgb$p`!lc7!5jkN>55V(fo za%$7Qn?8Qxz@|f1Z|dpxHhB_l@=blCk@^8*>w>LesDrH|8pDbogE(Z;_z?}0zeB8z z;;F2Sk|VZFnKaW##ima(a2r-E319)^Ca=37PHfrYg7H8cFdiaq{JsQq4BU$?-}v#5 z;`l1!KY~-e&x(7$Ro&_uxz9h~s;74buG^Z2jbUI6sfG-7w7>lTZlue(%Li7=pFd_4JRFS#HOLo z>&#OXX11fX9T1Hpwuabx;^9+}v8OjEB8tg)eVIpCDFqf>ocPXLabn*?Lo0%t01BIqx|D zx0jIIbXc{-ipwn7<{&3uXdJ|;#Bs9Uk5mTU!$iw%qdI z>EbxM4Da+z2!98%H@c z9Y%j~-kpCpJkxg{{%X|i!hoeR|}+}ag`Uz>U;qgWX_7A&$3mVIKSZZAbvkm z86fnA?!NfyAD;2u|NdQ2pgB7QH*2*4P|u7Y13k)EvjRNv)MV`~7k%$pr`6GJl@C>d^9lgB<9f zL@IdTmpdljdFj0$n2J_Crrs)WR}e`Dh>v2hm?pq^OU=r|t}XwNf{-I$J2Np@<0n$k z7lZW&AKvnv|2h4w@4I6-e}(R<nhsQa z6bHr3TB|LdP%O?W*`lg$s;^nF-3_q#gOA_6ZS>Dyd+u#d=2);8noIMXkf^|NizK26 z@PB;wv8OKk^uzBBoz)|;1^1#(2bxTo6%)%qfu*Xp31@c(a7$Bk97)bv>OpiA`$gA64AJX{%y;z{_wFc{^q^bTRn;24~c_Qaav|) zMWNVE0WIyt&AnzkaH@#ggH%>QWoYcd--IWB z{NSTE-n#uF>-N>u>j9?Tm$k(NP^#97F`otkF#^Hf5mN zoUEBoM?vF%^8Z{Y6KRRZ!*Q$oEC<-R`W7?;n1W=E$yY7%T&h)X4YgQnC2N^daJl5; z+<8^%rD1BQ?T^?7!4t=hEDw+IAkYmN|tr1D$KsPR4x;8QCuKKj}WzR=v$z zZub3>nZFy-{xO-YL}Z)xNSjbX97oo%4BO6hIqG~;RQyC2*+(oyl&(`nX-#$7kD=Gr;I7KN`$ zadId9AH!_514G=5B9bYM6Z>78tg(n&tk&(WaLXF^njm*p7);xkq-18G^OS_L*_kS{ z%gPeKV#vrt3|LhWwWtjc4MFSc;Cryh>jG}P-#)Tx9?6P{w` zIGGfxn0UIX$=pOuob*A78~8NDYJ`YIHA1XnObi(l>o7I}EH)Nn8+QYtA)*1;pa*mn z$d26!?p$C6ke45}*BVJeU~O2gHc$JDa0=*4vyG*(|#~)9nkeCTXzSz zGKcOqy|&k2=MXwc+D_VRvCS0%F=-XH8n@TG-Qf0M+#c$5j{W@6`d2T$cH7q!<THYHs88 z{$6hl8WD$L@mxa^Jr#-btHyD_RKTkuP8Gav#Op!4UTWI&)XyFbZ+_=TzI*F09v!Zw z-*LiOcigyP{fe8f{NRx%Z+u?QYb)-=0O2?qjU&+{*2b2M)QG3v5t8%;i$+N}7Nqg+ z`ndeFdqoR#I<($SStEo=K(Cn?$w8c?tnF41uM2Uz5Wg4kdI_ZGv2DMY`TSeo_su(= zcxp1F;6`qb{C0Huv~#}kmDMXdzJASzjy(Ql8~1)gmp{3$2IELH4t5H)Q>e~hNk9_e zrb}AmXaSy#D-KzBM%+Dy@BLExF@o{&%{5=6mkEz2ZA}eC#7fAN9(^_kLq<*VI88j3Ra- zzO4Y$sGUX$uo4m5X>!LATVIb>*^6bf*A$Ryl_8l{G-+@XCtg8gycB9L;;&#j z>bm!b+s6Lsb?^A{-OSMdSO8i8Q>#aM?#18!4sh3XA3gf86aRScGgfrW9H!wI*s*wZ zrB0z*N5v3lmLpWdC69I8Mb=w=>Bhk>*D+n{c!=A9I1XYAj@Jc#FUDV?=l!_o&j8{e7717vCb<37-{ozv=UUu6fasEc zy!VTDeCM%IihF)bdw*$Nq@BMKB}g6nVCeECvY z?tI|3Tk+%OtVStrqRv}t8Cco?E{Y)XfV0{gmM;Wu{w6?P z#%jr(R8avlU&x%RlQ7eC&~m#0(&cYDZCB;?vu~e+^?+^_nJiom;TN`EqXo5LC%BDhlxc$ z@Um_0PJ)HjHW$soEYsA5o?zJxZzKcPWTK0rW3tZ%V0VEhm}% literal 0 HcmV?d00001 diff --git a/app/assets/images/cancel-off.png b/app/assets/images/cancel-off.png new file mode 100644 index 0000000000000000000000000000000000000000..a3031f055375716b848c29191cabf6253b936379 GIT binary patch literal 699 zcmV;s0!00ZP)LlQBzMVHAd+8!Cx7n4ko2BpKR*l8~R!U*RTd z-KC3@i{RwscE?Or>7D!)-4qlQMGUEfL=(Km2!<>7e((2nscB0J9em*|hwptkhx2@% zbB?#@9f0R~!$uf}AB-`J&bbvyGuB!pb*#0!lD@62t+ig?I_L15bHjiz3>S+9fx3`mlMX0u7HR>SkWlB6Yo%&%RRecz|k z>G1UQM7!Ol*X!~4`1n@=JUl#5tyYn=JiLOW*=#mTx7)=S!|LklKTpX-R`T3bfqw!C(=Xn$g1v;G$#+deS0%Ocx zv)QCjD3GQpV`F0@W@{|~#bS};<71q2dw;I5Z>_z(xVT_`ejb2KCc`T%)><-|4E1`Q z!^1=3IKGv%5BvscXJ=4|#1P83Dl-```crC2O7H8sW2(GjgyYar=!5CjJ! zC~t3XFGvaizgDY}&*uS%qKK1|6XH0&lk_DBf>D%TwY9bNLDHfzX2m%-BMD01y4qo3(5`U!bUg@nP|I3oZ6002ovPDHLkV1f)gL$Ck< literal 0 HcmV?d00001 diff --git a/app/assets/images/cancel-on.png b/app/assets/images/cancel-on.png new file mode 100644 index 0000000000000000000000000000000000000000..08f249365afd29594b51210c6e21ba253897505d GIT binary patch literal 715 zcmV;+0yO=JP)C4}Mrzlg<+1Y8PEBfUp0jJpx4B>@E+cy3`^(Gw`Mf+2&yxZm<$to~Vpgvg&QKNR z_f#1(r6svZt%iF?s+n<8X?B&!h3g9Dbb8_=MX}!;HiQSAh`bp^WMl~Z-44teO7W_Y zV4thSL{h;rJY7!l3%5J4H1!tIzB`Dv+YxO(haWeausGZYkI8^hWj6mzo=L0{%;yxzh{5!Htr?51 zvG|W62MzC8BZ76hRpCyO2zOn<%e)K>NHge!-~)Ap33OdWw6hsLYbCxGNt0%wk_2z7 zfyYvXheSG)5HRK1VB~%mq7Dmurw#bi@hEcOr3&G1ZiF*$M=&9nB#VNf&Q^r$4G5kp zTURh&s)E0%5&hyVD}sp<72~zmAY`Y(9aqO6CXF%=zFHGzO-A&I(pE}v70YQxCPJ{Y z4L+?5-crdLn3ZRPEs!A4ehEY3ZRpL~w9>@aMN+{F4dI@v&>(QDHQum!mG~E^$OS8l z!7?%Uwib*ROP67Hw`ika)gX-(8Ia`-u_IEhxG7U<13kSsMW+$lbb2dUMm5p6pa}cjgA+U$^mJ^AjD?&bdi)8~y+Q002ovPDHLkV1g8IMc@Dc literal 0 HcmV?d00001 diff --git a/app/assets/images/mid-star.png b/app/assets/images/mid-star.png new file mode 100644 index 0000000000000000000000000000000000000000..aebb3ccb5b6c851512227f11ad01c0c106a441b9 GIT binary patch literal 6335 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 BNg@CM literal 0 HcmV?d00001 diff --git a/app/assets/images/star-half.png b/app/assets/images/star-half.png new file mode 100644 index 0000000000000000000000000000000000000000..3c19e90a8a755e004424db205c8433f0f63048b4 GIT binary patch literal 667 zcmV;M0%ZM(P)5kxfVxVHn4M^Ulocu4}uCxq(JO)+Y9GD5Jat zbqNy5lR|3Ugd)g`R}H!Z-82#+xHAej^Uvb`)a1J$lTNk&_XNAE zBfTQ9v_v8Sa5IxmRs3@SYh=d-l+~gM@B>A4P0dc=P9~jd$fQ$sTUl-3B9(2Y;#Z9F ziv79~Bz4_Q3b9eCYwPF+d^xg&l#+bj;nT_rzOS%t8z2j`7`B7!=X2+jXB|TV=B8-| z5I_(F*tU&nN^1AkB4vn~=}AV%Mjv-|U3#ej40jVdS&P?6I*kPtxhy74l2*jJNmZ458vW9PWLT-hGu|=LysO@uBuA(2q79M zrYCoMu8-X7>n;ia!^1;Yo0`tX3%(WvK`H*P`T^?|->M&U?OXr=002ovPDHLkV1gTF BCtCmj literal 0 HcmV?d00001 diff --git a/app/assets/images/star-off.png b/app/assets/images/star-off.png new file mode 100644 index 0000000000000000000000000000000000000000..956fa7c637cddb4db6a091556cc63a0f6a186264 GIT binary patch literal 685 zcmV;e0#f~nP)R5*>Dl0QrnVHn1Ly)Enp;##exZh7()Uk zR1!C3adKe*!lVgta&se2n3x!5BLe|s!Nt*~X-#y7B3eoUik5~%+lD(Tz0ZL^0YjVU zv%Pu#yl=keg9gZ?Q_Q(|Z0g_5!WIoz~V%6X6SCq;x5j%6#A6o&%l(m4W2o+DSkrok{@j zfS9fu_%t7u6-EyR*{?cCDbX|y$Ei}>*}-*d*tQMu1GuLQBnNZ({Kj-B6r%H5Cv9zQ z1OftQ-v&TRiEZ1cs+abw?R0hbV45b`)$HqmfpE80>NxE=6Ci}>F?0hV0tABwPH{}X=zm$k-d TM0>cp00000NkvXXu0mjfLk}+u0K@`S+@BV*_sWrqlwFo8}(kQkaqzD;A z=u{lE-2?}_II1YR)S)U)E*26HCl}q?MW=Ld6)l=U=ui@ZMFoe7jcsaT?tgaXnG9{0ZQ-FLi$F8Gk$pICVsN_6A+-I}LP#xf!zP`KEQ*A|86(eYX?Vrxqg`*m?> zyc17#YQ&Emm{YDS9%UIE2;KXq0p(YRuVBj4qCoke?8ek~ZfK?*SF$toUz&(q^LzVF zc)m+{o=z$CtKX9fo)4nH88Gx=Z0LtIJw+2pF{o}Aa&I@D_|-bv)_zWzpnQfNU|%D| z2ONlib6`yct1JCRlQx>P4J2Q!lkr}!P)`rD5RWn;!ch^BrauSPHXPUh?BDz@Ut;od zmQ4>}ahc!a2^JEAoD+yQ#m?=B%^vZad=f}S8X&Tqm1m)@;e`m!IcA(C>>M0F#2E0tt%Gn#jhcX3%^aLBZN-xDTU?LbBKJ1?Kz|Loh!06cE>#5u?R;17H*+`wdH ROO^lt002ovPDHLkV1mP{6ITEL literal 0 HcmV?d00001 diff --git a/app/assets/javascripts/jquery.raty.js b/app/assets/javascripts/jquery.raty.js new file mode 100644 index 0000000..26f53bf --- /dev/null +++ b/app/assets/javascripts/jquery.raty.js @@ -0,0 +1,464 @@ +/*! + * jQuery Raty - A Star Rating Plugin - http://wbotelhos.com/raty + * ------------------------------------------------------------------- + * + * jQuery Raty is a plugin that generates a customizable star rating. + * + * Licensed under The MIT License + * + * @version 2.4.5 + * @since 2010.06.11 + * @author Washington Botelho + * @documentation wbotelhos.com/raty + * @twitter twitter.com/wbotelhos + * + * Usage: + * ------------------------------------------------------------------- + * $('#star').raty(); + * + *
+ * + */ + +;(function($) { + + var methods = { + init: function(settings) { + return this.each(function() { + var self = this, + $this = $(self).empty(); + + self.opt = $.extend(true, {}, $.fn.raty.defaults, settings); + + $this.data('settings', self.opt); + + if (typeof self.opt.number == 'function') { + self.opt.number = self.opt.number.call(self); + } else { + self.opt.number = methods.between(self.opt.number, 0, 20) + } + + if (self.opt.path.substring(self.opt.path.length - 1, self.opt.path.length) != '/') { + self.opt.path += '/'; + } + + if (typeof self.opt.score == 'function') { + self.opt.score = self.opt.score.call(self); + } + + if (self.opt.score) { + self.opt.score = methods.between(self.opt.score, 0, self.opt.number); + } + + for (var i = 1; i <= self.opt.number; i++) { + $('', { + src : self.opt.path + ((!self.opt.score || self.opt.score < i) ? self.opt.starOff : self.opt.starOn), + alt : i, + title : (i <= self.opt.hints.length && self.opt.hints[i - 1] !== null) ? self.opt.hints[i - 1] : i + }).appendTo(self); + + if (self.opt.space) { + $this.append((i < self.opt.number) ? ' ' : ''); + } + } + + self.stars = $this.children('img:not(".raty-cancel")'); + self.score = $('', { type: 'hidden', name: self.opt.scoreName }).appendTo(self); + + if (self.opt.score && self.opt.score > 0) { + self.score.val(self.opt.score); + methods.roundStar.call(self, self.opt.score); + } + + if (self.opt.iconRange) { + methods.fill.call(self, self.opt.score); + } + + methods.setTarget.call(self, self.opt.score, self.opt.targetKeep); + + var space = self.opt.space ? 4 : 0, + width = self.opt.width || (self.opt.number * self.opt.size + self.opt.number * space); + + if (self.opt.cancel) { + self.cancel = $('', { src: self.opt.path + self.opt.cancelOff, alt: 'x', title: self.opt.cancelHint, 'class': 'raty-cancel' }); + + if (self.opt.cancelPlace == 'left') { + $this.prepend(' ').prepend(self.cancel); + } else { + $this.append(' ').append(self.cancel); + } + + width += (self.opt.size + space); + } + + if (self.opt.readOnly) { + methods.fixHint.call(self); + + if (self.cancel) { + self.cancel.hide(); + } + } else { + $this.css('cursor', 'pointer'); + + methods.bindAction.call(self); + } + + $this.css('width', width); + }); + }, between: function(value, min, max) { + return Math.min(Math.max(parseFloat(value), min), max); + }, bindAction: function() { + var self = this, + $this = $(self); + + $this.mouseleave(function() { + var score = self.score.val() || undefined; + + methods.initialize.call(self, score); + methods.setTarget.call(self, score, self.opt.targetKeep); + + if (self.opt.mouseover) { + self.opt.mouseover.call(self, score); + } + }); + + var action = self.opt.half ? 'mousemove' : 'mouseover'; + + if (self.opt.cancel) { + self.cancel.mouseenter(function() { + $(this).attr('src', self.opt.path + self.opt.cancelOn); + + self.stars.attr('src', self.opt.path + self.opt.starOff); + + methods.setTarget.call(self, null, true); + + if (self.opt.mouseover) { + self.opt.mouseover.call(self, null); + } + }).mouseleave(function() { + $(this).attr('src', self.opt.path + self.opt.cancelOff); + + if (self.opt.mouseover) { + self.opt.mouseover.call(self, self.score.val() || null); + } + }).click(function(evt) { + self.score.removeAttr('value'); + + if (self.opt.click) { + self.opt.click.call(self, null, evt); + } + }); + } + + self.stars.bind(action, function(evt) { + var value = parseInt(this.alt, 10); + + if (self.opt.half) { + var position = parseFloat((evt.pageX - $(this).offset().left) / self.opt.size), + diff = (position > .5) ? 1 : .5; + + value = parseFloat(this.alt) - 1 + diff; + + methods.fill.call(self, value); + + if (self.opt.precision) { + value = value - diff + position; + } + + methods.showHalf.call(self, value); + } else { + methods.fill.call(self, value); + } + + $this.data('score', value); + + methods.setTarget.call(self, value, true); + + if (self.opt.mouseover) { + self.opt.mouseover.call(self, value, evt); + } + }).click(function(evt) { + self.score.val((self.opt.half || self.opt.precision) ? $this.data('score') : this.alt); + + if (self.opt.click) { + self.opt.click.call(self, self.score.val(), evt); + } + }); + }, cancel: function(isClick) { + return $(this).each(function() { + var self = this, + $this = $(self); + + if ($this.data('readonly') === true) { + return this; + } + + if (isClick) { + methods.click.call(self, null); + } else { + methods.score.call(self, null); + } + + self.score.removeAttr('value'); + }); + }, click: function(score) { + return $(this).each(function() { + if ($(this).data('readonly') === true) { + return this; + } + + methods.initialize.call(this, score); + + if (this.opt.click) { + this.opt.click.call(this, score); + } else { + methods.error.call(this, 'you must add the "click: function(score, evt) { }" callback.'); + } + + methods.setTarget.call(this, score, true); + }); + }, error: function(message) { + $(this).html(message); + + $.error(message); + }, fill: function(score) { + var self = this, + number = self.stars.length, + count = 0, + $star , + star , + icon ; + + for (var i = 1; i <= number; i++) { + $star = self.stars.eq(i - 1); + + if (self.opt.iconRange && self.opt.iconRange.length > count) { + star = self.opt.iconRange[count]; + + if (self.opt.single) { + icon = (i == score) ? (star.on || self.opt.starOn) : (star.off || self.opt.starOff); + } else { + icon = (i <= score) ? (star.on || self.opt.starOn) : (star.off || self.opt.starOff); + } + + if (i <= star.range) { + $star.attr('src', self.opt.path + icon); + } + + if (i == star.range) { + count++; + } + } else { + if (self.opt.single) { + icon = (i == score) ? self.opt.starOn : self.opt.starOff; + } else { + icon = (i <= score) ? self.opt.starOn : self.opt.starOff; + } + + $star.attr('src', self.opt.path + icon); + } + } + }, fixHint: function() { + var $this = $(this), + score = parseInt(this.score.val(), 10), + hint = this.opt.noRatedMsg; + + if (!isNaN(score) && score > 0) { + hint = (score <= this.opt.hints.length && this.opt.hints[score - 1] !== null) ? this.opt.hints[score - 1] : score; + } + + $this.data('readonly', true).css('cursor', 'default').attr('title', hint); + + this.score.attr('readonly', 'readonly'); + this.stars.attr('title', hint); + }, getScore: function() { + var score = [], + value ; + + $(this).each(function() { + value = this.score.val(); + + score.push(value ? parseFloat(value) : undefined); + }); + + return (score.length > 1) ? score : score[0]; + }, readOnly: function(isReadOnly) { + return this.each(function() { + var $this = $(this); + + if ($this.data('readonly') === isReadOnly) { + return this; + } + + if (this.cancel) { + if (isReadOnly) { + this.cancel.hide(); + } else { + this.cancel.show(); + } + } + + if (isReadOnly) { + $this.unbind(); + + $this.children('img').unbind(); + + methods.fixHint.call(this); + } else { + methods.bindAction.call(this); + methods.unfixHint.call(this); + } + + $this.data('readonly', isReadOnly); + }); + }, reload: function() { + return methods.set.call(this, {}); + }, roundStar: function(score) { + var diff = (score - Math.floor(score)).toFixed(2); + + if (diff > this.opt.round.down) { + var icon = this.opt.starOn; // Full up: [x.76 .. x.99] + + if (diff < this.opt.round.up && this.opt.halfShow) { // Half: [x.26 .. x.75] + icon = this.opt.starHalf; + } else if (diff < this.opt.round.full) { // Full down: [x.00 .. x.5] + icon = this.opt.starOff; + } + + this.stars.eq(Math.ceil(score) - 1).attr('src', this.opt.path + icon); + } // Full down: [x.00 .. x.25] + }, score: function() { + return arguments.length ? methods.setScore.apply(this, arguments) : methods.getScore.call(this); + }, set: function(settings) { + this.each(function() { + var $this = $(this), + actual = $this.data('settings'), + clone = $this.clone().removeAttr('style').insertBefore($this); + + $this.remove(); + + clone.raty($.extend(actual, settings)); + }); + + return $(this.selector); + }, setScore: function(score) { + return $(this).each(function() { + if ($(this).data('readonly') === true) { + return this; + } + + methods.initialize.call(this, score); + methods.setTarget.call(this, score, true); + }); + }, setTarget: function(value, isKeep) { + if (this.opt.target) { + var $target = $(this.opt.target); + + if ($target.length == 0) { + methods.error.call(this, 'target selector invalid or missing!'); + } + + var score = value; + + if (!isKeep || score === undefined) { + score = this.opt.targetText; + } else { + if (this.opt.targetType == 'hint') { + score = (score === null && this.opt.cancel) + ? this.opt.cancelHint + : this.opt.hints[Math.ceil(score - 1)]; + } else { + score = this.opt.precision + ? parseFloat(score).toFixed(1) + : score; + } + } + + if (this.opt.targetFormat.indexOf('{score}') < 0) { + methods.error.call(this, 'template "{score}" missing!'); + } + + if (value !== null) { + score = this.opt.targetFormat.toString().replace('{score}', score); + } + + if ($target.is(':input')) { + $target.val(score); + } else { + $target.html(score); + } + } + }, showHalf: function(score) { + var diff = (score - Math.floor(score)).toFixed(1); + + if (diff > 0 && diff < .6) { + this.stars.eq(Math.ceil(score) - 1).attr('src', this.opt.path + this.opt.starHalf); + } + }, initialize: function(score) { + score = !score ? 0 : methods.between(score, 0, this.opt.number); + + methods.fill.call(this, score); + + if (score > 0) { + if (this.opt.halfShow) { + methods.roundStar.call(this, score); + } + + this.score.val(score); + } + }, unfixHint: function() { + for (var i = 0; i < this.opt.number; i++) { + this.stars.eq(i).attr('title', (i < this.opt.hints.length && this.opt.hints[i] !== null) ? this.opt.hints[i] : i); + } + + $(this).data('readonly', false).css('cursor', 'pointer').removeAttr('title'); + + this.score.attr('readonly', 'readonly'); + } + }; + + $.fn.raty = function(method) { + if (methods[method]) { + return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); + } else if (typeof method === 'object' || !method) { + return methods.init.apply(this, arguments); + } else { + $.error('Method ' + method + ' does not exist!'); + } + }; + + $.fn.raty.defaults = { + cancel : false, + cancelHint : 'cancel this rating!', + cancelOff : 'cancel-off.png', + cancelOn : 'cancel-on.png', + cancelPlace : 'left', + click : undefined, + half : false, + halfShow : true, + hints : ['bad', 'poor', 'regular', 'good', 'gorgeous'], + iconRange : undefined, + mouseover : undefined, + noRatedMsg : 'not rated yet', + number : 5, + path : 'img/', + precision : false, + round : { down: .25, full: .6, up: .76 }, + readOnly : false, + score : undefined, + scoreName : 'score', + single : false, + size : 16, + space : true, + starHalf : 'star-half.png', + starOff : 'star-off.png', + starOn : 'star-on.png', + target : undefined, + targetFormat : '{score}', + targetKeep : false, + targetText : '', + targetType : 'hint', + width : undefined + }; + +})(jQuery); diff --git a/app/assets/javascripts/letsrate.js.erb b/app/assets/javascripts/letsrate.js.erb new file mode 100644 index 0000000..1445cf3 --- /dev/null +++ b/app/assets/javascripts/letsrate.js.erb @@ -0,0 +1,50 @@ +$.fn.raty.defaults.path = "/assets"; +$.fn.raty.defaults.half_show = true; +$.fn.raty.defaults.half = false; +$.fn.raty.defaults.cancel = false; + +$(function(){ + $(".star").each(function() { + var $readonly = ($(this).attr('data-readonly') == 'true'); + $(this).raty({ + score: function() { + return $(this).attr('data-rating') + }, + number: function() { + return $(this).attr('data-star-count') + }, + half: $(this).attr('data-enable-half'), + half_show: $(this).attr('data-half-show'), + path: $(this).attr('data-star-path'), + starOn: $(this).attr('data-star-on'), + starOff: $(this).attr('data-star-off'), + starHalf: $(this).attr('data-star-half'), + cancel: $(this).attr('data-cancel'), + cancelPlace: $(this).attr('data-cancel-place'), + cancelHint: $(this).attr('data-cancel-hint'), + cancelOn: $(this).attr('data-cancel-on'), + cancelOff: $(this).attr('data-cancel-off'), + readOnly: $readonly, + click: function(score, evt) { + var _this = this; + if (score == null) { score = 0; } + $.post('<%= Rails.application.class.routes.url_helpers.rate_path %>', + { + score: score, + dimension: $(this).attr('data-dimension'), + id: $(this).attr('data-id'), + klass: $(this).attr('data-classname') + }, + function(data) { + if(data) { + // success code goes here ... + + if ($(_this).attr('data-disable-after-rate') == 'true') { + $(_this).raty('set', { readOnly: true, score: score }); + } + } + }); + } + }); + }); +}); diff --git a/app/assets/javascripts/ratyrate.js.erb b/app/assets/javascripts/ratyrate.js.erb new file mode 100644 index 0000000..432a5b4 --- /dev/null +++ b/app/assets/javascripts/ratyrate.js.erb @@ -0,0 +1,62 @@ +$.fn.raty.defaults.half = false; +$.fn.raty.defaults.halfShow = false; +$.fn.raty.defaults.path = "/assets"; +$.fn.raty.defaults.cancel = false; + +$(function(){ + $(".star").each(function() { + var $readonly = ($(this).attr('data-readonly') == 'true'); + var $half = ($(this).attr('data-enable-half') == 'true'); + var $halfShow = ($(this).attr('data-half-show') == 'true'); + var $single = ($(this).attr('data-single') == 'true'); + $(this).raty({ + score: function() { + return $(this).attr('data-rating') + }, + number: function() { + return $(this).attr('data-star-count') + }, + half: $half, + halfShow: $halfShow, + single: $single, + path: $(this).attr('data-star-path'), + starOn: $(this).attr('data-star-on'), + starOff: $(this).attr('data-star-off'), + starHalf: $(this).attr('data-star-half'), + cancel: $(this).attr('data-cancel'), + cancelPlace: $(this).attr('data-cancel-place'), + cancelHint: $(this).attr('data-cancel-hint'), + cancelOn: $(this).attr('data-cancel-on'), + cancelOff: $(this).attr('data-cancel-off'), + noRatedMsg: $(this).attr('data-no-rated-message'), + round: $(this).attr('data-round'), + space: $(this).attr('data-space'), + target: $(this).attr('data-target'), + targetText: $(this).attr('data-target-text'), + targetType: $(this).attr('data-target-type'), + targetFormat: $(this).attr('data-target-format'), + targetScoret: $(this).attr('data-target-score'), + readOnly: $readonly, + click: function(score, evt) { + var _this = this; + if (score == null) { score = 0; } + $.post('<%= Rails.application.class.routes.url_helpers.rate_path %>', + { + score: score, + dimension: $(this).attr('data-dimension'), + id: $(this).attr('data-id'), + klass: $(this).attr('data-classname') + }, + function(data) { + if(data) { + // success code goes here ... + + if ($(_this).attr('data-disable-after-rate') == 'true') { + $(_this).raty('set', { readOnly: true, score: score }); + } + } + }); + } + }); + }); +}); diff --git a/app/controllers/rater_controller.rb b/app/controllers/rater_controller.rb new file mode 100644 index 0000000..1a7a11b --- /dev/null +++ b/app/controllers/rater_controller.rb @@ -0,0 +1,13 @@ +class RaterController < ApplicationController + + def create + if user_signed_in? + obj = params[:klass].classify.constantize.find(params[:id]) + obj.rate params[:score].to_f, current_user, params[:dimension] + + render :json => true + else + render :json => false + end + end +end diff --git a/app/models/average_cache.rb b/app/models/average_cache.rb new file mode 100644 index 0000000..f9edf03 --- /dev/null +++ b/app/models/average_cache.rb @@ -0,0 +1,4 @@ +class AverageCache < ActiveRecord::Base + belongs_to :rater, :class_name => "User" + belongs_to :rateable, :polymorphic => true +end diff --git a/app/models/movie.rb b/app/models/movie.rb index 43e5da6..a258953 100644 --- a/app/models/movie.rb +++ b/app/models/movie.rb @@ -1,4 +1,6 @@ class Movie < ActiveRecord::Base + ratyrate_rateable "visual_effects", "original_score", "director", "custome_design" + def poster "http://ia.media-imdb.com/images/M/#{poster_url}" end diff --git a/app/models/overall_average.rb b/app/models/overall_average.rb new file mode 100644 index 0000000..16acb68 --- /dev/null +++ b/app/models/overall_average.rb @@ -0,0 +1,4 @@ +class OverallAverage < ActiveRecord::Base + belongs_to :rateable, :polymorphic => true +end + diff --git a/app/models/rate.rb b/app/models/rate.rb new file mode 100644 index 0000000..4eabbff --- /dev/null +++ b/app/models/rate.rb @@ -0,0 +1,7 @@ +class Rate < ActiveRecord::Base + belongs_to :rater, :class_name => "User" + belongs_to :rateable, :polymorphic => true + + #attr_accessible :rate, :dimension + +end \ No newline at end of file diff --git a/app/models/rating_cache.rb b/app/models/rating_cache.rb new file mode 100644 index 0000000..1a2b2eb --- /dev/null +++ b/app/models/rating_cache.rb @@ -0,0 +1,3 @@ +class RatingCache < ActiveRecord::Base + belongs_to :cacheable, :polymorphic => true +end \ No newline at end of file diff --git a/app/models/user.rb b/app/models/user.rb index c822027..e9c3b13 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,4 +1,6 @@ class User < ActiveRecord::Base + ratyrate_rater + # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, diff --git a/app/views/movies/show.html.erb b/app/views/movies/show.html.erb index f17ba11..706ab43 100644 --- a/app/views/movies/show.html.erb +++ b/app/views/movies/show.html.erb @@ -14,4 +14,26 @@

<%= @movie.description %>

$ <%= @movie.price %>

+ +
+
+ <%= imdb_style_rating_for @movie, current_user%> +
+
+
+ <% if current_user %> + Visual Effects: <%= rating_for @movie, "visual_effects", disable_after_rate: true, imdb_avg: true %> +
+ Visual Effects: <%= rating_for_user @movie, current_user, "visual_effects" %> +
+ Original Score: <%= rating_for @movie, "original_score", target: '#nrea', targetScore: '#nrea', single: true %> +
+ Director: <%= rating_for @movie, "original_score" %> +
+ Custome Design: <%= rating_for @movie, "custome_design" %> + <% end %> +
+
+
+
diff --git a/bin/rails b/bin/rails index 7feb6a3..728cd85 100755 --- a/bin/rails +++ b/bin/rails @@ -1,8 +1,4 @@ #!/usr/bin/env ruby -begin - load File.expand_path("../spring", __FILE__) -rescue LoadError -end APP_PATH = File.expand_path('../../config/application', __FILE__) require_relative '../config/boot' require 'rails/commands' diff --git a/bin/rake b/bin/rake index 8017a02..1724048 100755 --- a/bin/rake +++ b/bin/rake @@ -1,8 +1,4 @@ #!/usr/bin/env ruby -begin - load File.expand_path("../spring", __FILE__) -rescue LoadError -end require_relative '../config/boot' require 'rake' Rake.application.run diff --git a/bin/spring b/bin/spring deleted file mode 100755 index 253ec37..0000000 --- a/bin/spring +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env ruby - -# This file loads spring without using Bundler, in order to be fast -# It gets overwritten when you run the `spring binstub` command - -unless defined?(Spring) - require "rubygems" - require "bundler" - - if match = Bundler.default_lockfile.read.match(/^GEM$.*?^ spring \((.*?)\)$.*?^$/m) - ENV["GEM_PATH"] = ([Bundler.bundle_path.to_s] + Gem.path).join(File::PATH_SEPARATOR) - ENV["GEM_HOME"] = "" - Gem.paths = ENV - - gem "spring", match[1] - require "spring/binstub" - end -end diff --git a/config/database.yml b/config/database.yml index 556576e..f29616c 100644 --- a/config/database.yml +++ b/config/database.yml @@ -1,54 +1,25 @@ -# MySQL. Versions 5.0+ are recommended. +# SQLite version 3.x +# gem install sqlite3 # -# Install the MYSQL driver -# gem install mysql2 -# -# Ensure the MySQL gem is defined in your Gemfile -# gem 'mysql2' -# -# And be sure to use new-style password hashing: -# http://dev.mysql.com/doc/refman/5.0/en/old-client.html -# -default: &default - adapter: mysql2 - encoding: utf8 - pool: 5 - username: root - password: - socket: /tmp/mysql.sock - +# Ensure the SQLite 3 gem is defined in your Gemfile +# gem 'sqlite3' development: - <<: *default - database: moviestore_development + adapter: sqlite3 + database: db/development.sqlite3 + pool: 5 + timeout: 5000 # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". # Do not set this db to the same as development or production. test: - <<: *default - database: moviestore_test + adapter: sqlite3 + database: db/test.sqlite3 + pool: 5 + timeout: 5000 -# As with config/secrets.yml, you never want to store sensitive information, -# like your database password, in your source code. If your source code is -# ever seen by anyone, they now have access to your database. -# -# Instead, provide the password as a unix environment variable when you boot -# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database -# for a full rundown on how to provide these environment variables in a -# production deployment. -# -# On Heroku and other platform providers, you may have a full connection URL -# available as an environment variable. For example: -# -# DATABASE_URL="mysql2://myuser:mypass@localhost/somedatabase" -# -# You can use this database configuration with: -# -# production: -# url: <%= ENV['DATABASE_URL'] %> -# production: - <<: *default - database: moviestore_production - username: moviestore + adapter: sqlite3 + database: db/production.sqlite3 + pool: 5 password: <%= ENV['MOVIESTORE_DATABASE_PASSWORD'] %> diff --git a/config/routes.rb b/config/routes.rb index 26a4aa1..fc0872b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,5 @@ Rails.application.routes.draw do + post '/rate' => 'rater#create', :as => 'rate' devise_for :users, path_names: { sign_in: 'login', sign_out: 'logout', sign_up: 'register' } resources :movies, only: [:show, :index] diff --git a/db/migrate/20140706144643_create_rates.rb b/db/migrate/20140706144643_create_rates.rb new file mode 100644 index 0000000..a15dc84 --- /dev/null +++ b/db/migrate/20140706144643_create_rates.rb @@ -0,0 +1,20 @@ +class CreateRates < ActiveRecord::Migration + + def self.up + create_table :rates do |t| + t.belongs_to :rater + t.belongs_to :rateable, :polymorphic => true + t.float :stars, :null => false + t.string :dimension + t.timestamps + end + + add_index :rates, :rater_id + add_index :rates, [:rateable_id, :rateable_type] + end + + def self.down + drop_table :rates + end + +end \ No newline at end of file diff --git a/db/migrate/20140814135421_create_rating_caches.rb b/db/migrate/20140814135421_create_rating_caches.rb new file mode 100644 index 0000000..8eb063a --- /dev/null +++ b/db/migrate/20140814135421_create_rating_caches.rb @@ -0,0 +1,19 @@ +class CreateRatingCaches < ActiveRecord::Migration + + def self.up + create_table :rating_caches do |t| + t.belongs_to :cacheable, :polymorphic => true + t.float :avg, :null => false + t.integer :qty, :null => false + t.string :dimension + t.timestamps + end + + add_index :rating_caches, [:cacheable_id, :cacheable_type] + end + + def self.down + drop_table :rating_caches + end + +end \ No newline at end of file diff --git a/db/migrate/20140814135422_create_average_caches.rb b/db/migrate/20140814135422_create_average_caches.rb new file mode 100644 index 0000000..317d571 --- /dev/null +++ b/db/migrate/20140814135422_create_average_caches.rb @@ -0,0 +1,17 @@ +class CreateAverageCaches < ActiveRecord::Migration + + def self.up + create_table :average_caches do |t| + t.belongs_to :rater + t.belongs_to :rateable, :polymorphic => true + t.float :avg, :null => false + t.timestamps + end + end + + def self.down + drop_table :average_caches + end + +end + diff --git a/db/migrate/20140814135423_create_overall_averages.rb b/db/migrate/20140814135423_create_overall_averages.rb new file mode 100644 index 0000000..8b7ecaa --- /dev/null +++ b/db/migrate/20140814135423_create_overall_averages.rb @@ -0,0 +1,16 @@ +class CreateOverallAverages < ActiveRecord::Migration + + def self.up + create_table :overall_averages do |t| + t.belongs_to :rateable, :polymorphic => true + t.float :overall_avg, :null => false + t.timestamps + end + end + + def self.down + drop_table :overall_averages + end + +end + diff --git a/db/schema.rb b/db/schema.rb index 59fe27d..9811685 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,16 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20140701125957) do +ActiveRecord::Schema.define(version: 20140728144926) do + + create_table "average_caches", force: true do |t| + t.integer "rater_id" + t.integer "rateable_id" + t.string "rateable_type" + t.float "avg", null: false + t.datetime "created_at" + t.datetime "updated_at" + end create_table "movies", force: true do |t| t.string "title" @@ -24,6 +33,39 @@ t.datetime "updated_at" end + create_table "overall_averages", force: true do |t| + t.integer "rateable_id" + t.string "rateable_type" + t.float "overall_avg", null: false + t.datetime "created_at" + t.datetime "updated_at" + end + + create_table "rates", force: true do |t| + t.integer "rater_id" + t.integer "rateable_id" + t.string "rateable_type" + t.float "stars", null: false + t.string "dimension" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "rates", ["rateable_id", "rateable_type"], name: "index_rates_on_rateable_id_and_rateable_type" + add_index "rates", ["rater_id"], name: "index_rates_on_rater_id" + + create_table "rating_caches", force: true do |t| + t.integer "cacheable_id" + t.string "cacheable_type" + t.float "avg", null: false + t.integer "qty", null: false + t.string "dimension" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "rating_caches", ["cacheable_id", "cacheable_type"], name: "index_rating_caches_on_cacheable_id_and_cacheable_type" + create_table "users", force: true do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false @@ -39,7 +81,7 @@ t.datetime "updated_at" end - add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree - add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree + add_index "users", ["email"], name: "index_users_on_email", unique: true + add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end