├── .gitignore ├── .travis.yml ├── Gemfile ├── Gemfile.lock ├── Guardfile ├── README.md ├── Rakefile ├── app ├── assets │ ├── fonts │ │ ├── icons.eot │ │ ├── icons.otf │ │ ├── icons.svg │ │ ├── icons.ttf │ │ └── icons.woff │ ├── images │ │ ├── .keep │ │ ├── favicon.ico │ │ ├── loading.gif │ │ ├── missing.jpg │ │ └── night.jpg │ ├── javascripts │ │ ├── application.js │ │ ├── jquery.infinitescroll.js │ │ ├── likeships.js.coffee │ │ ├── notifications.js.coffee │ │ ├── rainyday.js │ │ ├── semantic.js │ │ ├── songs.js.coffee │ │ ├── textcomplete.js │ │ └── ting.js.coffee │ └── stylesheets │ │ ├── animate.css │ │ ├── application.css.scss │ │ ├── comments.css.scss │ │ ├── likeships.css.scss │ │ ├── notifications.css.scss │ │ ├── semantic.css.erb │ │ ├── songs.css.scss │ │ ├── textcomplete.css.scss │ │ ├── ting.css.scss │ │ └── users.css.scss ├── controllers │ ├── application_controller.rb │ ├── comments_controller.rb │ ├── concerns │ │ └── .keep │ ├── likeships_controller.rb │ ├── notifications_controller.rb │ ├── password_resets_controller.rb │ ├── sessions_controller.rb │ ├── songs_controller.rb │ └── users_controller.rb ├── helpers │ ├── application_helper.rb │ ├── comments_helper.rb │ ├── likeships_helper.rb │ ├── notifications_helper.rb │ ├── password_resets_helper.rb │ ├── sessions_helper.rb │ ├── songs_helper.rb │ └── users_helper.rb ├── mailers │ ├── .keep │ └── user_mailer.rb ├── models │ ├── .keep │ ├── comment.rb │ ├── concerns │ │ └── .keep │ ├── likeship.rb │ ├── notification.rb │ ├── song.rb │ └── user.rb ├── uploaders │ └── avatar_uploader.rb └── views │ ├── comments │ ├── _comment.html.slim │ ├── _comments.html.slim │ ├── _form.html.slim │ ├── _recent_comments.html.slim │ ├── create.js.erb │ └── destroy.js.erb │ ├── kaminari │ ├── _gap.html.slim │ ├── _page.html.slim │ └── _paginator.html.slim │ ├── layouts │ ├── _navbar.html.slim │ ├── application.html.slim │ └── users_form.html.slim │ ├── likeships │ ├── _like.html.slim │ ├── _likes.html.slim │ ├── _unlike.html.slim │ ├── create.js.erb │ └── destroy.js.erb │ ├── notifications │ ├── _notifications.html.slim │ ├── clear.js.erb │ ├── destroy.js.erb │ ├── index.html.slim │ └── notification │ │ ├── _comment.html.slim │ │ └── _mention.html.slim │ ├── password_resets │ ├── _form.html.slim │ ├── edit.html.slim │ └── new.html.slim │ ├── sessions │ ├── create.js.erb │ └── new.html.slim │ ├── shared │ ├── _error_messages.html.slim │ └── _flash.html.erb │ ├── songs │ ├── _form.html.slim │ ├── _play_list.html.slim │ ├── _share_buttons.html.slim │ ├── _song.html.slim │ ├── _songs.html.slim │ ├── collect.html.slim │ ├── create.js.erb │ ├── destroy.js.erb │ ├── edit.html.slim │ ├── index.html.slim │ ├── new.html.slim │ ├── show.html.slim │ └── update.js.erb │ ├── user_mailer │ ├── activation_needed_email.html.erb │ ├── activation_needed_email.text.erb │ ├── activation_success_email.html.erb │ ├── activation_success_email.text.erb │ ├── reset_password_email.html.erb │ └── reset_password_email.text.erb │ └── users │ ├── _user_panel.html.slim │ ├── create.js.erb │ ├── edit.html.slim │ ├── favorite_songs.js.erb │ ├── new.html.slim │ ├── recent_comments.js.erb │ ├── show.html.slim │ ├── update.js.erb │ └── user_songs.js.erb ├── bin ├── bundle ├── rails └── rake ├── config.ru ├── config ├── application.rb ├── boot.rb ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── cookies_serializer.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── kaminari_config.rb │ ├── mime_types.rb │ ├── session_store.rb │ ├── sorcery.rb │ └── wrap_parameters.rb ├── locales │ ├── en.yml │ ├── kaminari.en.yml │ ├── kaminari.zh-CN.yml │ └── zh-CN.yml ├── routes.rb └── secrets.yml ├── db ├── migrate │ ├── 20141206123135_sorcery_core.rb │ ├── 20141206123136_sorcery_remember_me.rb │ ├── 20141206123137_sorcery_reset_password.rb │ ├── 20141208045147_create_songs.rb │ ├── 20141212155258_create_comments.rb │ ├── 20141213092401_create_likeships.rb │ ├── 20141214004426_create_notifications.rb │ └── 20141223012634_sorcery_user_activation.rb └── schema.rb ├── lib ├── assets │ └── .keep └── tasks │ └── .keep ├── test ├── controllers │ ├── .keep │ ├── comments_controller_test.rb │ ├── likeships_controller_test.rb │ ├── notifications_controller_test.rb │ ├── password_resets_controller_test.rb │ ├── sessions_controller_test.rb │ ├── songs_controller_test.rb │ └── users_controller_test.rb ├── fixtures │ ├── .keep │ ├── comments.yml │ ├── likeships.yml │ ├── notifications.yml │ ├── songs.yml │ └── users.yml ├── helpers │ ├── .keep │ ├── comments_helper_test.rb │ ├── likeships_helper_test.rb │ ├── notifications_helper_test.rb │ ├── password_resets_helper_test.rb │ ├── sessions_helper_test.rb │ ├── songs_helper_test.rb │ └── users_helper_test.rb ├── integration │ └── .keep ├── mailers │ ├── .keep │ ├── previews │ │ └── user_mailer_preview.rb │ └── user_mailer_test.rb ├── models │ ├── .keep │ ├── comment_test.rb │ ├── likeship_test.rb │ ├── notification_test.rb │ ├── song_test.rb │ └── user_test.rb └── test_helper.rb └── vendor └── assets ├── javascripts └── .keep └── stylesheets └── .keep /.gitignore: -------------------------------------------------------------------------------- 1 | *.rbc 2 | capybara-*.html 3 | .rspec 4 | /log 5 | /tmp 6 | /db/*.sqlite3 7 | /db/*.sqlite3-journal 8 | /public/system 9 | /coverage/ 10 | /spec/tmp 11 | **.orig 12 | rerun.txt 13 | pickle-email-*.html 14 | 15 | # TODO Comment out these rules if you are OK with secrets being uploaded to the repo 16 | config/initializers/secret_token.rb 17 | #config/secrets.yml 18 | 19 | ## Environment normalisation: 20 | /.bundle 21 | /vendor/bundle 22 | 23 | # these should all be checked in to normalise the environment: 24 | # Gemfile.lock, .ruby-version, .ruby-gemset 25 | 26 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 27 | .rvmrc 28 | 29 | # if using bower-rails ignore default bower_components path bower.json files 30 | /vendor/assets/bower_components 31 | *.bowerrc 32 | bower.json 33 | 34 | # Ignore upload images 35 | /public/uploads/ 36 | 37 | # Ignore Spring files. 38 | /spring/*.pid 39 | 40 | .DS_Store 41 | 42 | #config/environments/*.rb 43 | 44 | # Automatically precompile assets before pushing to Heroku 45 | public/** -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | 3 | rvm: 4 | - "2.0.0" 5 | 6 | bundler_args: --without production 7 | 8 | services: 9 | - memcached -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://ruby.taobao.org' 2 | 3 | 4 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' 5 | gem 'rails', '4.2' 6 | 7 | # Use Uglifier as compressor for JavaScript assets 8 | gem 'uglifier', '>= 1.3.0' 9 | 10 | # Use CoffeeScript for .js.coffee assets and views 11 | gem 'coffee-rails', '~> 4.0.1' 12 | 13 | # Compass 14 | gem 'sass-rails', '~> 4.0.5' 15 | gem 'compass-rails' 16 | 17 | # Use jquery as the JavaScript library 18 | gem 'jquery-rails' 19 | 20 | # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks 21 | gem 'turbolinks' 22 | 23 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 24 | gem 'jbuilder', '~> 2.0' 25 | 26 | # bundle exec rake doc:rails generates the API under doc/api. 27 | gem 'sdoc', '~> 0.4.0', group: :doc 28 | 29 | # Font-awesome 30 | gem "font-awesome-rails" 31 | 32 | # Upload avatar 33 | gem 'carrierwave' 34 | gem 'mini_magick' 35 | gem 'remotipart' 36 | 37 | # Pagination 38 | gem 'kaminari' 39 | 40 | # Parse XML data 41 | gem 'nokogiri' 42 | 43 | # Authentication 44 | gem 'sorcery', '0.9.0' 45 | 46 | # Slim 47 | gem 'slim' 48 | 49 | # Memcached 50 | gem 'dalli' 51 | 52 | # i18n 53 | gem 'rails-i18n' 54 | 55 | group :production do 56 | gem 'pg', '0.15.1' 57 | gem 'rails_12factor', '0.0.2' 58 | end 59 | 60 | group :development, :test do 61 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console 62 | gem 'byebug' 63 | # Access an IRB console on exception pages or by using <%= console %> in views 64 | gem 'web-console', '~> 2.0.0.beta4' 65 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring 66 | gem 'spring' 67 | end 68 | 69 | 70 | group :development do 71 | gem 'sqlite3' 72 | gem "better_errors" 73 | gem 'binding_of_caller' 74 | gem 'meta_request' 75 | gem 'traceroute' 76 | end 77 | 78 | group :test do 79 | gem 'minitest-reporters', '1.0.5' 80 | gem 'guard-minitest', '2.3.1' 81 | end 82 | 83 | # require: false so bcrypt is loaded only when has_secure_password is used. 84 | # This is to avoid ActiveModel (and by extension the entire framework) 85 | # being dependent on a binary library. 86 | gem 'bcrypt', require: false 87 | 88 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://ruby.taobao.org/ 3 | specs: 4 | actionmailer (4.2.0) 5 | actionpack (= 4.2.0) 6 | actionview (= 4.2.0) 7 | activejob (= 4.2.0) 8 | mail (~> 2.5, >= 2.5.4) 9 | rails-dom-testing (~> 1.0, >= 1.0.5) 10 | actionpack (4.2.0) 11 | actionview (= 4.2.0) 12 | activesupport (= 4.2.0) 13 | rack (~> 1.6.0) 14 | rack-test (~> 0.6.2) 15 | rails-dom-testing (~> 1.0, >= 1.0.5) 16 | rails-html-sanitizer (~> 1.0, >= 1.0.1) 17 | actionview (4.2.0) 18 | activesupport (= 4.2.0) 19 | builder (~> 3.1) 20 | erubis (~> 2.7.0) 21 | rails-dom-testing (~> 1.0, >= 1.0.5) 22 | rails-html-sanitizer (~> 1.0, >= 1.0.1) 23 | activejob (4.2.0) 24 | activesupport (= 4.2.0) 25 | globalid (>= 0.3.0) 26 | activemodel (4.2.0) 27 | activesupport (= 4.2.0) 28 | builder (~> 3.1) 29 | activerecord (4.2.0) 30 | activemodel (= 4.2.0) 31 | activesupport (= 4.2.0) 32 | arel (~> 6.0) 33 | activesupport (4.2.0) 34 | i18n (~> 0.7) 35 | json (~> 1.7, >= 1.7.7) 36 | minitest (~> 5.1) 37 | thread_safe (~> 0.3, >= 0.3.4) 38 | tzinfo (~> 1.1) 39 | ansi (1.5.0) 40 | arel (6.0.0) 41 | bcrypt (3.1.10) 42 | better_errors (2.1.1) 43 | coderay (>= 1.0.0) 44 | erubis (>= 2.6.6) 45 | rack (>= 0.9.0) 46 | binding_of_caller (0.7.2) 47 | debug_inspector (>= 0.0.1) 48 | builder (3.2.2) 49 | byebug (4.0.2) 50 | columnize (= 0.9.0) 51 | callsite (0.0.11) 52 | carrierwave (0.10.0) 53 | activemodel (>= 3.2.0) 54 | activesupport (>= 3.2.0) 55 | json (>= 1.7) 56 | mime-types (>= 1.16) 57 | celluloid (0.16.0) 58 | timers (~> 4.0.0) 59 | chunky_png (1.3.4) 60 | coderay (1.1.0) 61 | coffee-rails (4.0.1) 62 | coffee-script (>= 2.2.0) 63 | railties (>= 4.0.0, < 5.0) 64 | coffee-script (2.3.0) 65 | coffee-script-source 66 | execjs 67 | coffee-script-source (1.9.1) 68 | columnize (0.9.0) 69 | compass (0.12.7) 70 | chunky_png (~> 1.2) 71 | fssm (>= 0.2.7) 72 | sass (~> 3.2.19) 73 | compass-rails (2.0.0) 74 | compass (>= 0.12.2) 75 | dalli (2.7.3) 76 | debug_inspector (0.0.2) 77 | erubis (2.7.0) 78 | execjs (2.4.0) 79 | faraday (0.9.1) 80 | multipart-post (>= 1.2, < 3) 81 | ffi (1.9.8) 82 | font-awesome-rails (4.3.0.0) 83 | railties (>= 3.2, < 5.0) 84 | formatador (0.2.5) 85 | fssm (0.2.10) 86 | globalid (0.3.3) 87 | activesupport (>= 4.1.0) 88 | guard (2.12.5) 89 | formatador (>= 0.2.4) 90 | listen (~> 2.7) 91 | lumberjack (~> 1.0) 92 | nenv (~> 0.1) 93 | notiffany (~> 0.0) 94 | pry (>= 0.9.12) 95 | shellany (~> 0.0) 96 | thor (>= 0.18.1) 97 | guard-minitest (2.3.1) 98 | guard (~> 2.0) 99 | minitest (>= 3.0) 100 | hike (1.2.3) 101 | hitimes (1.2.2) 102 | i18n (0.7.0) 103 | jbuilder (2.2.11) 104 | activesupport (>= 3.0.0, < 5) 105 | multi_json (~> 1.2) 106 | jquery-rails (4.0.3) 107 | rails-dom-testing (~> 1.0) 108 | railties (>= 4.2.0) 109 | thor (>= 0.14, < 2.0) 110 | json (1.8.2) 111 | jwt (1.4.1) 112 | kaminari (0.16.3) 113 | actionpack (>= 3.0.0) 114 | activesupport (>= 3.0.0) 115 | listen (2.9.0) 116 | celluloid (>= 0.15.2) 117 | rb-fsevent (>= 0.9.3) 118 | rb-inotify (>= 0.9) 119 | loofah (2.0.1) 120 | nokogiri (>= 1.5.9) 121 | lumberjack (1.0.9) 122 | mail (2.6.3) 123 | mime-types (>= 1.16, < 3) 124 | meta_request (0.3.4) 125 | callsite (~> 0.0, >= 0.0.11) 126 | rack-contrib (~> 1.1) 127 | railties (>= 3.0.0, < 5.0.0) 128 | method_source (0.8.2) 129 | mime-types (2.4.3) 130 | mini_magick (4.1.0) 131 | mini_portile (0.6.2) 132 | minitest (5.5.1) 133 | minitest-reporters (1.0.5) 134 | ansi 135 | builder 136 | minitest (>= 5.0) 137 | ruby-progressbar 138 | multi_json (1.11.0) 139 | multi_xml (0.5.5) 140 | multipart-post (2.0.0) 141 | nenv (0.2.0) 142 | nokogiri (1.6.6.2) 143 | mini_portile (~> 0.6.0) 144 | notiffany (0.0.6) 145 | nenv (~> 0.1) 146 | shellany (~> 0.0) 147 | oauth (0.4.7) 148 | oauth2 (1.0.0) 149 | faraday (>= 0.8, < 0.10) 150 | jwt (~> 1.0) 151 | multi_json (~> 1.3) 152 | multi_xml (~> 0.5) 153 | rack (~> 1.2) 154 | pg (0.15.1) 155 | pry (0.10.1) 156 | coderay (~> 1.1.0) 157 | method_source (~> 0.8.1) 158 | slop (~> 3.4) 159 | rack (1.6.0) 160 | rack-contrib (1.2.0) 161 | rack (>= 0.9.1) 162 | rack-test (0.6.3) 163 | rack (>= 1.0) 164 | rails (4.2.0) 165 | actionmailer (= 4.2.0) 166 | actionpack (= 4.2.0) 167 | actionview (= 4.2.0) 168 | activejob (= 4.2.0) 169 | activemodel (= 4.2.0) 170 | activerecord (= 4.2.0) 171 | activesupport (= 4.2.0) 172 | bundler (>= 1.3.0, < 2.0) 173 | railties (= 4.2.0) 174 | sprockets-rails 175 | rails-deprecated_sanitizer (1.0.3) 176 | activesupport (>= 4.2.0.alpha) 177 | rails-dom-testing (1.0.5) 178 | activesupport (>= 4.2.0.beta, < 5.0) 179 | nokogiri (~> 1.6.0) 180 | rails-deprecated_sanitizer (>= 1.0.1) 181 | rails-html-sanitizer (1.0.2) 182 | loofah (~> 2.0) 183 | rails-i18n (4.0.4) 184 | i18n (~> 0.6) 185 | railties (~> 4.0) 186 | rails_12factor (0.0.2) 187 | rails_serve_static_assets 188 | rails_stdout_logging 189 | rails_serve_static_assets (0.0.4) 190 | rails_stdout_logging (0.0.3) 191 | railties (4.2.0) 192 | actionpack (= 4.2.0) 193 | activesupport (= 4.2.0) 194 | rake (>= 0.8.7) 195 | thor (>= 0.18.1, < 2.0) 196 | rake (10.4.2) 197 | rb-fsevent (0.9.4) 198 | rb-inotify (0.9.5) 199 | ffi (>= 0.5.0) 200 | rdoc (4.2.0) 201 | remotipart (1.2.1) 202 | ruby-progressbar (1.7.1) 203 | sass (3.2.19) 204 | sass-rails (4.0.5) 205 | railties (>= 4.0.0, < 5.0) 206 | sass (~> 3.2.2) 207 | sprockets (~> 2.8, < 3.0) 208 | sprockets-rails (~> 2.0) 209 | sdoc (0.4.1) 210 | json (~> 1.7, >= 1.7.7) 211 | rdoc (~> 4.0) 212 | shellany (0.0.1) 213 | slim (3.0.3) 214 | temple (~> 0.7.3) 215 | tilt (>= 1.3.3, < 2.1) 216 | slop (3.6.0) 217 | sorcery (0.9.0) 218 | bcrypt (~> 3.1) 219 | oauth (~> 0.4, >= 0.4.4) 220 | oauth2 (>= 0.8.0) 221 | spring (1.3.3) 222 | sprockets (2.12.3) 223 | hike (~> 1.2) 224 | multi_json (~> 1.0) 225 | rack (~> 1.0) 226 | tilt (~> 1.1, != 1.3.0) 227 | sprockets-rails (2.2.4) 228 | actionpack (>= 3.0) 229 | activesupport (>= 3.0) 230 | sprockets (>= 2.8, < 4.0) 231 | sqlite3 (1.3.10) 232 | temple (0.7.5) 233 | thor (0.19.1) 234 | thread_safe (0.3.5) 235 | tilt (1.4.1) 236 | timers (4.0.1) 237 | hitimes 238 | traceroute (0.4.0) 239 | rails (>= 3.0.0) 240 | turbolinks (2.5.3) 241 | coffee-rails 242 | tzinfo (1.2.2) 243 | thread_safe (~> 0.1) 244 | uglifier (2.7.1) 245 | execjs (>= 0.3.0) 246 | json (>= 1.8.0) 247 | web-console (2.0.0) 248 | activemodel (~> 4.0) 249 | binding_of_caller (>= 0.7.2) 250 | railties (~> 4.0) 251 | sprockets-rails (>= 2.0, < 4.0) 252 | 253 | PLATFORMS 254 | ruby 255 | 256 | DEPENDENCIES 257 | bcrypt 258 | better_errors 259 | binding_of_caller 260 | byebug 261 | carrierwave 262 | coffee-rails (~> 4.0.1) 263 | compass-rails 264 | dalli 265 | font-awesome-rails 266 | guard-minitest (= 2.3.1) 267 | jbuilder (~> 2.0) 268 | jquery-rails 269 | kaminari 270 | meta_request 271 | mini_magick 272 | minitest-reporters (= 1.0.5) 273 | nokogiri 274 | pg (= 0.15.1) 275 | rails (= 4.2) 276 | rails-i18n 277 | rails_12factor (= 0.0.2) 278 | remotipart 279 | sass-rails (~> 4.0.5) 280 | sdoc (~> 0.4.0) 281 | slim 282 | sorcery (= 0.9.0) 283 | spring 284 | sqlite3 285 | traceroute 286 | turbolinks 287 | uglifier (>= 1.3.0) 288 | web-console (~> 2.0.0.beta4) 289 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | # Defines the matching rules for Guard. 2 | guard :minitest, spring: true, all_on_start: false do 3 | watch(%r{^test/(.*)/?(.*)_test\.rb$}) 4 | watch('test/test_helper.rb') { 'test' } 5 | watch('config/routes.rb') { integration_tests } 6 | watch(%r{^app/models/(.*?)\.rb$}) do |matches| 7 | "test/models/#{matches[1]}_test.rb" 8 | end 9 | watch(%r{^app/controllers/(.*?)_controller\.rb$}) do |matches| 10 | resource_tests(matches[1]) 11 | end 12 | watch(%r{^app/views/([^/]*?)/.*\.html\.erb$}) do |matches| 13 | ["test/controllers/#{matches[1]}_controller_test.rb"] + 14 | integration_tests(matches[1]) 15 | end 16 | watch(%r{^app/helpers/(.*?)_helper\.rb$}) do |matches| 17 | integration_tests(matches[1]) 18 | end 19 | watch('app/views/layouts/application.html.erb') do 20 | 'test/integration/site_layout_test.rb' 21 | end 22 | watch('app/helpers/sessions_helper.rb') do 23 | integration_tests << 'test/helpers/sessions_helper_test.rb' 24 | end 25 | watch('app/controllers/sessions_controller.rb') do 26 | ['test/controllers/sessions_controller_test.rb', 27 | 'test/integration/users_login_test.rb'] 28 | end 29 | watch('app/controllers/account_activations_controller.rb') do 30 | 'test/integration/users_signup_test.rb' 31 | end 32 | watch(%r{app/views/users/*}) do 33 | resource_tests('users') + 34 | ['test/integration/microposts_interface_test.rb'] 35 | end 36 | end 37 | 38 | # Returns the integration tests corresponding to the given resource. 39 | def integration_tests(resource = :all) 40 | if resource == :all 41 | Dir["test/integration/*"] 42 | else 43 | Dir["test/integration/#{resource}_*.rb"] 44 | end 45 | end 46 | 47 | # Returns the controller tests corresponding to the given resource. 48 | def controller_test(resource) 49 | "test/controllers/#{resource}_controller_test.rb" 50 | end 51 | 52 | # Returns all tests for the given resource. 53 | def resource_tests(resource) 54 | integration_tests(resource) << controller_test(resource) 55 | end 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ting 2 | 3 | [](https://travis-ci.org/Aufree/ting) 4 | 5 | Ting is a music social networking project written in Ruby on Rails and Semantic-UI. 6 | 7 | You can check out the demo at [this link](http://tinger.herokuapp.com). 8 | 9 | **You can ping me on [Twitter](https://twitter.com/_Paul_King_) or follow me on [Weibo](http://weibo.com/jinfali).** 10 | 11 | ## Screen Shots 12 | 13 |  14 | 15 |  16 | 17 |  18 | 19 |  20 | 21 |  22 | 23 | ## Requirements 24 | 25 | Ruby 2.0.0 + 26 | Memcached 1.4 + 27 | ImageMagick 6.8 + 28 | 29 | 30 | ## Installation 31 | 32 | $ git clone git://github.com/Aufree/ting.git 33 | $ cd ting 34 | 35 | ### Linux(Ubuntu) 36 | 37 | $ sudo apt-get update 38 | $ sudo apt-get install memcached imagemagick 39 | 40 | ### Mac OS 41 | 42 | $ brew install memcached && brew install imagemagick 43 | 44 | ## Run 45 | 46 | $ bundle install 47 | $ rake db:migrate 48 | # ensure that memcached has started up 49 | $ rails s 50 | 51 | ## Testing 52 | 53 | $ rake test 54 | 55 | ## License 56 | 57 | Copyright (c) 2015 Paul King 58 | 59 | --------------- 60 | 61 | Released under the MIT license: 62 | 63 | * [www.opensource.org/licenses/MIT](http://www.opensource.org/licenses/MIT) 64 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/assets/fonts/icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aufree/ting/66631ffdecf0d4752243a7a2d57cf3e9bc160a62/app/assets/fonts/icons.eot -------------------------------------------------------------------------------- /app/assets/fonts/icons.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aufree/ting/66631ffdecf0d4752243a7a2d57cf3e9bc160a62/app/assets/fonts/icons.otf -------------------------------------------------------------------------------- /app/assets/fonts/icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aufree/ting/66631ffdecf0d4752243a7a2d57cf3e9bc160a62/app/assets/fonts/icons.ttf -------------------------------------------------------------------------------- /app/assets/fonts/icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aufree/ting/66631ffdecf0d4752243a7a2d57cf3e9bc160a62/app/assets/fonts/icons.woff -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aufree/ting/66631ffdecf0d4752243a7a2d57cf3e9bc160a62/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aufree/ting/66631ffdecf0d4752243a7a2d57cf3e9bc160a62/app/assets/images/favicon.ico -------------------------------------------------------------------------------- /app/assets/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aufree/ting/66631ffdecf0d4752243a7a2d57cf3e9bc160a62/app/assets/images/loading.gif -------------------------------------------------------------------------------- /app/assets/images/missing.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aufree/ting/66631ffdecf0d4752243a7a2d57cf3e9bc160a62/app/assets/images/missing.jpg -------------------------------------------------------------------------------- /app/assets/images/night.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aufree/ting/66631ffdecf0d4752243a7a2d57cf3e9bc160a62/app/assets/images/night.jpg -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | //= require jquery 2 | //= require jquery_ujs 3 | //= require jquery.remotipart 4 | //= require turbolinks 5 | //= require jquery.infinitescroll 6 | //= require_tree . 7 | 8 | Turbolinks.enableProgressBar(); -------------------------------------------------------------------------------- /app/assets/javascripts/jquery.infinitescroll.js: -------------------------------------------------------------------------------- 1 | /*! 2 | -------------------------------- 3 | Infinite Scroll 4 | -------------------------------- 5 | + https://github.com/paulirish/infinite-scroll 6 | + version 2.1.0 7 | + Copyright 2011/12 Paul Irish & Luke Shumard 8 | + Licensed under the MIT license 9 | 10 | + Documentation: http://infinite-scroll.com/ 11 | */ 12 | ;(function(e){if(typeof define==="function"&&define.amd){define(["jquery"],e)}else{e(jQuery)}})(function(e,t){"use strict";e.infinitescroll=function(n,r,i){this.element=e(i);if(!this._create(n,r)){this.failed=true}};e.infinitescroll.defaults={loading:{finished:t,finishedMsg:"Congratulations, you've reached the end of the internet.",img:"data:image/gif;base64,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",msg:null,msgText:"Loading the next set of posts...",selector:null,speed:"fast",start:t},state:{isDuringAjax:false,isInvalidPage:false,isDestroyed:false,isDone:false,isPaused:false,isBeyondMaxPage:false,currPage:1},debug:false,behavior:t,binder:e(window),nextSelector:"div.navigation a:first",navSelector:"div.navigation",contentSelector:null,extraScrollPx:150,itemSelector:"div.post",animate:false,pathParse:t,dataType:"html",appendCallback:true,bufferPx:40,errorCallback:function(){},infid:0,pixelsFromNavToBottom:t,path:t,prefill:false,maxPage:t};e.infinitescroll.prototype={_binding:function(n){var r=this,i=r.options;i.v="2.0b2.120520";if(!!i.behavior&&this["_binding_"+i.behavior]!==t){this["_binding_"+i.behavior].call(this);return}if(n!=="bind"&&n!=="unbind"){this._debug("Binding value "+n+" not valid");return false}if(n==="unbind"){this.options.binder.unbind("smartscroll.infscr."+r.options.infid)}else{this.options.binder[n]("smartscroll.infscr."+r.options.infid,function(){r.scroll()})}this._debug("Binding",n)},_create:function(r,i){var s=e.extend(true,{},e.infinitescroll.defaults,r);this.options=s;var o=e(window);var u=this;if(!u._validate(r)){return false}var a=e(s.nextSelector).attr("href");if(!a){this._debug("Navigation selector not found");return false}s.path=s.path||this._determinepath(a);s.contentSelector=s.contentSelector||this.element;s.loading.selector=s.loading.selector||s.contentSelector;s.loading.msg=s.loading.msg||e('
" + message + "
2 | <%= t('mailer.activation.welcome') %>, <%= @user.name %> 3 |
4 | 5 |
6 | <%= t('mailer.activation.successfully_registered') %>,
7 | <%= t('mailer.activation.your_username') %>: <%= @user.name %>.
8 | <%= t('mailer.activation.please_follow') %><%= link_to "#{t('mailer.activation.this_link')}", @url, { style: "color: #08c; text-decoration: none;" } %><%= t('mailer.activation.complete') %>.
9 |
2 | <%= t('mailer.activation.congratz') %>, <%= @user.name %> 3 |
4 | 5 |
6 | <%= t('mailer.subject.done') %>,
7 | <%= t('mailer.activation.your_username') %>: <%= @user.name %>.
8 | <%= t('mailer.activation.you_can') %><%= link_to "#{t('mailer.activation.access')}", @url, { style: "color: #08c; text-decoration: none;" } %>, <%= t('mailer.activation.enjoy') %>!
9 |
2 | <%= t('mailer.reset_password.header') %> 3 |
4 | 5 |6 | <%= t('mailer.reset_password.body') %><%= link_to "#{t('mailer.activation.this_link')}", @url, { style: "color: #08c; text-decoration: none;" } %> 7 |
-------------------------------------------------------------------------------- /app/views/user_mailer/reset_password_email.text.erb: -------------------------------------------------------------------------------- 1 | <%= t('mailer.reset_password.header') %> 2 | =============================================== 3 | <%= t('mailer.reset_password.body') %><%= t('mailer.activation.this_link') %>: <%= @url %> -------------------------------------------------------------------------------- /app/views/users/_user_panel.html.slim: -------------------------------------------------------------------------------- 1 | .ui.large.vertical.labeld.menu 2 | .ui.header.item.center.teal.label 3 | = link_to current_user 4 | = image_tag current_user.avatar_url(:large), class: "ui circular image thumb user-avatar run-popup", data: { content: "#{t('.check_profile')}", variation: "large"} 5 | = link_to notifications_path, class: "item" 6 | - count = current_user.notifications.unread.count 7 | - if count > 0 8 | #unread-count.ui.red.label = count 9 | - else 10 | #unread-count.ui.label = count 11 | | #{t('notifications.index.notifications')} 12 | = link_to songs_path, class: "item" 13 | i.home.green.icon 14 | | #{t('layouts.users_form.home')} 15 | = link_to :collect, class: "item" 16 | i.star.yellow.icon 17 | | #{t('.likes')} 18 | = link_to edit_user_path(current_user), class: "item" 19 | i.settings.blue.icon 20 | | #{t('.settings')} 21 | = link_to :logout, method: :delete, class: "item" 22 | i.sign.out.red.icon 23 | | #{t('users.logout')} -------------------------------------------------------------------------------- /app/views/users/create.js.erb: -------------------------------------------------------------------------------- 1 | $('.ui.form').removeClass('loading') 2 | <% if @user.errors.empty? %> 3 | Turbolinks.visit('<%= root_path %>'); 4 | <% else %> 5 | $('#signup-errors').html("<%= j render 'shared/error_messages', object: @user %>"); 6 | $('#error_explanation').transition('tada'); 7 | <% end %> -------------------------------------------------------------------------------- /app/views/users/edit.html.slim: -------------------------------------------------------------------------------- 1 | .ui.grid 2 | .six.wide.column 3 | .four.wide.column 4 | .ui.form 5 | #user-edit-errors 6 | = render 'shared/error_messages', object: @user 7 | = form_for @user, remote: true do |f| 8 | .ui.center.aligned.field.basic.segment 9 | = image_tag(current_user.avatar_url(:large), class: "ui image circular thumb user-avatar user-avatar-upload run-popup", data: { content: "#{t('.upload_avatar')}", position: "top center" }) 10 | = f.file_field :avatar, class: "upload-avatar" 11 | .inline.fields.ui.center.aligned.basic.segment 12 | - I18n.available_locales.each do |locale| 13 | .field 14 | .ui.radio.checkbox.checked 15 | = f.radio_button(:locale, "#{locale}") 16 | = f.label t("locales.#{locale}") 17 | .field 18 | = f.text_field :email, placeholder: "#{t('activerecord.attributes.user.email')}", disabled: "disabled" 19 | .field 20 | = f.text_field :bio, placeholder: "#{t('activerecord.attributes.user.bio')}" 21 | .ui.buttons.session-buttons 22 | = link_to "#{t('.my_page')}", user_path(@user), class: "ui button orange" 23 | .or 24 | = f.submit "#{t('form.save')}", class: "ui blue button" -------------------------------------------------------------------------------- /app/views/users/favorite_songs.js.erb: -------------------------------------------------------------------------------- 1 | $('.status-panel').html("<%= j render 'songs/play_list', songs: @songs %>") 2 | -------------------------------------------------------------------------------- /app/views/users/new.html.slim: -------------------------------------------------------------------------------- 1 | .signup-form 2 | #signup-errors 3 | = render 'shared/error_messages', object: @user 4 | = form_for @user, remote: true do |f| 5 | .ui.form 6 | .field 7 | .ui.left.icon.large.input 8 | = f.text_field :name, placeholder: "#{t('activerecord.attributes.user.name')}" 9 | i.user.icon 10 | .field 11 | .ui.left.icon.large.input 12 | = f.text_field :email, placeholder: "#{t('activerecord.attributes.user.email')}" 13 | i.mail.icon 14 | .field 15 | .ui.left.icon.large.input 16 | = f.password_field :password, placeholder: "#{t('activerecord.attributes.user.password')}" 17 | i.lock.icon 18 | .field 19 | .ui.left.icon.large.input 20 | = f.password_field :password_confirmation, placeholder: "#{t('activerecord.attributes.user.password_confirmation')}" 21 | i.lock.icon 22 | = f.submit "#{t('users.signup')}", class: "ui fluid green large button", data: { disable_with: "#{t('form.submitting')}" } 23 | -------------------------------------------------------------------------------- /app/views/users/recent_comments.js.erb: -------------------------------------------------------------------------------- 1 | $('.status-panel').html("<%= j render 'comments/recent_comments', comments: @comments %>") -------------------------------------------------------------------------------- /app/views/users/show.html.slim: -------------------------------------------------------------------------------- 1 | #canvas-bg 2 | canvas#particles 3 | .ui.grid 4 | .three.wide.column 5 | .ten.wide.column 6 | #user-show.ui.grid 7 | .one.wide.column 8 | .fourteen.wide.column 9 | .ui.grid 10 | .one.wide.column 11 | .ui.five.wide.column.center.aligned.info-column 12 | = image_tag(@user.avatar_url(:large), class: "ui image circular lg run-popup") 13 | .center 14 | h3 = @user.name 15 | .center.bio 16 | = @user.bio 17 | .ui.divider 18 | - class_name = "ui left aligned basic segment" unless I18n.locale.to_s == "zh-CN" 19 | .user-status class="#{class_name}" 20 | .user-created-at 21 | p #{t('.created_at')}: #{time_ago_in_words(@user.created_at)} #{t('.ago')} 22 | .user-songs-count 23 | p #{t('.total_songs')}: #{t('.songs_count', count: @user.songs.count)} 24 | .user-comments-count 25 | p #{t('.total_comments')}: #{t('.comments_count', count: @user.comments.count)} 26 | .nine.wide.column.content-column 27 | .ui.secondary.large.menu data-loading="#{t('.loading')}" 28 | = link_to "#{t('.songs')}", user_songs_url+"?id=#{@user.name}", remote: true, class: "active item" 29 | = link_to "#{t('users.user_panel.likes')}", favorite_url+"?id=#{@user.name}", remote: true, class: "item" 30 | = link_to "#{t('comments.comments')}", recent_comments_url+"?id=#{@user.name}", remote: true, class: "item" 31 | .status-panel 32 | = render 'songs/play_list', songs: @songs 33 | 34 | audio id="player" data-xiami_id="-1" 35 | 36 | javascript: 37 | $(function() { 38 | if($("#particles").size()>0){ 39 | (function() { 40 | var width, height, largeHeader, canvas, ctx, circles, target, animateHeader = true; 41 | width = $('#canvas-bg').width(); 42 | height= $('#canvas-bg').height(); 43 | target = {x: 0, y: height}; 44 | largeHeader = document.getElementById('canvas-bg'); 45 | largeHeader.style.height = height+'px'; 46 | canvas = document.getElementById('particles'); 47 | canvas.width = width; 48 | canvas.height = height; 49 | ctx = canvas.getContext('2d'); 50 | 51 | circles = []; 52 | for(var x = 0; x < width*0.5; x++) { 53 | var c = new Circle(); 54 | circles.push(c); 55 | } 56 | animate(); 57 | window.addEventListener('resize',function(){ 58 | width = window.innerWidth; 59 | canvas.width = width; 60 | }); 61 | function animate() { 62 | if(animateHeader) { 63 | ctx.clearRect(0,0,width,height); 64 | for(var i in circles) { 65 | circles[i].draw(); 66 | } 67 | } 68 | requestAnimationFrame(animate); 69 | } 70 | function Circle() { 71 | var _this = this; 72 | 73 | (function() { 74 | _this.pos = {}; 75 | init(); 76 | })(); 77 | 78 | function init() { 79 | _this.pos.x = Math.random()*width; 80 | _this.pos.y = height+Math.random()*100; 81 | _this.alpha = 0.1+Math.random()*0.3; 82 | _this.scale = 0.1+Math.random()*0.3; 83 | _this.velocity = Math.random(); 84 | } 85 | 86 | this.draw = function() { 87 | if(_this.alpha <= 0) { 88 | init(); 89 | } 90 | 91 | _this.pos.x -=0.1; 92 | _this.pos.y -= _this.velocity; 93 | _this.alpha -= 0.0005; 94 | ctx.beginPath(); 95 | ctx.arc(_this.pos.x, _this.pos.y, _this.scale*10, 0, 2 * Math.PI, false); 96 | ctx.fillStyle = 'rgba(255,255,255,'+ _this.alpha+')'; 97 | ctx.fill(); 98 | }; 99 | } 100 | })(); 101 | }}) -------------------------------------------------------------------------------- /app/views/users/update.js.erb: -------------------------------------------------------------------------------- 1 | <% if @user.errors.empty? %> 2 | Turbolinks.visit('<%= user_path(@user) %>'); 3 | <% else %> 4 | $('#user-edit-errors').html("<%= j render 'shared/error_messages', object: @user %>") 5 | $('#error_explanation').transition('tada'); 6 | <% end %> -------------------------------------------------------------------------------- /app/views/users/user_songs.js.erb: -------------------------------------------------------------------------------- 1 | $('.status-panel').html("<%= j render 'songs/play_list', songs: @songs %>") -------------------------------------------------------------------------------- /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', __FILE__) 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 | -------------------------------------------------------------------------------- /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 File.expand_path('../boot', __FILE__) 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 Ting 10 | class Application < Rails::Application 11 | # Settings in config/environments/* take precedence over those specified here. 12 | # Application configuration should go into files in config/initializers 13 | # -- all .rb files in that directory are automatically loaded. 14 | 15 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 16 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 17 | # config.time_zone = 'Central Time (US & Canada)' 18 | 19 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 20 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 21 | config.i18n.default_locale = "en" 22 | config.i18n.available_locales = [:en, 'zh-CN'] 23 | I18n.enforce_available_locales = false 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | # Set up gems listed in the Gemfile. 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | 4 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 5 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: 5 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: db/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: db/test.sqlite3 22 | 23 | production: 24 | <<: *default 25 | database: db/production.sqlite3 26 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports and disable caching. 13 | config.consider_all_requests_local = true 14 | config.action_controller.perform_caching = true 15 | config.cache_store = [:dalli_store,"127.0.0.1", { namespace: "ting", compress: true }] 16 | 17 | # Don't care if the mailer can't send. 18 | config.action_mailer.raise_delivery_errors = false 19 | 20 | # Print deprecation notices to the Rails logger. 21 | config.active_support.deprecation = :log 22 | 23 | # Raise an error on page load if there are pending migrations. 24 | config.active_record.migration_error = :page_load 25 | 26 | # Debug mode disables concatenation and preprocessing of assets. 27 | # This option may cause significant delays in view rendering with a large 28 | # number of complex assets. 29 | config.assets.debug = true 30 | 31 | # Adds additional error checking when serving assets at runtime. 32 | # Checks for improperly declared sprockets dependencies. 33 | # Raises helpful error messages. 34 | config.assets.raise_runtime_errors = true 35 | config.action_mailer.default_url_options = { host: 'localhost:3000' } 36 | config.action_mailer.delivery_method = :smtp 37 | config.action_mailer.smtp_settings = { 38 | address: 'smtp.gmail.com', 39 | port: 587, 40 | domain: 'gmail.com', 41 | user_name: 'your_email', 42 | password: 'your_email_password', 43 | authentication: 'plain', 44 | enable_starttls_auto: true 45 | } 46 | # Raises error for missing translations 47 | # config.action_view.raise_on_missing_translations = true 48 | end 49 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Enable Rack::Cache to put a simple HTTP cache in front of your application 18 | # Add `rack-cache` to your Gemfile before enabling this. 19 | # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid. 20 | # config.action_dispatch.rack_cache = true 21 | 22 | # Disable Rails's static asset server (Apache or nginx will already do this). 23 | config.serve_static_assets = false 24 | 25 | # Compress JavaScripts and CSS. 26 | config.assets.js_compressor = :uglifier 27 | # config.assets.css_compressor = :sass 28 | 29 | # Do not fallback to assets pipeline if a precompiled asset is missed. 30 | config.assets.compile = false 31 | 32 | # Generate digests for assets URLs. 33 | config.assets.digest = true 34 | 35 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 36 | 37 | # Specifies the header that your server uses for sending files. 38 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache 39 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx 40 | 41 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 42 | # config.force_ssl = true 43 | 44 | # Set to :debug to see everything in the log. 45 | config.log_level = :info 46 | 47 | # Prepend all log lines with the following tags. 48 | # config.log_tags = [ :subdomain, :uuid ] 49 | 50 | # Use a different logger for distributed setups. 51 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 52 | 53 | # Use a different cache store in production. 54 | config.cache_store = :dalli_store 55 | 56 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 57 | # config.action_controller.asset_host = "http://assets.example.com" 58 | 59 | # Ignore bad email addresses and do not raise email delivery errors. 60 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 61 | # config.action_mailer.raise_delivery_errors = false 62 | 63 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 64 | # the I18n.default_locale when a translation cannot be found). 65 | config.i18n.fallbacks = true 66 | 67 | # Send deprecation notices to registered listeners. 68 | config.active_support.deprecation = :notify 69 | 70 | # Disable automatic flushing of the log to improve performance. 71 | # config.autoflush_log = false 72 | 73 | # Use default logging formatter so that PID and timestamp are not suppressed. 74 | config.log_formatter = ::Logger::Formatter.new 75 | 76 | # Do not dump schema after migrations. 77 | config.active_record.dump_schema_after_migration = false 78 | config.assets.raise_runtime_errors = true 79 | config.action_mailer.default_url_options = { host: 'tinger.heroku.com' } 80 | config.action_mailer.delivery_method = :smtp 81 | config.action_mailer.smtp_settings = { 82 | address: 'smtp.gmail.com', 83 | port: 587, 84 | domain: 'gmail.com', 85 | user_name: 'your_email', 86 | password: 'your_email_password', 87 | authentication: 'plain', 88 | enable_starttls_auto: true 89 | } 90 | end 91 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure static asset server for tests with Cache-Control for performance. 16 | config.serve_static_files = true 17 | config.static_cache_control = 'public, max-age=3600' 18 | 19 | # Show full error reports and disable caching. 20 | config.consider_all_requests_local = true 21 | config.action_controller.perform_caching = false 22 | 23 | # Raise exceptions instead of rendering exception templates. 24 | config.action_dispatch.show_exceptions = false 25 | 26 | # Disable request forgery protection in test environment. 27 | config.action_controller.allow_forgery_protection = false 28 | 29 | # Tell Action Mailer not to deliver emails to the real world. 30 | # The :test delivery method accumulates sent emails in the 31 | # ActionMailer::Base.deliveries array. 32 | config.action_mailer.delivery_method = :test 33 | 34 | # Print deprecation notices to the stderr. 35 | config.active_support.deprecation = :stderr 36 | 37 | config.active_support.test_order = :sorted 38 | 39 | config.active_record.raise_in_transactional_callbacks = true 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | end 43 | -------------------------------------------------------------------------------- /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 | # Precompile additional assets. 7 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 8 | # Rails.application.config.assets.precompile += %w( search.js ) 9 | -------------------------------------------------------------------------------- /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/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.action_dispatch.cookies_serializer = :json -------------------------------------------------------------------------------- /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 += [:password] 5 | -------------------------------------------------------------------------------- /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/kaminari_config.rb: -------------------------------------------------------------------------------- 1 | Kaminari.configure do |config| 2 | config.default_per_page = 10 3 | # config.max_per_page = nil 4 | config.window = 1 5 | config.outer_window = 1 6 | # config.left = 0 7 | # config.right = 0 8 | # config.page_method_name = :page 9 | # config.param_name = :page 10 | end 11 | -------------------------------------------------------------------------------- /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/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_ting_session' 4 | -------------------------------------------------------------------------------- /config/initializers/sorcery.rb: -------------------------------------------------------------------------------- 1 | # The first thing you need to configure is which modules you need in your app. 2 | # The default is nothing which will include only core features (password encryption, login/logout). 3 | # Available submodules are: :user_activation, :http_basic_auth, :remember_me, 4 | # :reset_password, :session_timeout, :brute_force_protection, :activity_logging, :external 5 | Rails.application.config.sorcery.submodules = [:remember_me, :reset_password, :user_activation] 6 | 7 | # Here you can configure each submodule's features. 8 | Rails.application.config.sorcery.configure do |config| 9 | # -- core -- 10 | # What controller action to call for non-authenticated users. You can also 11 | # override the 'not_authenticated' method of course. 12 | # Default: `:not_authenticated` 13 | # 14 | # config.not_authenticated_action = 15 | 16 | 17 | # When a non logged in user tries to enter a page that requires login, save 18 | # the URL he wanted to reach, and send him there after login, using 'redirect_back_or_to'. 19 | # Default: `true` 20 | # 21 | # config.save_return_to_url = 22 | 23 | 24 | # Set domain option for cookies; Useful for remember_me submodule. 25 | # Default: `nil` 26 | # 27 | # config.cookie_domain = 28 | 29 | 30 | # -- session timeout -- 31 | # How long in seconds to keep the session alive. 32 | # Default: `3600` 33 | # 34 | # config.session_timeout = 35 | 36 | 37 | # Use the last action as the beginning of session timeout. 38 | # Default: `false` 39 | # 40 | # config.session_timeout_from_last_action = 41 | 42 | 43 | # -- http_basic_auth -- 44 | # What realm to display for which controller name. For example {"My App" => "Application"} 45 | # Default: `{"application" => "Application"}` 46 | # 47 | # config.controller_to_realm_map = 48 | 49 | 50 | # -- activity logging -- 51 | # will register the time of last user login, every login. 52 | # Default: `true` 53 | # 54 | # config.register_login_time = 55 | 56 | 57 | # will register the time of last user logout, every logout. 58 | # Default: `true` 59 | # 60 | # config.register_logout_time = 61 | 62 | 63 | # will register the time of last user action, every action. 64 | # Default: `true` 65 | # 66 | # config.register_last_activity_time = 67 | 68 | 69 | # -- external -- 70 | # What providers are supported by this app, i.e. [:twitter, :facebook, :github, :linkedin, :xing, :google, :liveid] . 71 | # Default: `[]` 72 | # 73 | # config.external_providers = 74 | 75 | 76 | # You can change it by your local ca_file. i.e. '/etc/pki/tls/certs/ca-bundle.crt' 77 | # Path to ca_file. By default use a internal ca-bundle.crt. 78 | # Default: `'path/to/ca_file'` 79 | # 80 | # config.ca_file = 81 | 82 | 83 | # For information about LinkedIn API: 84 | # - user info fields go to https://developer.linkedin.com/documents/profile-fields 85 | # - access permissions go to https://developer.linkedin.com/documents/authentication#granting 86 | # 87 | # config.linkedin.key = "" 88 | # config.linkedin.secret = "" 89 | # config.linkedin.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=linkedin" 90 | # config.linkedin.user_info_fields = ['first-name', 'last-name'] 91 | # config.linkedin.user_info_mapping = {first_name: "firstName", last_name: "lastName"} 92 | # config.linkedin.access_permissions = ['r_basicprofile'] 93 | # 94 | # 95 | # For information about XING API: 96 | # - user info fields go to https://dev.xing.com/docs/get/users/me 97 | # 98 | # config.xing.key = "" 99 | # config.xing.secret = "" 100 | # config.xing.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=xing" 101 | # config.xing.user_info_mapping = {first_name: "first_name", last_name: "last_name"} 102 | # 103 | # 104 | # Twitter wil not accept any requests nor redirect uri containing localhost, 105 | # make sure you use 0.0.0.0:3000 to access your app in development 106 | # 107 | # config.twitter.key = "" 108 | # config.twitter.secret = "" 109 | # config.twitter.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=twitter" 110 | # config.twitter.user_info_mapping = {:email => "screen_name"} 111 | # 112 | # config.facebook.key = "" 113 | # config.facebook.secret = "" 114 | # config.facebook.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=facebook" 115 | # config.facebook.user_info_mapping = {:email => "name"} 116 | # config.facebook.access_permissions = ["email", "publish_stream"] 117 | # 118 | # config.github.key = "" 119 | # config.github.secret = "" 120 | # config.github.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=github" 121 | # config.github.user_info_mapping = {:email => "name"} 122 | # 123 | # config.google.key = "" 124 | # config.google.secret = "" 125 | # config.google.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=google" 126 | # config.google.user_info_mapping = {:email => "email", :username => "name"} 127 | # 128 | # config.vk.key = "" 129 | # config.vk.secret = "" 130 | # config.vk.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=vk" 131 | # config.vk.user_info_mapping = {:login => "domain", :name => "full_name"} 132 | # 133 | # To use liveid in development mode you have to replace mydomain.com with 134 | # a valid domain even in development. To use a valid domain in development 135 | # simply add your domain in your /etc/hosts file in front of 127.0.0.1 136 | # 137 | # config.liveid.key = "" 138 | # config.liveid.secret = "" 139 | # config.liveid.callback_url = "http://mydomain.com:3000/oauth/callback?provider=liveid" 140 | # config.liveid.user_info_mapping = {:username => "name"} 141 | 142 | 143 | # --- user config --- 144 | config.user_config do |user| 145 | # -- core -- 146 | # specify username attributes, for example: [:username, :email]. 147 | # Default: `[:email]` 148 | # 149 | user.username_attribute_names = [:email] 150 | 151 | 152 | # change *virtual* password attribute, the one which is used until an encrypted one is generated. 153 | # Default: `:password` 154 | # 155 | # user.password_attribute_name = 156 | 157 | 158 | # downcase the username before trying to authenticate, default is false 159 | # Default: `false` 160 | # 161 | # user.downcase_username_before_authenticating = 162 | 163 | 164 | # change default email attribute. 165 | # Default: `:email` 166 | # 167 | # user.email_attribute_name = 168 | 169 | 170 | # change default crypted_password attribute. 171 | # Default: `:crypted_password` 172 | # 173 | # user.crypted_password_attribute_name = 174 | 175 | 176 | # what pattern to use to join the password with the salt 177 | # Default: `""` 178 | # 179 | # user.salt_join_token = 180 | 181 | 182 | # change default salt attribute. 183 | # Default: `:salt` 184 | # 185 | # user.salt_attribute_name = 186 | 187 | 188 | # how many times to apply encryption to the password. 189 | # Default: `nil` 190 | # 191 | # user.stretches = 192 | 193 | 194 | # encryption key used to encrypt reversible encryptions such as AES256. 195 | # WARNING: If used for users' passwords, changing this key will leave passwords undecryptable! 196 | # Default: `nil` 197 | # 198 | # user.encryption_key = 199 | 200 | 201 | # use an external encryption class. 202 | # Default: `nil` 203 | # 204 | # user.custom_encryption_provider = 205 | 206 | 207 | # encryption algorithm name. See 'encryption_algorithm=' for available options. 208 | # Default: `:bcrypt` 209 | # 210 | # user.encryption_algorithm = 211 | 212 | 213 | # make this configuration inheritable for subclasses. Useful for ActiveRecord's STI. 214 | # Default: `false` 215 | # 216 | # user.subclasses_inherit_config = 217 | 218 | 219 | # -- remember_me -- 220 | # allow the remember_me cookie to settable through AJAX 221 | # Default: `true` 222 | # 223 | # user.remember_me_httponly = 224 | 225 | # How long in seconds the session length will be 226 | # Default: `604800` 227 | # 228 | # user.remember_me_for = 229 | 230 | 231 | # -- user_activation -- 232 | # the attribute name to hold activation state (active/pending). 233 | # Default: `:activation_state` 234 | # 235 | # user.activation_state_attribute_name = 236 | 237 | 238 | # the attribute name to hold activation code (sent by email). 239 | # Default: `:activation_token` 240 | # 241 | # user.activation_token_attribute_name = 242 | 243 | 244 | # the attribute name to hold activation code expiration date. 245 | # Default: `:activation_token_expires_at` 246 | # 247 | # user.activation_token_expires_at_attribute_name = 248 | 249 | 250 | # how many seconds before the activation code expires. nil for never expires. 251 | # Default: `nil` 252 | # 253 | # user.activation_token_expiration_period = 254 | 255 | 256 | # your mailer class. Required. 257 | # Default: `nil` 258 | # 259 | user.user_activation_mailer = UserMailer 260 | 261 | 262 | # when true sorcery will not automatically 263 | # email activation details and allow you to 264 | # manually handle how and when email is sent. 265 | # Default: `false` 266 | # 267 | # user.activation_mailer_disabled = 268 | 269 | 270 | # activation needed email method on your mailer class. 271 | # Default: `:activation_needed_email` 272 | # 273 | # user.activation_needed_email_method_name = 274 | 275 | 276 | # activation success email method on your mailer class. 277 | # Default: `:activation_success_email` 278 | # 279 | # user.activation_success_email_method_name = 280 | 281 | 282 | # do you want to prevent or allow users that did not activate by email to login? 283 | # Default: `true` 284 | # 285 | # user.prevent_non_active_users_to_login = 286 | 287 | 288 | # -- reset_password -- 289 | # reset password code attribute name. 290 | # Default: `:reset_password_token` 291 | # 292 | # user.reset_password_token_attribute_name = 293 | 294 | 295 | # expires at attribute name. 296 | # Default: `:reset_password_token_expires_at` 297 | # 298 | # user.reset_password_token_expires_at_attribute_name = 299 | 300 | 301 | # when was email sent, used for hammering protection. 302 | # Default: `:reset_password_email_sent_at` 303 | # 304 | # user.reset_password_email_sent_at_attribute_name = 305 | 306 | 307 | # mailer class. Needed. 308 | # Default: `nil` 309 | # 310 | user.reset_password_mailer = UserMailer 311 | 312 | 313 | # reset password email method on your mailer class. 314 | # Default: `:reset_password_email` 315 | # 316 | # user.reset_password_email_method_name = 317 | 318 | 319 | # when true sorcery will not automatically 320 | # email password reset details and allow you to 321 | # manually handle how and when email is sent 322 | # Default: `false` 323 | # 324 | # user.reset_password_mailer_disabled = 325 | 326 | 327 | # how many seconds before the reset request expires. nil for never expires. 328 | # Default: `nil` 329 | # 330 | # user.reset_password_expiration_period = 331 | 332 | 333 | # hammering protection, how long to wait before allowing another email to be sent. 334 | # Default: `5 * 60` 335 | # 336 | # user.reset_password_time_between_emails = 337 | 338 | 339 | # -- brute_force_protection -- 340 | # Failed logins attribute name. 341 | # Default: `:failed_logins_count` 342 | # 343 | # user.failed_logins_count_attribute_name = 344 | 345 | 346 | # This field indicates whether user is banned and when it will be active again. 347 | # Default: `:lock_expires_at` 348 | # 349 | # user.lock_expires_at_attribute_name = 350 | 351 | 352 | # How many failed logins allowed. 353 | # Default: `50` 354 | # 355 | # user.consecutive_login_retries_amount_limit = 356 | 357 | 358 | # How long the user should be banned. in seconds. 0 for permanent. 359 | # Default: `60 * 60` 360 | # 361 | # user.login_lock_time_period = 362 | 363 | # Unlock token attribute name 364 | # Default: `:unlock_token` 365 | # 366 | # user.unlock_token_attribute_name = 367 | 368 | # Unlock token mailer method 369 | # Default: `:send_unlock_token_email` 370 | # 371 | # user.unlock_token_email_method_name = 372 | 373 | # when true sorcery will not automatically 374 | # send email with unlock token 375 | # Default: `false` 376 | # 377 | # user.unlock_token_mailer_disabled = true 378 | 379 | # Unlock token mailer class 380 | # Default: `nil` 381 | # 382 | # user.unlock_token_mailer = UserMailer 383 | 384 | # -- activity logging -- 385 | # Last login attribute name. 386 | # Default: `:last_login_at` 387 | # 388 | # user.last_login_at_attribute_name = 389 | 390 | 391 | # Last logout attribute name. 392 | # Default: `:last_logout_at` 393 | # 394 | # user.last_logout_at_attribute_name = 395 | 396 | 397 | # Last activity attribute name. 398 | # Default: `:last_activity_at` 399 | # 400 | # user.last_activity_at_attribute_name = 401 | 402 | 403 | # How long since last activity is he user defined logged out? 404 | # Default: `10 * 60` 405 | # 406 | # user.activity_timeout = 407 | 408 | 409 | # -- external -- 410 | # Class which holds the various external provider data for this user. 411 | # Default: `nil` 412 | # 413 | # user.authentications_class = 414 | 415 | 416 | # User's identifier in authentications class. 417 | # Default: `:user_id` 418 | # 419 | # user.authentications_user_id_attribute_name = 420 | 421 | 422 | # Provider's identifier in authentications class. 423 | # Default: `:provider` 424 | # 425 | # user.provider_attribute_name = 426 | 427 | 428 | # User's external unique identifier in authentications class. 429 | # Default: `:uid` 430 | # 431 | # user.provider_uid_attribute_name = 432 | end 433 | 434 | # This line must come after the 'user config' block. 435 | # Define which model authenticates with sorcery. 436 | config.user_class = "User" 437 | end 438 | -------------------------------------------------------------------------------- /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] if respond_to?(:wrap_parameters) 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/locales/en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | locales: 3 | en: "English" 4 | zh-CN: "简体中文" 5 | layouts: 6 | users_form: 7 | home: Home 8 | navbar: 9 | total_users: Total users 10 | users: 11 | signup: Sign up 12 | login: Login 13 | logout: Logout 14 | login_first: Login First 15 | has_already_logged_in: User has already logged in 16 | user_panel: 17 | likes: Likes 18 | settings: Settings 19 | check_profile: Check your profile 20 | edit: 21 | upload_avatar: Click and upload avatar 22 | my_page: My Page 23 | show: 24 | loading: Loading... 25 | created_at: Created at 26 | songs: Songs 27 | total_songs: Total Songs 28 | total_comments: Total Comments 29 | ago: ago 30 | users_count: "%{count}" 31 | songs_count: "%{count}" 32 | comments_count: "%{count}" 33 | no_user: The user does not exist 34 | create: 35 | successfully: Register successfully 36 | update: 37 | successfully: Profile is successfully saved 38 | faild: Profile save faild 39 | activate: 40 | activated: User was successfully activated 41 | sessions: 42 | new: 43 | remember_me: Remember me 44 | forget_password: Forget password 45 | invalid_email_password_combination: Invalid Email Password combination 46 | inactivation: User is inactivation 47 | create: 48 | successfully: Logged in successfully 49 | destroy: 50 | successfully: Logout successfully 51 | songs: 52 | form: 53 | title: Titile 54 | singer: Singer(Auto fetch) 55 | content: Content 56 | cant_be_blank: can't be blank 57 | passed: "Check passed, you can publish the song after filling the content" 58 | play_list: 59 | no_songs_yet: No songs yet 60 | songs: 61 | share_a_song: Share a song 62 | recommend_songs: Recommend songs 63 | loaded: All songs is loaded 64 | create: 65 | success: Publishing success 66 | faild: Publishing faild 67 | update: 68 | successfully: Successfully updated 69 | destroy: 70 | successfully: Song was successfully deleted 71 | cant_fetch: The information of song can't be fetch 72 | fetch_faild: " can't be fetch" 73 | comments: 74 | comments: Comments 75 | no_comments_yet: No comments yet 76 | notifications: 77 | index: 78 | notifications: Notifications 79 | empty_notifications: Empty notifications 80 | clearing: Clearing... 81 | notification: 82 | comment: 83 | add_a_comment_at: add a comment at 84 | mention: 85 | mention_you_at: mention you at 86 | password_resets: 87 | create: 88 | hint: You will receive an email with instructions about how to reset your password in a few minutes 89 | update: 90 | successfully_updated_password: Successfully updated password 91 | form: 92 | submit: Submit 93 | submitting: Submitting... 94 | save: Save 95 | are_you_sure: Are you sure? 96 | reset_password: Reset password 97 | mailer: 98 | subject: 99 | welcome: Welcome to the Ting 100 | reset_password: Reset Password 101 | done: Your account is confirmed 102 | activation: 103 | welcome: Welcome 104 | successfully_registered: You are successfully registered 105 | your_username: Your username is 106 | please_follow: Please follow 107 | this_link: " this link " 108 | complete: to complete the confirmation 109 | congratz: Congratulations 110 | you_can: Now you can 111 | access: " access to the site" 112 | enjoy: Enjoy it 113 | reset_password: 114 | header: "Hello, You have requested to reset your password" 115 | body: "To choose a new password, just follow" 116 | shared: 117 | error_messages: 118 | the_form_contains: The form contains 119 | permission_denied: Permission denied 120 | activerecord: 121 | errors: 122 | models: 123 | song: 124 | attributes: 125 | title: 126 | fetch: " can't be fetch" 127 | artist: 128 | fetch: " can't be fetch" 129 | attributes: 130 | user: 131 | name: "Name" 132 | email: "Email" 133 | password: "Password" 134 | password_confirmation: "Password confirmation" 135 | bio: "Bio" 136 | song: 137 | s_id: "Xiami ID " 138 | title: "Song title" 139 | artist: "Singer" 140 | content: "Content" 141 | pic: "Picture link" 142 | comment: 143 | content: "Content" -------------------------------------------------------------------------------- /config/locales/kaminari.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | views: 3 | pagination: 4 | first: "« First" 5 | last: "Last »" 6 | previous: "‹ Prev" 7 | next: "Next ›" 8 | truncate: "…" 9 | helpers: 10 | page_entries_info: 11 | one_page: 12 | display_entries: 13 | zero: "No %{entry_name} found" 14 | one: "Displaying 1 %{entry_name}" 15 | other: "Displaying all %{count} %{entry_name}" 16 | more_pages: 17 | display_entries: "Displaying %{entry_name} %{first} - %{last} of %{total} in total" -------------------------------------------------------------------------------- /config/locales/kaminari.zh-CN.yml: -------------------------------------------------------------------------------- 1 | zh-CN: 2 | views: 3 | pagination: 4 | first: "« 第一页" 5 | last: "最后一页 »" 6 | previous: "‹ 前一页" 7 | next: "下一页 ›" 8 | truncate: "…" 9 | helpers: 10 | page_entries_info: 11 | one_page: 12 | display_entries: 13 | zero: "%{entry_name} 未找到" 14 | one: "displaying 1 %{entry_name}" 15 | other: "displaying all %{count} %{entry_name}" 16 | more_pages: 17 | display_entries: "displaying %{entry_name} %{first} - %{last} of %{total} in total" -------------------------------------------------------------------------------- /config/locales/zh-CN.yml: -------------------------------------------------------------------------------- 1 | zh-CN: 2 | locales: 3 | en: "English" 4 | zh-CN: "简体中文" 5 | layouts: 6 | users_form: 7 | home: 主页 8 | navbar: 9 | total_users: 注册用户 10 | users: 11 | signup: 注册 12 | login: 登录 13 | logout: 退出 14 | login_first: 请先登录 15 | has_already_logged_in: 用户已经登录 16 | user_panel: 17 | likes: 喜欢 18 | settings: 设置 19 | check_profile: 查看个人信息 20 | edit: 21 | upload_avatar: 点击上传头像 22 | my_page: 个人主页 23 | show: 24 | loading: 加载中... 25 | created_at: 创建时间 26 | songs: 歌曲 27 | total_songs: 分享歌曲 28 | total_comments: 发表评论 29 | ago: "" 30 | users_count: "%{count} 位" 31 | songs_count: "%{count} 首" 32 | comments_count: "%{count} 条" 33 | no_user: 该用户不存在 34 | create: 35 | successfully: 注册成功 36 | update: 37 | successfully: 资料保存成功 38 | faild: 资料保存失败 39 | activate: 40 | activated: 验证成功 41 | sessions: 42 | new: 43 | remember_me: 记住我 44 | forget_password: 忘记密码 45 | invalid_email_password_combination: 邮箱或密码无效 46 | inactivation: 用户尚未通过验证 47 | create: 48 | successfully: 登录成功 49 | destroy: 50 | successfully: 成功退出 51 | songs: 52 | form: 53 | title: 歌曲名 54 | singer: 歌手信息(自动抓取) 55 | content: 分享内容 56 | cant_be_blank: 不能为空 57 | passed: "检测通过, 填写分享内容后即可发布" 58 | play_list: 59 | no_songs_yet: 没有相关歌曲 60 | songs: 61 | share_a_song: 分享歌曲 62 | recommend_songs: "你还没有喜欢过任何歌曲, 下面歌曲由系统自动推荐" 63 | loaded: 全部歌曲加载完毕 64 | create: 65 | success: 成功发布 66 | faild: 发布失败 67 | update: 68 | successfully: 更新成功 69 | destroy: 70 | successfully: 删除成功 71 | cant_fetch: 未获取到该歌曲的相关信息 72 | fetch_faild: 抓取失败 73 | comments: 74 | comments: 所有评论 75 | no_comments_yet: 暂无评论 76 | notifications: 77 | index: 78 | notifications: 通知 79 | empty_notifications: 清空通知 80 | clearing: 正在清空... 81 | notification: 82 | comment: 83 | add_a_comment_at: 添加评论于 84 | mention: 85 | mention_you_at: 提及您于 86 | password_resets: 87 | create: 88 | hint: 您将在几分钟内收到重置密码的指令 89 | update: 90 | successfully_updated_password: 密码更新成功 91 | form: 92 | submit: 提交 93 | submitting: 提交中... 94 | save: 保存 95 | are_you_sure: 确认删除? 96 | reset_password: 重置密码 97 | mailer: 98 | subject: 99 | welcome: 欢迎来到 Ting 100 | reset_password: 重置密码 101 | done: 你的账号已经完成激活 102 | activation: 103 | welcome: 欢迎您 104 | successfully_registered: 您已经成功注册 105 | your_username: 您的用户名是 106 | please_follow: 请使用 107 | this_link: 该链接 108 | complete: 完成验证 109 | congratz: 恭喜您 110 | you_can: 你现在可以 111 | access: 进入网站 112 | enjoy: 尽情享受音乐带来的美妙 113 | reset_password: 114 | header: "您好, 您正在请求重置密码" 115 | body: "重置密码请使用" 116 | shared: 117 | error_messages: 118 | the_form_contains: 表单包含 119 | permission_denied: 无权访问 120 | activerecord: 121 | errors: 122 | models: 123 | song: 124 | attributes: 125 | title: 126 | fetch: 抓取失败 127 | artist: 128 | fetch: 抓取失败 129 | attributes: 130 | user: 131 | name: 名称 132 | email: 邮箱 133 | password: 密码 134 | password_confirmation: 确认密码 135 | bio: 个人说明 136 | song: 137 | s_id: "虾米 ID " 138 | title: 歌曲名称 139 | artist: 歌手 140 | content: 分享内容 141 | pic: 图片链接 142 | comment: 143 | content: 评论内容 -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | 3 | root 'songs#index' 4 | resources :sessions, only: [ :new, :create, :destroy ] 5 | resources :users, only: [ :new, :create, :update, :edit, :show ] do 6 | member do 7 | get :activate 8 | end 9 | end 10 | resources :songs do 11 | resources :comments, only: [ :create, :destroy ] 12 | end 13 | resource :likeship, only: [ :create, :destroy ] 14 | resources :notifications, only: [ :index, :destroy ] do 15 | collection do 16 | post :count 17 | delete :clear 18 | end 19 | end 20 | resources :password_resets, only: [ :new, :create, :edit, :update] 21 | get 'login', to: 'sessions#new', as: :login 22 | get 'signup', to: 'users#new', as: :signup 23 | delete 'logout', to: 'sessions#destroy', as: :logout 24 | get 'collect', to: 'songs#collect', as: :collect 25 | get 'user_songs', to: 'users#user_songs', as: :user_songs 26 | get 'favorite_songs', to: 'users#favorite_songs', as: :favorite 27 | get 'recent_comments', to: 'users#recent_comments', as: :recent_comments 28 | get 'language', to: 'users#language', as: :set_locale 29 | # The priority is based upon order of creation: first created -> highest priority. 30 | # See how all your routes lay out with "rake routes". 31 | 32 | # You can have the root of your site routed with "root" 33 | # root 'welcome#index' 34 | 35 | # Example of regular route: 36 | # get 'products/:id' => 'catalog#view' 37 | 38 | # Example of named route that can be invoked with purchase_url(id: product.id) 39 | # get 'products/:id/purchase' => 'catalog#purchase', as: :purchase 40 | 41 | # Example resource route (maps HTTP verbs to controller actions automatically): 42 | # resources :products 43 | 44 | # Example resource route with options: 45 | # resources :products do 46 | # member do 47 | # get 'short' 48 | # post 'toggle' 49 | # end 50 | # 51 | # collection do 52 | # get 'sold' 53 | # end 54 | # end 55 | 56 | # Example resource route with sub-resources: 57 | # resources :products do 58 | # resources :comments, :sales 59 | # resource :seller 60 | # end 61 | 62 | # Example resource route with more complex sub-resources: 63 | # resources :products do 64 | # resources :comments 65 | # resources :sales do 66 | # get 'recent', on: :collection 67 | # end 68 | # end 69 | 70 | # Example resource route with concerns: 71 | # concern :toggleable do 72 | # post 'toggle' 73 | # end 74 | # resources :posts, concerns: :toggleable 75 | # resources :photos, concerns: :toggleable 76 | 77 | # Example resource route within a namespace: 78 | # namespace :admin do 79 | # # Directs /admin/products/* to Admin::ProductsController 80 | # # (app/controllers/admin/products_controller.rb) 81 | # resources :products 82 | # end 83 | end 84 | -------------------------------------------------------------------------------- /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: fad025dab989623db1cee16e26e670938583c91f05350fe016ba360adb5049adef3e4834780e13ee55bdbdf791099feee04830139476c736694118572f0a3bf0 15 | 16 | test: 17 | secret_key_base: fad025dab989623db1cee16e26e670938583c91f05350fe016ba360adb5049adef3e4834780e13ee55bdbdf791099feee04830139476c736694118572f0a3bf0 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"] %> -------------------------------------------------------------------------------- /db/migrate/20141206123135_sorcery_core.rb: -------------------------------------------------------------------------------- 1 | class SorceryCore < ActiveRecord::Migration 2 | def change 3 | create_table :users do |t| 4 | t.string :name 5 | t.string :email, :null => false 6 | t.string :crypted_password, :null => false 7 | t.string :salt, :null => false 8 | t.string :avatar 9 | t.string :bio 10 | t.string :locale 11 | 12 | t.timestamps 13 | end 14 | 15 | add_index :users, :email, unique: true 16 | end 17 | end -------------------------------------------------------------------------------- /db/migrate/20141206123136_sorcery_remember_me.rb: -------------------------------------------------------------------------------- 1 | class SorceryRememberMe < ActiveRecord::Migration 2 | def change 3 | add_column :users, :remember_me_token, :string, :default => nil 4 | add_column :users, :remember_me_token_expires_at, :datetime, :default => nil 5 | 6 | add_index :users, :remember_me_token 7 | end 8 | end -------------------------------------------------------------------------------- /db/migrate/20141206123137_sorcery_reset_password.rb: -------------------------------------------------------------------------------- 1 | class SorceryResetPassword < ActiveRecord::Migration 2 | def change 3 | add_column :users, :reset_password_token, :string, :default => nil 4 | add_column :users, :reset_password_token_expires_at, :datetime, :default => nil 5 | add_column :users, :reset_password_email_sent_at, :datetime, :default => nil 6 | 7 | add_index :users, :reset_password_token 8 | end 9 | end -------------------------------------------------------------------------------- /db/migrate/20141208045147_create_songs.rb: -------------------------------------------------------------------------------- 1 | class CreateSongs < ActiveRecord::Migration 2 | def change 3 | create_table :songs do |t| 4 | t.integer :s_id 5 | t.string :title 6 | t.string :artist 7 | t.string :pic 8 | t.text :content 9 | t.integer :user_id 10 | t.integer :comments_count, default: 0 11 | 12 | t.timestamps 13 | end 14 | add_index :songs, [:user_id, :created_at] 15 | end 16 | end -------------------------------------------------------------------------------- /db/migrate/20141212155258_create_comments.rb: -------------------------------------------------------------------------------- 1 | class CreateComments < ActiveRecord::Migration 2 | def change 3 | create_table :comments do |t| 4 | t.text :content 5 | t.integer :song_id 6 | t.integer :user_id 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20141213092401_create_likeships.rb: -------------------------------------------------------------------------------- 1 | class CreateLikeships < ActiveRecord::Migration 2 | def change 3 | create_table :likeships do |t| 4 | t.integer :user_id 5 | t.integer :likeable_id 6 | t.string :likeable_type 7 | 8 | t.timestamps 9 | end 10 | add_index :likeships, [:user_id, :likeable_id, :likeable_type], unique: true 11 | end 12 | end -------------------------------------------------------------------------------- /db/migrate/20141214004426_create_notifications.rb: -------------------------------------------------------------------------------- 1 | class CreateNotifications < ActiveRecord::Migration 2 | def change 3 | create_table :notifications do |t| 4 | t.belongs_to :user, index: true 5 | t.belongs_to :subject, polymorphic: true, index: true 6 | t.string :name 7 | t.boolean :read, default: false 8 | 9 | t.timestamps 10 | end 11 | end 12 | end -------------------------------------------------------------------------------- /db/migrate/20141223012634_sorcery_user_activation.rb: -------------------------------------------------------------------------------- 1 | class SorceryUserActivation < ActiveRecord::Migration 2 | def change 3 | add_column :users, :activation_state, :string, :default => nil 4 | add_column :users, :activation_token, :string, :default => nil 5 | add_column :users, :activation_token_expires_at, :datetime, :default => nil 6 | 7 | add_index :users, :activation_token 8 | end 9 | end -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | # This file is auto-generated from the current state of the database. Instead 3 | # of editing this file, please use the migrations feature of Active Record to 4 | # incrementally modify your database, and then regenerate this schema definition. 5 | # 6 | # Note that this schema.rb definition is the authoritative source for your 7 | # database schema. If you need to create the application database on another 8 | # system, you should be using db:schema:load, not running all the migrations 9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 10 | # you'll amass, the slower it'll run and the greater likelihood for issues). 11 | # 12 | # It's strongly recommended that you check this file into your version control system. 13 | 14 | ActiveRecord::Schema.define(version: 20141229142842) do 15 | 16 | create_table "comments", force: true do |t| 17 | t.text "content" 18 | t.integer "song_id" 19 | t.integer "user_id" 20 | t.datetime "created_at" 21 | t.datetime "updated_at" 22 | end 23 | 24 | create_table "likeships", force: true do |t| 25 | t.integer "user_id" 26 | t.integer "likeable_id" 27 | t.string "likeable_type" 28 | t.datetime "created_at" 29 | t.datetime "updated_at" 30 | end 31 | 32 | add_index "likeships", ["user_id", "likeable_id", "likeable_type"], name: "index_likeships_on_user_id_and_likeable_id_and_likeable_type", unique: true 33 | 34 | create_table "notifications", force: true do |t| 35 | t.integer "user_id" 36 | t.integer "subject_id" 37 | t.string "subject_type" 38 | t.string "name" 39 | t.boolean "read", default: false 40 | t.datetime "created_at" 41 | t.datetime "updated_at" 42 | end 43 | 44 | add_index "notifications", ["subject_id", "subject_type"], name: "index_notifications_on_subject_id_and_subject_type" 45 | add_index "notifications", ["user_id"], name: "index_notifications_on_user_id" 46 | 47 | create_table "songs", force: true do |t| 48 | t.integer "s_id" 49 | t.string "title" 50 | t.string "artist" 51 | t.string "pic" 52 | t.text "content" 53 | t.integer "user_id" 54 | t.integer "comments_count", default: 0 55 | t.datetime "created_at" 56 | t.datetime "updated_at" 57 | end 58 | 59 | add_index "songs", ["user_id", "created_at"], name: "index_songs_on_user_id_and_created_at" 60 | 61 | create_table "users", force: true do |t| 62 | t.string "name" 63 | t.string "email", null: false 64 | t.string "crypted_password", null: false 65 | t.string "salt", null: false 66 | t.string "avatar" 67 | t.string "bio" 68 | t.string "douban" 69 | t.datetime "created_at" 70 | t.datetime "updated_at" 71 | t.string "remember_me_token" 72 | t.datetime "remember_me_token_expires_at" 73 | t.string "reset_password_token" 74 | t.datetime "reset_password_token_expires_at" 75 | t.datetime "reset_password_email_sent_at" 76 | t.string "activation_state" 77 | t.string "activation_token" 78 | t.datetime "activation_token_expires_at" 79 | t.string "locale" 80 | end 81 | 82 | add_index "users", ["activation_token"], name: "index_users_on_activation_token" 83 | add_index "users", ["email"], name: "index_users_on_email", unique: true 84 | add_index "users", ["remember_me_token"], name: "index_users_on_remember_me_token" 85 | add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token" 86 | 87 | end 88 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aufree/ting/66631ffdecf0d4752243a7a2d57cf3e9bc160a62/lib/assets/.keep -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aufree/ting/66631ffdecf0d4752243a7a2d57cf3e9bc160a62/lib/tasks/.keep -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aufree/ting/66631ffdecf0d4752243a7a2d57cf3e9bc160a62/test/controllers/.keep -------------------------------------------------------------------------------- /test/controllers/comments_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class CommentsControllerTest < ActionController::TestCase 4 | def setup 5 | @comment = comments(:one) 6 | @other_comment = comments(:two) 7 | songs(:one) 8 | end 9 | 10 | test "should be valid" do 11 | assert @comment.valid? 12 | end 13 | 14 | test "should redierct create when not logged in" do 15 | assert_no_difference "Comment.count" do 16 | post :create, comment: @comment.attributes, song_id: @comment.song_id 17 | end 18 | assert_redirected_to login_url 19 | end 20 | 21 | test "should redierct destroy when not logged in" do 22 | assert_no_difference "Comment.count" do 23 | delete :destroy, id: @comment, song_id: @comment.song_id 24 | end 25 | assert_redirected_to login_url 26 | end 27 | 28 | test "should redirect destroy for wrong comment" do 29 | login_user(users(:paul)) 30 | assert_no_difference 'Comment.count' do 31 | assert_raise(ActiveRecord::RecordNotFound) do 32 | delete :destroy, id: @other_comment, song_id: @comment.song_id 33 | end 34 | end 35 | end 36 | 37 | test "should redirect destroy when comment successfully destroyed" do 38 | login_user(@comment.user) 39 | assert_difference 'Comment.count', -1 do 40 | delete :destroy, id: @comment, song_id: @comment.song_id 41 | end 42 | assert_redirected_to @comment.song 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /test/controllers/likeships_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class LikeshipsControllerTest < ActionController::TestCase 4 | def setup 5 | @song = songs(:one) 6 | @paul_likeship = likeships(:one) 7 | @paul_likeship.id = @song.id 8 | @foo_likeship = likeships(:two) 9 | end 10 | 11 | test "should redirect create when not logged in" do 12 | assert_no_difference 'Likeship.count' do 13 | post :create, likeship: @paul_likeship.attributes 14 | end 15 | assert_redirected_to login_url 16 | end 17 | 18 | test "should redirect destroy when not logged in" do 19 | assert_no_difference "Likeship.count" do 20 | unlike!(@paul_likeship) 21 | end 22 | assert_redirected_to login_url 23 | end 24 | 25 | test "should create when user logged in" do 26 | login_user(users(:paul)) 27 | assert_difference "Likeship.count", +1 do 28 | like!(@paul_likeship) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /test/controllers/notifications_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class NotificationsControllerTest < ActionController::TestCase 4 | test "should redirect index when not logged in" do 5 | get :index 6 | assert_redirected_to login_url 7 | end 8 | 9 | test "should update attribute after current user get index" do 10 | login_user(users(:paul)) 11 | get :index 12 | assert_response :success 13 | assert_equal true, notifications(:one).read 14 | end 15 | 16 | test "should redirect destroy for wrong user" do 17 | login_user(users(:paul)) 18 | assert_raise(ActiveRecord::RecordNotFound) do 19 | xhr :delete, :destroy, id: notifications(:two) 20 | end 21 | end 22 | 23 | test "should destroy for correct user" do 24 | login_user(users(:paul)) 25 | assert_difference "Notification.count", -1 do 26 | xhr :delete, :destroy, id: notifications(:one) 27 | end 28 | end 29 | 30 | test "should destroy all notifications" do 31 | user = users(:paul) 32 | login_user(user) 33 | xhr :delete, :clear 34 | assert_equal 0, user.notifications.count 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /test/controllers/password_resets_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PasswordResetsControllerTest < ActionController::TestCase 4 | 5 | end 6 | -------------------------------------------------------------------------------- /test/controllers/sessions_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SessionsControllerTest < ActionController::TestCase 4 | test "should get new" do 5 | get :new 6 | assert_response :success 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /test/controllers/songs_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SongsControllerTest < ActionController::TestCase 4 | 5 | def setup 6 | @song = songs(:one) 7 | @other_song = songs(:two) 8 | end 9 | 10 | test "shoud redirect create when user not loged in" do 11 | assert_no_difference 'Song.count' do 12 | post :create, song: { s_id: 1, content: "lalala" } 13 | end 14 | assert_redirected_to login_url 15 | end 16 | 17 | test "should redirect destroy when user not loged in" do 18 | assert_no_difference "Song.count" do 19 | delete :destroy, id: @song 20 | end 21 | assert_redirected_to login_url 22 | end 23 | 24 | test "should redirect collect when user not loged in" do 25 | get :collect 26 | assert_redirected_to login_url 27 | end 28 | 29 | test "should redirect edit for wrong song" do 30 | login_user(users(:paul)) 31 | get :edit, id: @other_song 32 | assert_not flash.empty? 33 | assert_redirected_to root_path 34 | end 35 | 36 | test "should redirect update for wrong song" do 37 | login_user(users(:paul)) 38 | patch :update, id: @other_song, song: { content: "@other_song.content" } 39 | assert_not flash.empty? 40 | assert_redirected_to root_path 41 | end 42 | 43 | test "should destroy for wrong song" do 44 | login_user(users(:paul)) 45 | assert_no_difference "Song.count" do 46 | delete :destroy, id: @other_song 47 | end 48 | assert_redirected_to root_path 49 | end 50 | 51 | end -------------------------------------------------------------------------------- /test/controllers/users_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UsersControllerTest < ActionController::TestCase 4 | 5 | def setup 6 | @user = users(:paul) 7 | @other_user = users(:foo) 8 | end 9 | 10 | test "new" do 11 | get :new 12 | assert_response :success 13 | end 14 | 15 | test "should redirect edit when not logined in" do 16 | get :edit, id: @user 17 | assert_not flash.empty? 18 | assert_redirected_to login_url 19 | end 20 | 21 | test "should redirect update when not logined in" do 22 | patch :update, id: @user, user: { name: @user.name, email: @user.email, bio: @user.bio } 23 | assert_not flash.empty? 24 | assert_redirected_to login_url 25 | end 26 | 27 | test "should redirect edit when user successfully update profile" do 28 | login_user(@user) 29 | patch :update, id: @user, user: { name: @user.name, email: @user.email, bio: @user.bio } 30 | assert_not flash.empty? 31 | assert_redirected_to root_url 32 | end 33 | 34 | test "should redirect new when user already logined in" do 35 | login_user(@user) 36 | get :new 37 | assert_not flash.empty? 38 | assert_redirected_to root_url 39 | end 40 | 41 | test 'should redirect edit when user logined in as wrong user' do 42 | login_user(@other_user) 43 | get :edit, id: @user 44 | assert_not flash.empty? 45 | assert_redirected_to root_url 46 | end 47 | 48 | test "should redirect update when user logined in as wrong user" do 49 | login_user(@other_user) 50 | patch :update, id: @user, user: { name: @user.name, email: @user.email, bio: @user.bio } 51 | assert_not flash.empty? 52 | assert_redirected_to root_url 53 | end 54 | 55 | end 56 | -------------------------------------------------------------------------------- /test/fixtures/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aufree/ting/66631ffdecf0d4752243a7a2d57cf3e9bc160a62/test/fixtures/.keep -------------------------------------------------------------------------------- /test/fixtures/comments.yml: -------------------------------------------------------------------------------- 1 | one: 2 | content: "comment one" 3 | user: paul 4 | song: one 5 | 6 | two: 7 | content: "comment two" 8 | user: foo 9 | song: two -------------------------------------------------------------------------------- /test/fixtures/likeships.yml: -------------------------------------------------------------------------------- 1 | one: 2 | likeable_type: Song 3 | likeable_id: 1 4 | two: 5 | likeable_type: Song 6 | likeable_id: 1 -------------------------------------------------------------------------------- /test/fixtures/notifications.yml: -------------------------------------------------------------------------------- 1 | one: 2 | user: paul 3 | name: "mention" 4 | subject: one (Comment) 5 | two: 6 | user: foo 7 | name: "comment" 8 | subject: one (Comment) -------------------------------------------------------------------------------- /test/fixtures/songs.yml: -------------------------------------------------------------------------------- 1 | one: 2 | s_id: 1 3 | title: "Hello World" 4 | artist: "paul" 5 | pic: "http://example.com/dog.png" 6 | content: "Good music" 7 | user: paul 8 | 9 | two: 10 | s_id: 2 11 | title: "Cool" 12 | artist: "foo" 13 | pic: "http://example.com/dog.png" 14 | content: "I love this music" 15 | user: foo 16 | -------------------------------------------------------------------------------- /test/fixtures/users.yml: -------------------------------------------------------------------------------- 1 | paul: 2 | name: paul 3 | email: paul@example.com 4 | salt: <%= salt = "asdasdastr4325234324sdfds" %> 5 | crypted_password: <%= Sorcery::CryptoProviders::BCrypt.encrypt("secret", salt) %> 6 | bio: "Fxxk GFW" 7 | 8 | foo: 9 | name: foo 10 | email: foo@example.com 11 | salt: <%= salt = "asdasdastr4325234324sdfds" %> 12 | crypted_password: <%= Sorcery::CryptoProviders::BCrypt.encrypt("secret", salt) %> 13 | bio: "Fxxk GFW" -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aufree/ting/66631ffdecf0d4752243a7a2d57cf3e9bc160a62/test/helpers/.keep -------------------------------------------------------------------------------- /test/helpers/comments_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class CommentsHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/helpers/likeships_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class LikeshipsHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/helpers/notifications_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class NotificationsHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/helpers/password_resets_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PasswordResetsHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/helpers/sessions_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SessionsHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/helpers/songs_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SongsHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/helpers/users_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UsersHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aufree/ting/66631ffdecf0d4752243a7a2d57cf3e9bc160a62/test/integration/.keep -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aufree/ting/66631ffdecf0d4752243a7a2d57cf3e9bc160a62/test/mailers/.keep -------------------------------------------------------------------------------- /test/mailers/previews/user_mailer_preview.rb: -------------------------------------------------------------------------------- 1 | # Preview all emails at http://localhost:3000/rails/mailers/user_mailer 2 | class UserMailerPreview < ActionMailer::Preview 3 | # def reset_password_email 4 | # @user = User.first 5 | # UserMailer.reset_password_email(@user) 6 | # end 7 | 8 | # http://localhost:3000/rails/mailers/user_mailer/activation_needed_email 9 | def activation_needed_email 10 | @user = User.first 11 | UserMailer.activation_needed_email(@user) 12 | end 13 | 14 | # http://localhost:3000/rails/mailers/user_mailer/activation_success_email 15 | def activation_success_email 16 | @user = User.first 17 | UserMailer.activation_success_email(@user) 18 | end 19 | 20 | def reset_password_email 21 | @user = User.first 22 | UserMailer.reset_password_email(@user) 23 | end 24 | end -------------------------------------------------------------------------------- /test/mailers/user_mailer_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UserMailerTest < ActionMailer::TestCase 4 | 5 | end 6 | -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aufree/ting/66631ffdecf0d4752243a7a2d57cf3e9bc160a62/test/models/.keep -------------------------------------------------------------------------------- /test/models/comment_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class CommentTest < ActiveSupport::TestCase 4 | def setup 5 | @comment = comments(:one) 6 | end 7 | 8 | test "should be valid" do 9 | assert @comment.valid? 10 | end 11 | 12 | test "user id should be present" do 13 | @comment.user_id = nil 14 | assert_not @comment.valid? 15 | end 16 | 17 | test "song id should be present" do 18 | @comment.song_id = nil 19 | assert_not @comment.valid? 20 | end 21 | 22 | test "content should be present" do 23 | @comment.content = " " 24 | assert_not @comment.valid? 25 | end 26 | 27 | test 'should notify at_users' do 28 | count = Notification.count 29 | user = users(:foo) 30 | @comment.content = "@#{@comment.song.user.name}" 31 | @comment.user = user 32 | @comment.dup.save! 33 | assert_equal count + 1, Notification.count 34 | end 35 | 36 | test 'should notify musician' do 37 | count = Notification.count 38 | user = users(:foo) 39 | @comment.user = user 40 | @comment.dup.save! 41 | assert_equal count + 1, Notification.count 42 | end 43 | 44 | test 'should not notify musician' do 45 | count = Notification.count 46 | @comment.dup.save! 47 | assert_equal count, Notification.count 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /test/models/likeship_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class LikeshipTest < ActiveSupport::TestCase 4 | def setup 5 | @user = users(:paul) 6 | @like_song = @user.likeships.new(likeable_type: "Song", likeable_id: 1) 7 | @like_comment = @user.likeships.new(likeable_type: "Comment", likeable_id: 2) 8 | end 9 | 10 | test "should be valid" do 11 | assert @like_song.valid? 12 | assert @like_comment.valid? 13 | end 14 | 15 | test "user id should be present" do 16 | @like_song.user_id = nil 17 | assert_not @like_song.valid? 18 | end 19 | 20 | test "likeable type should be present" do 21 | @like_song.likeable_type = nil 22 | assert_not @like_song.valid? 23 | end 24 | 25 | test "likeable id should be present" do 26 | @like_song.likeable_id = nil 27 | assert_not @like_song.valid? 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /test/models/notification_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class NotificationTest < ActiveSupport::TestCase 4 | test "should decrease notification count" do 5 | assert_difference "Notification.count", -1 do 6 | notifications(:one).destroy 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /test/models/song_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SongTest < ActiveSupport::TestCase 4 | def setup 5 | @user = users(:paul) 6 | @song = @user.songs.build(s_id: 3, 7 | title: "梦的列车", 8 | artist: "Alex", 9 | pic: "http://img.xiami.net/images/album/img1/1/11383201444.jpg", 10 | content: "Good music") 11 | end 12 | 13 | test "shuold be valid" do 14 | assert @song.valid? 15 | end 16 | 17 | test "s_id should not be blank" do 18 | assert_raise(OpenURI::HTTPError) do 19 | @song.s_id = " " 20 | assert_not @song.valid? 21 | end 22 | end 23 | 24 | test "content should not be blank" do 25 | @song.content = " " 26 | assert_not @song.valid? 27 | end 28 | 29 | test "s_id should greater than 0" do 30 | @song.s_id = 0 31 | assert_not @song.valid? 32 | end 33 | 34 | test "content should have maximum length" do 35 | @song.content = "a" * 10001 36 | assert_not @song.valid? 37 | end 38 | 39 | test "associated comments should be destroyed" do 40 | comment = comments(:one) 41 | assert_difference 'Comment.count', -1 do 42 | comment.song.destroy 43 | end 44 | end 45 | end -------------------------------------------------------------------------------- /test/models/user_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @user = User.new(name: "Aufree", email: "user@example.com", 7 | password: "foobar", password_confirmation: "foobar") 8 | end 9 | 10 | test "should be valid" do 11 | assert @user.valid? 12 | end 13 | 14 | test "name should be present" do 15 | @user.name = " " 16 | assert_not @user.valid? 17 | end 18 | 19 | test "email should be present" do 20 | @user.email = " " 21 | assert_not @user.valid? 22 | end 23 | 24 | test "name should not be too long" do 25 | @user.name = "a" * 51 26 | assert_not @user.valid? 27 | end 28 | 29 | test "bio should not be too long" do 30 | @user.bio = "a" * 141 31 | assert_not @user.valid? 32 | end 33 | 34 | test "name validation should accept valid format" do 35 | invalid_names = %w["au free", "张三"] 36 | invalid_names.each do |invalid_name| 37 | @user.name = invalid_name 38 | assert_not @user.valid? 39 | end 40 | end 41 | 42 | test "email validation should accept valid addresses" do 43 | valid_addresses = %w[user@example.com USER@foo.COM A_US-ER@foo.bar.org 44 | first.last@foo.jp alice+bob@baz.cn] 45 | valid_addresses.each do |valid_address| 46 | @user.email = valid_address 47 | assert @user.valid?, "#{valid_address.inspect} should be valid" 48 | end 49 | end 50 | 51 | test "email validation should reject invalid addresses" do 52 | invalid_addresses = %w[user@example,com user_at_foo.org user.name@example. 53 | foo@bar_baz.com foo@bar+baz.com] 54 | invalid_addresses.each do |invalid_address| 55 | @user.email = invalid_address 56 | assert_not @user.valid?, "#{invalid_address.inspect} should be invalid" 57 | end 58 | end 59 | 60 | test "user name should be unique" do 61 | duplicate_user = @user.dup 62 | duplicate_user.name = @user.name.upcase 63 | @user.save 64 | assert_not duplicate_user.valid? 65 | end 66 | 67 | test "email address should be unique" do 68 | duplicate_user = @user.dup 69 | duplicate_user.email = @user.email.upcase 70 | @user.save 71 | assert_not duplicate_user.valid? 72 | end 73 | 74 | test "password should have a minimum length" do 75 | @user.password = @user.password_confirmation = "a" * 5 76 | assert_not @user.valid? 77 | end 78 | 79 | test "password should have a maximum length" do 80 | @user.password = @user.password_confirmation = "a" * 17 81 | assert_not @user.valid? 82 | end 83 | 84 | test "bio should have a maximum length" do 85 | @user.bio = "a" * 141 86 | assert_not @user.valid? 87 | end 88 | 89 | # test "associated songs should be destroyed" do 90 | # @user.save 91 | # @user.songs.create!(s_id: 100, title: "lorem ipsum", artist: "k", content: "cool") 92 | # assert_difference 'Song.count', -1 do 93 | # @user.destroy 94 | # end 95 | # end 96 | 97 | test "associated comments should be destroyed" do 98 | comment = comments(:one) 99 | assert_difference 'Comment.count', -1 do 100 | comment.user.destroy 101 | end 102 | end 103 | 104 | end 105 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | require File.expand_path('../../config/environment', __FILE__) 3 | require 'rails/test_help' 4 | require 'minitest/reporters' 5 | Minitest::Reporters.use! 6 | include Sorcery::TestHelpers::Rails::Controller 7 | 8 | class ActiveSupport::TestCase 9 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 10 | fixtures :all 11 | 12 | def like!(likeship) 13 | post :create, 14 | likeable_type: likeship.likeable_type, 15 | likeable_id: likeship.id 16 | end 17 | 18 | def unlike!(likeship) 19 | delete :destroy, 20 | likeable_type: likeship.likeable_type, 21 | likeable_id: likeship.id 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aufree/ting/66631ffdecf0d4752243a7a2d57cf3e9bc160a62/vendor/assets/javascripts/.keep -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Aufree/ting/66631ffdecf0d4752243a7a2d57cf3e9bc160a62/vendor/assets/stylesheets/.keep --------------------------------------------------------------------------------