├── .gitignore
├── .rspec
├── Gemfile
├── Gemfile.lock
├── Guardfile
├── LICENSE
├── README.md
├── Rakefile
├── app
├── assets
│ ├── images
│ │ └── rails.png
│ ├── javascripts
│ │ ├── application.js
│ │ ├── sessions.js.coffee
│ │ ├── static_pages.js.coffee
│ │ └── users.js.coffee
│ └── stylesheets
│ │ ├── application.css
│ │ ├── custom.css.scss
│ │ ├── sessions.css.scss
│ │ ├── static_pages.css.scss
│ │ └── users.css.scss
├── controllers
│ ├── application_controller.rb
│ ├── concerns
│ │ ├── .keep
│ │ └── signed_in_user.rb
│ ├── microposts_controller.rb
│ ├── relationships_controller.rb
│ ├── sessions_controller.rb
│ ├── static_pages_controller.rb
│ ├── users
│ │ ├── followers_controller.rb
│ │ └── following_controller.rb
│ └── users_controller.rb
├── decorators
│ └── micropost_decorator.rb
├── helpers
│ ├── application_helper.rb
│ ├── sessions_helper.rb
│ ├── static_pages_helper.rb
│ └── users_helper.rb
├── mailers
│ └── .keep
├── models
│ ├── .keep
│ ├── concerns
│ │ ├── .keep
│ │ └── emailable.rb
│ ├── micropost.rb
│ ├── relationship.rb
│ └── user.rb
├── presenters
│ └── users
│ │ ├── follow_presenter.rb
│ │ ├── followed_users_presenter.rb
│ │ └── followers_presenter.rb
└── views
│ ├── layouts
│ ├── _footer.html.erb
│ ├── _header.html.erb
│ ├── _shim.html.erb
│ └── application.html.erb
│ ├── microposts
│ └── _micropost.html.erb
│ ├── relationships
│ ├── create.js.erb
│ └── destroy.js.erb
│ ├── sessions
│ └── new.html.erb
│ ├── shared
│ ├── _error_messages.html.erb
│ ├── _feed.html.erb
│ ├── _feed_item.html.erb
│ ├── _micropost_form.html.erb
│ ├── _stats.html.erb
│ └── _user_info.html.erb
│ ├── static_pages
│ ├── _contact_justin.html.erb
│ ├── about.html.erb
│ ├── contact.html.erb
│ ├── help.html.erb
│ ├── home.html.erb
│ └── show.html.erb
│ └── users
│ ├── _follow.html.erb
│ ├── _follow_form.html.erb
│ ├── _unfollow.html.erb
│ ├── _user.html.erb
│ ├── edit.html.erb
│ ├── index.html.erb
│ ├── new.html.erb
│ ├── show.html.erb
│ └── show_follow.html.erb
├── bin
├── bundle
├── git-railsconf.zsh
├── rails
└── rake
├── config.ru
├── config
├── application.rb
├── boot.rb
├── cucumber.yml
├── database.yml.example
├── environment.rb
├── environments
│ ├── development.rb
│ ├── production.rb
│ └── test.rb
├── initializers
│ ├── backtrace_silencers.rb
│ ├── filter_parameter_logging.rb
│ ├── inflections.rb
│ ├── mime_types.rb
│ ├── secret_token.rb
│ ├── session_store.rb
│ └── wrap_parameters.rb
├── locales
│ └── en.yml
└── routes.rb
├── db
├── migrate
│ ├── 20130311191400_create_users.rb
│ ├── 20130311194153_add_index_to_users_email.rb
│ ├── 20130311201841_add_password_digest_to_users.rb
│ ├── 20130314184954_add_remember_token_to_users.rb
│ ├── 20130315015932_add_admin_to_users.rb
│ ├── 20130315175534_create_microposts.rb
│ ├── 20130315230445_create_relationships.rb
│ └── 20140405044857_add_profanity_counter_to_user.rb
├── schema.rb
└── seeds.rb
├── features
├── signing_in.feature
├── step_definitions
│ └── authentication_steps.rb
└── support
│ └── env.rb
├── lib
├── assets
│ └── .keep
└── tasks
│ ├── .keep
│ ├── cucumber.rake
│ └── sample_data.rake
├── log
└── .keep
├── public
├── 404.html
├── 422.html
├── 500.html
├── assets
│ ├── application-4962059d8f80f9bb096692bacc29c4e8.css
│ ├── application-4962059d8f80f9bb096692bacc29c4e8.css.gz
│ ├── application-eeb856e3fe2c8f879c91d0e81d59cb40.js
│ ├── application-eeb856e3fe2c8f879c91d0e81d59cb40.js.gz
│ ├── glyphicons-halflings-c806376f05e4ccabe2c5315a8e95667c.png
│ ├── glyphicons-halflings-white-62b67d9edee3db90d18833087f848d6e.png
│ ├── manifest-802de9eb1c853769101852422b620883.json
│ └── rails-231a680f23887d9dd70710ea5efd3c62.png
├── favicon.ico
└── robots.txt
├── script
└── cucumber
├── spec
├── controllers
│ ├── microposts_controller_spec.rb
│ └── relationships_controller_spec.rb
├── decorators
│ └── micropost_decorator_spec.rb
├── factories.rb
├── helpers
│ └── application_helper_spec.rb
├── models
│ ├── concerns
│ │ └── emailable_spec.rb
│ ├── micropost_spec.rb
│ ├── relationship_spec.rb
│ └── user_spec.rb
├── requests
│ ├── authentication_pages_spec.rb
│ ├── micropost_pages_spec.rb
│ ├── static_pages_spec.rb
│ └── user_pages_spec.rb
├── spec_helper.rb
└── support
│ └── utilities.rb
└── vendor
└── assets
├── javascripts
└── .keep
└── stylesheets
└── .keep
/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore bundler config.
2 | /.bundle
3 |
4 | # Ignore the default SQLite database.
5 | /db/*.sqlite3
6 | /db/*.sqlite3-journal
7 |
8 | # Ignore all logfiles and tempfiles.
9 | /log/*.log
10 | /tmp
11 |
12 | # Ignore other unneeded files.
13 | database.yml
14 | doc/
15 | *.swp
16 | *~
17 | .project
18 | .DS_Store
19 | .idea
20 | .rvmrc
21 | .ruby-*
22 | .secret
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --colour
2 | --drb
3 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 | ruby '2.0.0'
3 | #ruby-gemset=railstutorial_rails_4_0
4 |
5 | gem 'rails', '4.0.4'
6 | gem 'draper'
7 | gem 'bootstrap-sass', '2.3.2.0'
8 | gem 'sprockets', '2.11.0'
9 | gem 'bcrypt-ruby', '3.1.2'
10 | gem 'faker', '1.1.2'
11 | gem 'will_paginate', '3.0.4'
12 | gem 'bootstrap-will_paginate', '0.0.9'
13 | gem 'awesome_print'
14 |
15 | group :development do
16 | gem 'pry-rails'
17 | end
18 |
19 | group :development, :test do
20 | gem 'sqlite3', '1.3.8'
21 | gem 'rspec-rails', '2.14'
22 | # The following optional lines are part of the advanced setup.
23 | gem 'guard-rails'
24 | gem 'guard-rspec'
25 | gem 'rspec-mocks'
26 | gem 'spork-rails', '4.0.0'
27 | gem 'guard-spork', '1.5.0'
28 | gem 'childprocess', '0.3.6'
29 | end
30 |
31 | group :test do
32 | gem 'selenium-webdriver', '2.35.1'
33 | gem 'capybara', '2.1.0'
34 | gem 'factory_girl_rails', '4.2.0'
35 | gem 'cucumber-rails', '1.3.0', :require => false
36 | gem 'database_cleaner', github: 'bmabey/database_cleaner'
37 |
38 | # Uncomment this line on OS X.
39 | gem 'growl', '1.0.3'
40 |
41 | # Uncomment these lines on Linux.
42 | # gem 'libnotify', '0.8.0'
43 |
44 | # Uncomment these lines on Windows.
45 | # gem 'rb-notifu', '0.0.4'
46 | # gem 'win32console', '1.3.2'
47 | # gem 'wdm', '0.1.0'
48 | end
49 |
50 | gem 'sass-rails', '4.0.1'
51 | gem 'uglifier', '2.1.1'
52 | gem 'coffee-rails', '4.0.1'
53 | gem 'jquery-rails', '3.0.4'
54 | gem 'turbolinks', '1.1.1'
55 | gem 'jbuilder', '1.0.2'
56 |
57 | group :doc do
58 | gem 'sdoc', '0.3.20', require: false
59 | end
60 |
61 | group :production do
62 | gem 'pg', '0.15.1'
63 | gem 'rails_12factor', '0.0.2'
64 | end
65 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GIT
2 | remote: git://github.com/bmabey/database_cleaner.git
3 | revision: 35bd646903fc56af185a9fa9e360c568bb56756e
4 | specs:
5 | database_cleaner (1.2.0)
6 |
7 | GEM
8 | remote: https://rubygems.org/
9 | specs:
10 | actionmailer (4.0.4)
11 | actionpack (= 4.0.4)
12 | mail (~> 2.5.4)
13 | actionpack (4.0.4)
14 | activesupport (= 4.0.4)
15 | builder (~> 3.1.0)
16 | erubis (~> 2.7.0)
17 | rack (~> 1.5.2)
18 | rack-test (~> 0.6.2)
19 | activemodel (4.0.4)
20 | activesupport (= 4.0.4)
21 | builder (~> 3.1.0)
22 | activerecord (4.0.4)
23 | activemodel (= 4.0.4)
24 | activerecord-deprecated_finders (~> 1.0.2)
25 | activesupport (= 4.0.4)
26 | arel (~> 4.0.0)
27 | activerecord-deprecated_finders (1.0.3)
28 | activesupport (4.0.4)
29 | i18n (~> 0.6, >= 0.6.9)
30 | minitest (~> 4.2)
31 | multi_json (~> 1.3)
32 | thread_safe (~> 0.1)
33 | tzinfo (~> 0.3.37)
34 | arel (4.0.2)
35 | awesome_print (1.2.0)
36 | bcrypt-ruby (3.1.2)
37 | bootstrap-sass (2.3.2.0)
38 | sass (~> 3.2)
39 | bootstrap-will_paginate (0.0.9)
40 | will_paginate
41 | builder (3.1.4)
42 | capybara (2.1.0)
43 | mime-types (>= 1.16)
44 | nokogiri (>= 1.3.3)
45 | rack (>= 1.0.0)
46 | rack-test (>= 0.5.4)
47 | xpath (~> 2.0)
48 | celluloid (0.15.2)
49 | timers (~> 1.1.0)
50 | celluloid-io (0.15.0)
51 | celluloid (>= 0.15.0)
52 | nio4r (>= 0.5.0)
53 | childprocess (0.3.6)
54 | ffi (~> 1.0, >= 1.0.6)
55 | coderay (1.1.0)
56 | coffee-rails (4.0.1)
57 | coffee-script (>= 2.2.0)
58 | railties (>= 4.0.0, < 5.0)
59 | coffee-script (2.2.0)
60 | coffee-script-source
61 | execjs
62 | coffee-script-source (1.7.0)
63 | cucumber (1.3.12)
64 | builder (>= 2.1.2)
65 | diff-lcs (>= 1.1.3)
66 | gherkin (~> 2.12)
67 | multi_json (>= 1.7.5, < 2.0)
68 | multi_test (>= 0.1.1)
69 | cucumber-rails (1.3.0)
70 | capybara (>= 1.1.2)
71 | cucumber (>= 1.1.8)
72 | nokogiri (>= 1.5.0)
73 | diff-lcs (1.2.5)
74 | draper (1.3.0)
75 | actionpack (>= 3.0)
76 | activemodel (>= 3.0)
77 | activesupport (>= 3.0)
78 | request_store (~> 1.0.3)
79 | erubis (2.7.0)
80 | execjs (2.0.2)
81 | factory_girl (4.2.0)
82 | activesupport (>= 3.0.0)
83 | factory_girl_rails (4.2.0)
84 | factory_girl (~> 4.2.0)
85 | railties (>= 3.0.0)
86 | faker (1.1.2)
87 | i18n (~> 0.5)
88 | ffi (1.9.3)
89 | formatador (0.2.4)
90 | gherkin (2.12.2)
91 | multi_json (~> 1.3)
92 | growl (1.0.3)
93 | guard (2.6.0)
94 | formatador (>= 0.2.4)
95 | listen (~> 2.7)
96 | lumberjack (~> 1.0)
97 | pry (>= 0.9.12)
98 | thor (>= 0.18.1)
99 | guard-rails (0.5.0)
100 | guard (>= 2.0.0)
101 | guard-rspec (4.2.8)
102 | guard (~> 2.1)
103 | rspec (>= 2.14, < 4.0)
104 | guard-spork (1.5.0)
105 | childprocess (>= 0.2.3)
106 | guard (>= 1.1)
107 | spork (>= 0.8.4)
108 | hike (1.2.3)
109 | i18n (0.6.9)
110 | jbuilder (1.0.2)
111 | activesupport (>= 3.0.0)
112 | jquery-rails (3.0.4)
113 | railties (>= 3.0, < 5.0)
114 | thor (>= 0.14, < 2.0)
115 | json (1.8.1)
116 | listen (2.7.1)
117 | celluloid (>= 0.15.2)
118 | celluloid-io (>= 0.15.0)
119 | rb-fsevent (>= 0.9.3)
120 | rb-inotify (>= 0.9)
121 | lumberjack (1.0.5)
122 | mail (2.5.4)
123 | mime-types (~> 1.16)
124 | treetop (~> 1.4.8)
125 | method_source (0.8.2)
126 | mime-types (1.25.1)
127 | mini_portile (0.5.2)
128 | minitest (4.7.5)
129 | multi_json (1.9.2)
130 | multi_test (0.1.1)
131 | nio4r (1.0.0)
132 | nokogiri (1.6.1)
133 | mini_portile (~> 0.5.0)
134 | pg (0.15.1)
135 | polyglot (0.3.4)
136 | pry (0.9.12.6)
137 | coderay (~> 1.0)
138 | method_source (~> 0.8)
139 | slop (~> 3.4)
140 | pry-rails (0.3.2)
141 | pry (>= 0.9.10)
142 | rack (1.5.2)
143 | rack-test (0.6.2)
144 | rack (>= 1.0)
145 | rails (4.0.4)
146 | actionmailer (= 4.0.4)
147 | actionpack (= 4.0.4)
148 | activerecord (= 4.0.4)
149 | activesupport (= 4.0.4)
150 | bundler (>= 1.3.0, < 2.0)
151 | railties (= 4.0.4)
152 | sprockets-rails (~> 2.0.0)
153 | rails_12factor (0.0.2)
154 | rails_serve_static_assets
155 | rails_stdout_logging
156 | rails_serve_static_assets (0.0.2)
157 | rails_stdout_logging (0.0.3)
158 | railties (4.0.4)
159 | actionpack (= 4.0.4)
160 | activesupport (= 4.0.4)
161 | rake (>= 0.8.7)
162 | thor (>= 0.18.1, < 2.0)
163 | rake (10.3.1)
164 | rb-fsevent (0.9.4)
165 | rb-inotify (0.9.3)
166 | ffi (>= 0.5.0)
167 | rdoc (3.12.2)
168 | json (~> 1.4)
169 | request_store (1.0.5)
170 | rspec (2.14.1)
171 | rspec-core (~> 2.14.0)
172 | rspec-expectations (~> 2.14.0)
173 | rspec-mocks (~> 2.14.0)
174 | rspec-core (2.14.8)
175 | rspec-expectations (2.14.5)
176 | diff-lcs (>= 1.1.3, < 2.0)
177 | rspec-mocks (2.14.6)
178 | rspec-rails (2.14.0)
179 | actionpack (>= 3.0)
180 | activesupport (>= 3.0)
181 | railties (>= 3.0)
182 | rspec-core (~> 2.14.0)
183 | rspec-expectations (~> 2.14.0)
184 | rspec-mocks (~> 2.14.0)
185 | rubyzip (0.9.9)
186 | sass (3.3.3)
187 | sass-rails (4.0.1)
188 | railties (>= 4.0.0, < 5.0)
189 | sass (>= 3.1.10)
190 | sprockets-rails (~> 2.0.0)
191 | sdoc (0.3.20)
192 | json (>= 1.1.3)
193 | rdoc (~> 3.10)
194 | selenium-webdriver (2.35.1)
195 | childprocess (>= 0.2.5)
196 | multi_json (~> 1.0)
197 | rubyzip (< 1.0.0)
198 | websocket (~> 1.0.4)
199 | slop (3.5.0)
200 | spork (1.0.0rc4)
201 | spork-rails (4.0.0)
202 | rails (>= 3.0.0, < 5)
203 | spork (>= 1.0rc0)
204 | sprockets (2.11.0)
205 | hike (~> 1.2)
206 | multi_json (~> 1.0)
207 | rack (~> 1.0)
208 | tilt (~> 1.1, != 1.3.0)
209 | sprockets-rails (2.0.1)
210 | actionpack (>= 3.0)
211 | activesupport (>= 3.0)
212 | sprockets (~> 2.8)
213 | sqlite3 (1.3.8)
214 | thor (0.19.1)
215 | thread_safe (0.3.3)
216 | tilt (1.4.1)
217 | timers (1.1.0)
218 | treetop (1.4.15)
219 | polyglot
220 | polyglot (>= 0.3.1)
221 | turbolinks (1.1.1)
222 | coffee-rails
223 | tzinfo (0.3.39)
224 | uglifier (2.1.1)
225 | execjs (>= 0.3.0)
226 | multi_json (~> 1.0, >= 1.0.2)
227 | websocket (1.0.7)
228 | will_paginate (3.0.4)
229 | xpath (2.0.0)
230 | nokogiri (~> 1.3)
231 |
232 | PLATFORMS
233 | ruby
234 |
235 | DEPENDENCIES
236 | awesome_print
237 | bcrypt-ruby (= 3.1.2)
238 | bootstrap-sass (= 2.3.2.0)
239 | bootstrap-will_paginate (= 0.0.9)
240 | capybara (= 2.1.0)
241 | childprocess (= 0.3.6)
242 | coffee-rails (= 4.0.1)
243 | cucumber-rails (= 1.3.0)
244 | database_cleaner!
245 | draper
246 | factory_girl_rails (= 4.2.0)
247 | faker (= 1.1.2)
248 | growl (= 1.0.3)
249 | guard-rails
250 | guard-rspec
251 | guard-spork (= 1.5.0)
252 | jbuilder (= 1.0.2)
253 | jquery-rails (= 3.0.4)
254 | pg (= 0.15.1)
255 | pry-rails
256 | rails (= 4.0.4)
257 | rails_12factor (= 0.0.2)
258 | rspec-mocks
259 | rspec-rails (= 2.14)
260 | sass-rails (= 4.0.1)
261 | sdoc (= 0.3.20)
262 | selenium-webdriver (= 2.35.1)
263 | spork-rails (= 4.0.0)
264 | sprockets (= 2.11.0)
265 | sqlite3 (= 1.3.8)
266 | turbolinks (= 1.1.1)
267 | uglifier (= 2.1.1)
268 | will_paginate (= 3.0.4)
269 |
--------------------------------------------------------------------------------
/Guardfile:
--------------------------------------------------------------------------------
1 | # A sample Guardfile
2 | # More info at https://github.com/guard/guard#readme
3 | require 'active_support/inflector'
4 |
5 | guard 'spork', :cucumber_env => { 'RAILS_ENV' => 'test' },
6 | :rspec_env => { 'RAILS_ENV' => 'test' } do
7 | watch('config/application.rb')
8 | watch('config/environment.rb')
9 | watch('config/environments/test.rb')
10 | watch(%r{^config/initializers/.+\.rb$})
11 | watch('Gemfile')
12 | watch('Gemfile.lock')
13 | watch('spec/spec_helper.rb') { :rspec }
14 | watch('test/test_helper.rb') { :test_unit }
15 | watch(%r{features/support/}) { :cucumber }
16 | end
17 |
18 | guard 'rspec', all_after_pass: false, cli: '--drb' do
19 | watch(%r{^spec/.+_spec\.rb$})
20 | watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
21 | watch('spec/spec_helper.rb') { "spec" }
22 |
23 | # Rails example
24 | watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
25 | watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
26 | watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
27 | watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
28 | watch('config/routes.rb') { "spec/routing" }
29 | watch('app/controllers/application_controller.rb') { "spec/controllers" }
30 |
31 | # Capybara features specs
32 | watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
33 |
34 | # Turnip features and steps
35 | watch(%r{^spec/acceptance/(.+)\.feature$})
36 | watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
37 |
38 | # Custom Rails Tutorial specs
39 | watch(%r{^app/controllers/(.+)_(controller)\.rb$}) do |m|
40 | ["spec/routing/#{m[1]}_routing_spec.rb",
41 | "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb",
42 | "spec/acceptance/#{m[1]}_spec.rb",
43 | (m[1][/_pages/] ? "spec/requests/#{m[1]}_spec.rb" :
44 | "spec/requests/#{m[1].singularize}_pages_spec.rb")]
45 | end
46 | watch(%r{^app/views/(.+)/}) do |m|
47 | (m[1][/_pages/] ? "spec/requests/#{m[1]}_spec.rb" :
48 | "spec/requests/#{m[1].singularize}_pages_spec.rb")
49 | end
50 | watch(%r{^app/controllers/sessions_controller\.rb$}) do |m|
51 | "spec/requests/authentication_pages_spec.rb"
52 | end
53 | end
54 |
55 | guard 'rails' do
56 | watch('Gemfile.lock')
57 | watch(%r{^(config|lib)/.*})
58 | end
59 |
60 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2013 Michael Hartl, 2014 Justin Gordon
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Refactoring Fat Models, Controllers, and Views Example
2 |
3 | [RailsOnMaui Links to slides, talk, etc.](http://www.railsonmaui.com/blog/2014/04/23/railsconf-2014/)
4 |
5 | This example application covers four Rails Refactoring techniques:
6 | Concerns, Decorators, Presenters, and moving code to models.
7 | It builds on the Ruby on Rails Tutorial: sample application by Michael Hartl:
8 | [*Ruby on Rails Tutorial: Learn Web Development with Rails*](http://railstutorial.org/).
9 | However, it is changed in that minors cannot post profanity. If you login as
10 | "littlepunk@sugarranchmaui.com", password "foobar", you get the minor profanity
11 | checking behavior. There is no UI for setting a user to be a minor, FYI.
12 |
13 | The code is carefully crafted so that the initial code (at branch `railsconf-start`
14 | is relatively "clean". The lessons can thus focus only on the refactoring techniques,
15 | which are presented several pull requests. Much of the lesson information is contained
16 | in the pull request description.
17 |
18 | Please inspect the individual commits, as the refactorings are broken up into smaller
19 | staps. The final result, the tip of the branch, contains the full refactoring.
20 | Please feel free to comment on the pull requests and/or submit issues. You may also
21 | contact me directly at [justin@shakacode.com](mailto:justin@shakacode.com).
22 |
23 | The branches labeled `rc-` are the final versions developed for my RailsConf 2014 presentation,
24 | titled "Concerns, Decorators, Presenters, Service Objects, Helpers, Help Me Decide!".
25 |
26 | Topic | Branch | Pull Request
27 | -----------|--------|------
28 | Concerns | rc-concerns | https://github.com/justin808/fat-code-refactoring-techniques/pull/9
29 | Decorators | rc-decorators | https://github.com/justin808/fat-code-refactoring-techniques/pull/10
30 | Presenters | rc-presenters | https://github.com/justin808/fat-code-refactoring-techniques/pull/11
31 | Models rather than Service Objects | rc-business-logic-in-model | https://github.com/justin808/fat-code-refactoring-techniques/pull/15
32 | Split Controllers | rc-split-controller | https://github.com/justin808/fat-code-refactoring-techniques/pull/13
33 | Controller Concerns | rc-controller-concerns | https://github.com/justin808/fat-code-refactoring-techniques/pull/14
34 |
35 | Note, I originally planned to cover "Service Objects". However, my example of a refactoring the
36 | "Kid Safe Microblogger" actually demonstrated how a Service Object pattern is not needed. Instead,
37 | the lesson is to convert to using core Rails techniques of moving logic to business models. You
38 | can find the prior two refactoring attempts here:
39 |
40 | * [Pull 6, Service Objects](https://github.com/justin808/fat-code-refactoring-techniques/pull/6).
41 | * [Pull 7, Focused Controller](https://github.com/justin808/fat-code-refactoring-techniques/pull/7).
42 |
43 | # Setup
44 |
45 | The command `guard` both runs the tests and the application.
46 | To re-run all tests, in the `guard` console window, type `a `.
47 |
48 | This will create a branch called `refactoring-tutorial` where you can follow the examples.
49 |
50 | ```bash
51 | $ cd /tmp
52 | $ git clone https://github.com/justin808/fat-code-refactoring-techniques.git
53 | $ git checkout railsconf-start
54 | $ git checkout -b refactoring-tutorial
55 | $ cd fat-code-refactoring-techniques
56 | $ cp config/database.yml.example config/database.yml
57 | $ bundle install
58 | $ bundle exec rake db:migrate
59 | $ bundle exec rake db:test:prepare
60 | $ guard
61 | ```
62 |
63 | Then, if you want to see the completed application:
64 |
65 | ```bash
66 | $ git checkout railsconf-finish
67 | $ bundle install
68 | $ guard
69 | ```
70 |
71 | I would suggest creating your `refactoring-tutorial` branch and then manually applying the commits
72 | in the pull requests, creating your own commits along the way.
73 |
74 | At each commit, the tests should continue to pass.
75 |
76 | You can simulate the flow of what I'll be doing in the presentation with the git scripts in `bin/git-railsconf.zsh`.
77 |
78 | ```bash
79 | $ cd
80 | $ . bin/git-railsconf.zsh
81 | $ git checkout railsconf-start # you're at the beginning of the refactorings
82 | ```
83 |
84 | Then you can run this to simulate the first edit:
85 |
86 | ```bash
87 | $ railsconf-start
88 | ```
89 |
90 | Take a look at the files that are changed. See the tests pass. Experiment. Then run:
91 |
92 | ```bash
93 | $ railsconf-advance-history
94 | ```
95 |
96 | That will move the history forward one step, and the changing files will be in the git index.
97 |
98 | *WARNING*: These scripts mercilessly do `git reset --hard`. So beware!
99 |
--------------------------------------------------------------------------------
/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 | SampleApp::Application.load_tasks
7 |
--------------------------------------------------------------------------------
/app/assets/images/rails.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakacode/fat-code-refactoring-techniques/a578449db549b4d367d354d2f94f1fd5c5bb5f8d/app/assets/images/rails.png
--------------------------------------------------------------------------------
/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // compiled file.
9 | //
10 | // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11 | // GO AFTER THE REQUIRES BELOW.
12 | //
13 | //= require jquery
14 | //= require jquery_ujs
15 | //= require turbolinks
16 | //= require bootstrap
17 | //= require_tree .
18 |
--------------------------------------------------------------------------------
/app/assets/javascripts/sessions.js.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://coffeescript.org/
4 |
--------------------------------------------------------------------------------
/app/assets/javascripts/static_pages.js.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://coffeescript.org/
4 |
--------------------------------------------------------------------------------
/app/assets/javascripts/users.js.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://coffeescript.org/
4 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the top of the
9 | * compiled file, but it's generally better to create a new file per style scope.
10 | *
11 | *= require_self
12 | *= require_tree .
13 | */
14 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/custom.css.scss:
--------------------------------------------------------------------------------
1 | @import "bootstrap";
2 |
3 | /* mixins, variables, etc. */
4 |
5 | $grayMediumLight: #eaeaea;
6 |
7 | @mixin box_sizing {
8 | -moz-box-sizing: border-box;
9 | -webkit-box-sizing: border-box;
10 | box-sizing: border-box;
11 | }
12 |
13 | /* universal */
14 |
15 | html {
16 | overflow-y: scroll;
17 | }
18 |
19 | body {
20 | padding-top: 60px;
21 | }
22 |
23 | section {
24 | overflow: auto;
25 | }
26 |
27 | textarea {
28 | resize: vertical;
29 | }
30 |
31 | .center {
32 | text-align: center;
33 | h1 {
34 | margin-bottom: 10px;
35 | }
36 | }
37 |
38 | /* typography */
39 |
40 | h1, h2, h3, h4, h5, h6 {
41 | line-height: 1;
42 | }
43 |
44 | h1 {
45 | font-size: 3em;
46 | letter-spacing: -2px;
47 | margin-bottom: 30px;
48 | text-align: center;
49 | }
50 |
51 | h2 {
52 | font-size: 1.2em;
53 | letter-spacing: -1px;
54 | margin-bottom: 30px;
55 | text-align: center;
56 | font-weight: normal;
57 | color: $grayLight;
58 | }
59 |
60 | p {
61 | font-size: 1.1em;
62 | line-height: 1.7em;
63 | }
64 |
65 |
66 | /* header */
67 |
68 | #logo {
69 | float: left;
70 | margin-right: 10px;
71 | font-size: 1.7em;
72 | color: white;
73 | text-transform: uppercase;
74 | letter-spacing: -1px;
75 | padding-top: 9px;
76 | font-weight: bold;
77 | line-height: 1;
78 | &:hover {
79 | color: white;
80 | text-decoration: none;
81 | }
82 | }
83 |
84 | /* footer */
85 |
86 | footer {
87 | margin-top: 45px;
88 | padding-top: 5px;
89 | border-top: 1px solid $grayMediumLight;
90 | color: $grayLight;
91 | a {
92 | color: $gray;
93 | &:hover {
94 | color: $grayDarker;
95 | }
96 | }
97 | small {
98 | float: left;
99 | }
100 | ul {
101 | float: right;
102 | list-style: none;
103 | li {
104 | float: left;
105 | margin-left: 10px;
106 | }
107 | }
108 | }
109 |
110 | /* miscellaneous */
111 |
112 | .debug_dump {
113 | clear: both;
114 | float: left;
115 | width: 100%;
116 | margin-top: 45px;
117 | @include box_sizing;
118 | }
119 |
120 | /* sidebar */
121 |
122 | aside {
123 | section {
124 | padding: 10px 0;
125 | border-top: 1px solid $grayLighter;
126 | &:first-child {
127 | border: 0;
128 | padding-top: 0;
129 | }
130 | span {
131 | display: block;
132 | margin-bottom: 3px;
133 | line-height: 1;
134 | }
135 | h1 {
136 | font-size: 1.4em;
137 | text-align: left;
138 | letter-spacing: -1px;
139 | margin-bottom: 3px;
140 | margin-top: 0px;
141 | }
142 | }
143 | }
144 |
145 | .gravatar {
146 | float: left;
147 | margin-right: 10px;
148 | }
149 |
150 | .stats {
151 | overflow: auto;
152 | a {
153 | float: left;
154 | padding: 0 10px;
155 | border-left: 1px solid $grayLighter;
156 | color: gray;
157 | &:first-child {
158 | padding-left: 0;
159 | border: 0;
160 | }
161 | &:hover {
162 | text-decoration: none;
163 | color: $blue;
164 | }
165 | }
166 | strong {
167 | display: block;
168 | }
169 | }
170 |
171 | .user_avatars {
172 | overflow: auto;
173 | margin-top: 10px;
174 | .gravatar {
175 | margin: 1px 1px;
176 | }
177 | }
178 |
179 | /* forms */
180 |
181 | input, textarea, select, .uneditable-input {
182 | border: 1px solid #bbb;
183 | width: 100%;
184 | margin-bottom: 15px;
185 | @include box_sizing;
186 | }
187 |
188 | input {
189 | height: auto !important;
190 | }
191 |
192 | #error_explanation {
193 | color: #f00;
194 | ul {
195 | list-style: none;
196 | margin: 0 0 18px 0;
197 | }
198 | }
199 |
200 | .field_with_errors {
201 | @extend .control-group;
202 | @extend .error;
203 | }
204 |
205 | /* Users index */
206 |
207 | .users {
208 | list-style: none;
209 | margin: 0;
210 | li {
211 | overflow: auto;
212 | padding: 10px 0;
213 | border-top: 1px solid $grayLighter;
214 | &:last-child {
215 | border-bottom: 1px solid $grayLighter;
216 | }
217 | }
218 | }
219 |
220 | /* microposts */
221 |
222 | .microposts {
223 | list-style: none;
224 | margin: 10px 0 0 0;
225 |
226 | li {
227 | padding: 10px 0;
228 | border-top: 1px solid #e8e8e8;
229 | }
230 | }
231 | .content {
232 | display: block;
233 | }
234 | .timestamp {
235 | color: $grayLight;
236 | }
237 | .gravatar {
238 | float: left;
239 | margin-right: 10px;
240 | }
241 | aside {
242 | textarea {
243 | height: 100px;
244 | margin-bottom: 5px;
245 | }
246 | }
247 |
248 | .parent-notification {
249 | font-size: 32px;
250 | font-weight: bold;
251 | }
252 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/sessions.css.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the Sessions controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/static_pages.css.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the StaticPages controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/users.css.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the Users controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | # Prevent CSRF attacks by raising an exception.
3 | # For APIs, you may want to use :null_session instead.
4 | protect_from_forgery with: :exception
5 | include SessionsHelper
6 | end
7 |
--------------------------------------------------------------------------------
/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakacode/fat-code-refactoring-techniques/a578449db549b4d367d354d2f94f1fd5c5bb5f8d/app/controllers/concerns/.keep
--------------------------------------------------------------------------------
/app/controllers/concerns/signed_in_user.rb:
--------------------------------------------------------------------------------
1 | # Controller Concern Example
2 | # Place this in controllers that have all actions having a signed in user
3 | # include SignedInUser
4 | module SignedInUser
5 | extend ActiveSupport::Concern
6 |
7 | included do
8 | before_action :signed_in_user
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/app/controllers/microposts_controller.rb:
--------------------------------------------------------------------------------
1 | class MicropostsController < ApplicationController
2 | before_action :signed_in_user, only: [:create, :destroy]
3 | before_action :correct_user, only: :destroy
4 |
5 | def create
6 | @micropost = Micropost.new(micropost_params.merge(user: current_user))
7 | if @micropost.save_with_profanity_callbacks
8 | flash[:success] = "Micropost created!"
9 | redirect_to root_url
10 | else
11 | adjust_micropost_profanity_message
12 | render 'static_pages/home'
13 | end
14 | end
15 |
16 | def destroy
17 | @micropost.destroy
18 | redirect_to root_url
19 | end
20 |
21 | private
22 | def micropost_params
23 | params.require(:micropost).permit(:content)
24 | end
25 |
26 | def correct_user
27 | @micropost = current_user.microposts.find_by(id: params[:id])
28 | redirect_to root_url if @micropost.nil?
29 | end
30 |
31 | def adjust_micropost_profanity_message
32 | if @micropost.profanity_validation_error?
33 | @micropost.errors[:content].clear # remove the default validation message
34 | flash.now[:error] = @micropost.decorate.profanity_violation_msg
35 | end
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/app/controllers/relationships_controller.rb:
--------------------------------------------------------------------------------
1 | class RelationshipsController < ApplicationController
2 | before_action :signed_in_user
3 |
4 | def create
5 | @user = User.find(params[:relationship][:followed_id])
6 | current_user.follow!(@user)
7 | respond_to do |format|
8 | format.html { redirect_to @user }
9 | format.js
10 | end
11 | end
12 |
13 | def destroy
14 | @user = Relationship.find(params[:id]).followed
15 | current_user.unfollow!(@user)
16 | respond_to do |format|
17 | format.html { redirect_to @user }
18 | format.js
19 | end
20 | end
21 | end
--------------------------------------------------------------------------------
/app/controllers/sessions_controller.rb:
--------------------------------------------------------------------------------
1 | class SessionsController < ApplicationController
2 |
3 | def new
4 | end
5 |
6 | def create
7 | user = User.find_by(email: params[:session][:email].downcase)
8 | if user && user.authenticate(params[:session][:password])
9 | sign_in user
10 | redirect_back_or user
11 | else
12 | flash.now[:error] = 'Invalid email/password combination'
13 | render 'new'
14 | end
15 | end
16 |
17 | def destroy
18 | sign_out
19 | redirect_to root_url
20 | end
21 | end
--------------------------------------------------------------------------------
/app/controllers/static_pages_controller.rb:
--------------------------------------------------------------------------------
1 | class StaticPagesController < ApplicationController
2 |
3 | def home
4 | if signed_in?
5 | @micropost = current_user.microposts.build
6 | @feed_items = current_user.feed.paginate(page: params[:page])
7 | end
8 | end
9 |
10 | def help
11 | end
12 |
13 | def about
14 | end
15 |
16 | def contact
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/app/controllers/users/followers_controller.rb:
--------------------------------------------------------------------------------
1 | module Users
2 | class FollowersController < ApplicationController
3 | include SignedInUser
4 |
5 | def followers
6 | @presenter = Users::FollowersPresenter.new(params[:id], params[:page])
7 | render '/users/show_follow'
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/app/controllers/users/following_controller.rb:
--------------------------------------------------------------------------------
1 | module Users
2 | class FollowingController < ApplicationController
3 | include SignedInUser
4 |
5 | def following
6 | @presenter = Users::FollowedUsersPresenter.new(params[:id], params[:page])
7 | render '/users/show_follow'
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/app/controllers/users_controller.rb:
--------------------------------------------------------------------------------
1 | class UsersController < ApplicationController
2 | before_action :signed_in_user,
3 | only: [:index, :edit, :update, :destroy, :following, :followers]
4 | before_action :correct_user, only: [:edit, :update]
5 | before_action :admin_user, only: :destroy
6 |
7 | def index
8 | @users = User.paginate(page: params[:page])
9 | end
10 |
11 | def show
12 | @user = User.find(params[:id])
13 | @microposts = @user.microposts.paginate(page: params[:page])
14 | end
15 |
16 | def new
17 | @user = User.new
18 | end
19 |
20 | def create
21 | @user = User.new(user_params)
22 | if @user.save
23 | sign_in @user
24 | flash[:success] = "Welcome to the Kid Blogger!"
25 | redirect_to @user
26 | else
27 | render 'new'
28 | end
29 | end
30 |
31 | def edit
32 | end
33 |
34 | def update
35 | if @user.update_attributes(user_params)
36 | flash[:success] = "Profile updated"
37 | redirect_to @user
38 | else
39 | render 'edit'
40 | end
41 | end
42 |
43 | def destroy
44 | User.find(params[:id]).destroy
45 | flash[:success] = "User destroyed."
46 | redirect_to users_url
47 | end
48 |
49 | private
50 |
51 | def user_params
52 | params.require(:user).permit(:name, :email, :password,
53 | :password_confirmation)
54 | end
55 |
56 | # Before filters
57 |
58 | def correct_user
59 | @user = User.find(params[:id])
60 | redirect_to(root_url) unless current_user?(@user)
61 | end
62 |
63 | def admin_user
64 | redirect_to(root_url) unless current_user.admin?
65 | end
66 | end
67 |
--------------------------------------------------------------------------------
/app/decorators/micropost_decorator.rb:
--------------------------------------------------------------------------------
1 | class MicropostDecorator < Draper::Decorator
2 |
3 | def posted_ago
4 | h.content_tag :span, class: 'timestamp' do
5 | "Posted #{h.time_ago_in_words(object.created_at)} ago."
6 | end
7 | end
8 |
9 | def profanity_violation_msg
10 | <<-MSG.html_safe
11 | Whoa, better watch your language! Profanity: '#{object.profane_words_in_content.join(", ")}' not allowed!
12 | You've tried to use profanity #{h.pluralize(object.user.profanity_count, "time")}!
13 |
Your parents have been notified!
14 | MSG
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 |
3 | # Returns the full title on a per-page basis.
4 | def full_title(page_title)
5 | base_title = "Kid Blogger"
6 | if page_title.empty?
7 | base_title
8 | else
9 | "#{base_title} | #{page_title}"
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/app/helpers/sessions_helper.rb:
--------------------------------------------------------------------------------
1 | module SessionsHelper
2 |
3 | def sign_in(user)
4 | remember_token = User.new_remember_token
5 | cookies.permanent[:remember_token] = remember_token
6 | user.update_attribute(:remember_token, User.hash(remember_token))
7 | self.current_user = user
8 | end
9 |
10 | def signed_in?
11 | !current_user.nil?
12 | end
13 |
14 | def current_user=(user)
15 | @current_user = user
16 | end
17 |
18 | def current_user
19 | remember_token = User.hash(cookies[:remember_token])
20 | @current_user ||= User.find_by(remember_token: remember_token)
21 | end
22 |
23 | def current_user?(user)
24 | user == current_user
25 | end
26 |
27 | def signed_in_user
28 | unless signed_in?
29 | store_location
30 | redirect_to signin_url, notice: "Please sign in."
31 | end
32 | end
33 |
34 | def sign_out
35 | current_user.update_attribute(:remember_token,
36 | User.hash(User.new_remember_token))
37 | cookies.delete(:remember_token)
38 | self.current_user = nil
39 | end
40 |
41 | def redirect_back_or(default)
42 | redirect_to(session[:return_to] || default)
43 | session.delete(:return_to)
44 | end
45 |
46 | def store_location
47 | session[:return_to] = request.url if request.get?
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/app/helpers/static_pages_helper.rb:
--------------------------------------------------------------------------------
1 | module StaticPagesHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/users_helper.rb:
--------------------------------------------------------------------------------
1 | module UsersHelper
2 |
3 | # Returns the Gravatar (http://gravatar.com/) for the given user.
4 | def gravatar_for(user, options = { size: 50 })
5 | gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
6 | size = options[:size]
7 | gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}"
8 | image_tag(gravatar_url, alt: user.name, class: "gravatar")
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/app/mailers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakacode/fat-code-refactoring-techniques/a578449db549b4d367d354d2f94f1fd5c5bb5f8d/app/mailers/.keep
--------------------------------------------------------------------------------
/app/models/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakacode/fat-code-refactoring-techniques/a578449db549b4d367d354d2f94f1fd5c5bb5f8d/app/models/.keep
--------------------------------------------------------------------------------
/app/models/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakacode/fat-code-refactoring-techniques/a578449db549b4d367d354d2f94f1fd5c5bb5f8d/app/models/concerns/.keep
--------------------------------------------------------------------------------
/app/models/concerns/emailable.rb:
--------------------------------------------------------------------------------
1 | module Emailable
2 | extend ActiveSupport::Concern
3 |
4 | included do
5 | before_save { self.email = email.downcase }
6 |
7 | VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
8 | validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
9 | uniqueness: { case_sensitive: false }
10 |
11 | # downcase the searched for email
12 | scope :by_email_wildcard, ->(q) { where("email like ?", "#{q.downcase}%") }
13 | end
14 |
15 | def email_domain
16 | regex = /\A[\w+\-.]+@([a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z)/i
17 | email[regex, 1]
18 | end
19 |
20 | module ClassMethods
21 | def by_email(email)
22 | where(email: email.downcase).first
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/app/models/micropost.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: microposts
4 | #
5 | # id :integer not null, primary key
6 | # content :string(255)
7 | # user_id :integer
8 | # created_at :datetime
9 | # updated_at :datetime
10 | #
11 |
12 | class Micropost < ActiveRecord::Base
13 | belongs_to :user
14 | default_scope -> { order('created_at DESC') }
15 | validates :content, presence: true, length: { maximum: 140 }
16 | validates :user_id, presence: true
17 | validate :no_profanity
18 |
19 | # Returns microposts from the users being followed by the given user.
20 | def self.from_users_followed_by(user)
21 | followed_user_ids = "SELECT followed_id FROM relationships
22 | WHERE follower_id = :user_id"
23 | where("user_id IN (#{followed_user_ids}) OR user_id = :user_id",
24 | user_id: user.id)
25 | end
26 |
27 | # return array of profane words in content or nil if none
28 | def profane_words_in_content
29 | # Better to set this somewhere configurable. Placing here for example purposes.
30 | profane_words = %w(poop fart fartface poopface poopbuttface)
31 | content_words = content.split(/\W/)
32 | content_words.select { |word| word.in? profane_words }.presence
33 | end
34 |
35 | # This could have been done with and after_save hook, but it seems wrong to ever be sending
36 | # emails from after_save.
37 | # Return true if save successful
38 | def save_with_profanity_callbacks
39 | transaction do
40 | valid = save
41 | if profanity_validation_error?
42 | user.update_for_using_profanity(profane_words_in_content)
43 | end
44 | valid
45 | end
46 | end
47 |
48 | def profanity_validation_error?
49 | errors[:content].find { |error| error =~ /\AProfanity:/ }
50 | end
51 |
52 | private
53 |
54 | def no_profanity
55 | if user && user.minor? && (profane_words = profane_words_in_content)
56 | errors.add(:content, "Profanity: #{profane_words.join(", ")} not allowed!")
57 | end
58 | end
59 | end
60 |
--------------------------------------------------------------------------------
/app/models/relationship.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: relationships
4 | #
5 | # id :integer not null, primary key
6 | # follower_id :integer
7 | # followed_id :integer
8 | # created_at :datetime
9 | # updated_at :datetime
10 | #
11 |
12 | class Relationship < ActiveRecord::Base
13 | belongs_to :follower, class_name: "User", touch: true
14 | belongs_to :followed, class_name: "User", touch: true
15 | validates :follower_id, presence: true
16 | validates :followed_id, presence: true
17 | end
18 |
--------------------------------------------------------------------------------
/app/models/user.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: users
4 | #
5 | # id :integer not null, primary key
6 | # name :string(255)
7 | # email :string(255)
8 | # created_at :datetime
9 | # updated_at :datetime
10 | # password_digest :string(255)
11 | # remember_token :string(255)
12 | # admin :boolean
13 | # profanity_count :integer default(0)
14 | # minor :boolean default(FALSE)
15 | #
16 |
17 | class User < ActiveRecord::Base
18 | include Emailable
19 | has_many :microposts, dependent: :destroy
20 | has_many :relationships, foreign_key: "follower_id", dependent: :destroy
21 | has_many :followed_users, through: :relationships, source: :followed
22 | has_many :reverse_relationships, foreign_key: "followed_id",
23 | class_name: "Relationship",
24 | dependent: :destroy
25 | has_many :followers, through: :reverse_relationships, source: :follower
26 |
27 | before_create :create_remember_token
28 | validates :name, presence: true, length: { maximum: 50 }
29 |
30 | has_secure_password
31 | validates :password, length: { minimum: 6 }
32 |
33 | def User.new_remember_token
34 | SecureRandom.urlsafe_base64
35 | end
36 |
37 | def User.hash(token)
38 | Digest::SHA1.hexdigest(token.to_s)
39 | end
40 |
41 | def feed
42 | Micropost.from_users_followed_by(self)
43 | end
44 |
45 | def following?(other_user)
46 | relationships.find_by(followed_id: other_user.id)
47 | end
48 |
49 | def follow!(other_user)
50 | relationships.create!(followed_id: other_user.id)
51 | end
52 |
53 | def unfollow!(other_user)
54 | relationships.find_by(followed_id: other_user.id).destroy
55 | end
56 |
57 | def update_for_using_profanity(profane_words_used)
58 | increment(:profanity_count, profane_words_used.size)
59 | save(validate: false)
60 | send_parent_notification_of_profanity(profane_words_used)
61 | end
62 |
63 | def send_parent_notification_of_profanity(profane_words)
64 | # PRETEND: send email
65 | Rails.logger.info("Sent profanity alert email to parent of #{name}, "\
66 | "who used profane words: #{profane_words}")
67 | end
68 |
69 | private
70 |
71 | def create_remember_token
72 | self.remember_token = User.hash(User.new_remember_token)
73 | end
74 | end
75 |
--------------------------------------------------------------------------------
/app/presenters/users/follow_presenter.rb:
--------------------------------------------------------------------------------
1 | module Users
2 | class FollowPresenter
3 | include Draper::ViewHelpers
4 |
5 | def initialize(user_id, page)
6 | @user_id = user_id
7 | @page = page
8 | end
9 |
10 | def user
11 | @user ||= User.find(@user_id)
12 | end
13 |
14 | def cache_key
15 | [user, title]
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/app/presenters/users/followed_users_presenter.rb:
--------------------------------------------------------------------------------
1 | module Users
2 | class FollowedUsersPresenter < FollowPresenter
3 | def users
4 | @users ||= user.followed_users.paginate(page: @page)
5 | end
6 |
7 | def title
8 | "Following"
9 | end
10 |
11 | def subtitle
12 | @subtitle ||= "You Are Following #{h.pluralize(user.followed_users.size, "Blogger")}"
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/presenters/users/followers_presenter.rb:
--------------------------------------------------------------------------------
1 | module Users
2 | class FollowersPresenter < FollowPresenter
3 | def users
4 | @users ||= user.followers.paginate(page: @page)
5 | end
6 |
7 | def title
8 | "Followers"
9 | end
10 |
11 | def subtitle
12 | @subtitle ||= "Your Got #{h.pluralize(user.followers.size, "Followers")}"
13 | end
14 | end
15 | end
16 |
17 |
--------------------------------------------------------------------------------
/app/views/layouts/_footer.html.erb:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/app/views/layouts/_header.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%= link_to "Kid Blogger", root_path, id: "logo" %>
5 |
6 |
7 | <%= link_to "Home", root_path %>
8 | <%= link_to "Help", help_path %>
9 | <% if signed_in? %>
10 | <%= link_to "Users", users_path %>
11 |
24 | <% else %>
25 | <%= link_to "Sign in", signin_path %>
26 | <% end %>
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/views/layouts/_shim.html.erb:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%= full_title(yield(:title)) %>
5 | <%= stylesheet_link_tag "application", media: "all",
6 | "data-turbolinks-track" => true %>
7 | <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
8 | <%= csrf_meta_tags %>
9 | <%= render 'layouts/shim' %>
10 |
11 |
12 | <%= render 'layouts/header' %>
13 |
14 | <% flash.each do |key, value| %>
15 |
<%= value %>
16 | <% end %>
17 | <%= yield %>
18 | <%= render 'layouts/footer' %>
19 | <%= debug(params) if Rails.env.development? %>
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/views/microposts/_micropost.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= micropost.content %>
3 | <%= micropost.decorate.posted_ago %>
4 | <% if current_user?(micropost.user) %>
5 | <%= link_to "delete", micropost, method: :delete,
6 | data: { confirm: "You sure?" },
7 | title: micropost.content %>
8 | <% end %>
9 |
10 |
--------------------------------------------------------------------------------
/app/views/relationships/create.js.erb:
--------------------------------------------------------------------------------
1 | $("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>")
2 | $("#followers").html('<%= @user.followers.count %>')
3 |
--------------------------------------------------------------------------------
/app/views/relationships/destroy.js.erb:
--------------------------------------------------------------------------------
1 | $("#follow_form").html("<%= escape_javascript(render('users/follow')) %>")
2 | $("#followers").html('<%= @user.followers.count %>')
3 |
--------------------------------------------------------------------------------
/app/views/sessions/new.html.erb:
--------------------------------------------------------------------------------
1 | <% provide(:title, "Sign in") %>
2 | Sign in
3 |
4 |
5 |
6 | <%= form_for(:session, url: sessions_path) do |f| %>
7 |
8 | <%= f.label :email %>
9 | <%= f.text_field :email %>
10 |
11 | <%= f.label :password %>
12 | <%= f.password_field :password %>
13 |
14 | <%= f.submit "Sign in", class: "btn btn-large btn-primary" %>
15 | <% end %>
16 |
17 |
New user? <%= link_to "Sign up now!", signup_path %>
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/views/shared/_error_messages.html.erb:
--------------------------------------------------------------------------------
1 | <% if object.errors.any? %>
2 |
3 |
4 | The form contains <%= pluralize(object.errors.count, "error") %>.
5 |
6 |
7 | <% object.errors.full_messages.each do |msg| %>
8 | * <%= msg %>
9 | <% end %>
10 |
11 |
12 | <% end %>
13 |
--------------------------------------------------------------------------------
/app/views/shared/_feed.html.erb:
--------------------------------------------------------------------------------
1 | <% if @feed_items.try(:any?) %>
2 |
3 | <%= render partial: 'shared/feed_item', collection: @feed_items %>
4 |
5 | <%= will_paginate @feed_items %>
6 | <% end %>
7 |
--------------------------------------------------------------------------------
/app/views/shared/_feed_item.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to gravatar_for(feed_item.user), feed_item.user %>
3 |
4 | <%= link_to feed_item.user.name, feed_item.user %>
5 |
6 | <%= feed_item.content %>
7 | <%= feed_item.decorate.posted_ago %>
8 | <% if current_user?(feed_item.user) %>
9 | <%= link_to "delete", feed_item, method: :delete,
10 | data: { confirm: "You sure?" },
11 | title: feed_item.content %>
12 | <% end %>
13 |
14 |
--------------------------------------------------------------------------------
/app/views/shared/_micropost_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_for(@micropost) do |f| %>
2 | <%= render 'shared/error_messages', object: f.object %>
3 |
4 | <%= f.text_area :content, placeholder: "Compose new micropost..." %>
5 |
6 | <%= f.submit "Post", class: "btn btn-large btn-primary" %>
7 | <% end %>
8 |
--------------------------------------------------------------------------------
/app/views/shared/_stats.html.erb:
--------------------------------------------------------------------------------
1 | <% @user ||= current_user %>
2 |
16 |
--------------------------------------------------------------------------------
/app/views/shared/_user_info.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= gravatar_for current_user, size: 52 %>
3 |
4 |
5 | <%= current_user.name %>
6 |
7 |
8 | <%= link_to "view my profile", current_user %>
9 |
10 |
11 | <%= pluralize(current_user.microposts.count, "micropost") %>
12 |
13 |
--------------------------------------------------------------------------------
/app/views/static_pages/_contact_justin.html.erb:
--------------------------------------------------------------------------------
1 |
2 | You may email Justin Gordon about the refactorings in this tutorial.
3 | Read more about Justin at his blog Rails on Maui .
4 |
5 |
--------------------------------------------------------------------------------
/app/views/static_pages/about.html.erb:
--------------------------------------------------------------------------------
1 | <% provide(:title, 'About Us') %>
2 | About Us
3 |
4 | This example application teaches 4 Rails Refactoring techniques:
5 | Concerns, Decorators, Presenters, and Service Objects.
6 |
7 | <%= render 'contact_justin' %>
8 |
9 | This example builds on the Ruby on Rails Tutorial
10 | by Michael Hartl.
11 |
12 |
--------------------------------------------------------------------------------
/app/views/static_pages/contact.html.erb:
--------------------------------------------------------------------------------
1 | <% provide(:title, 'Contact') %>
2 | Contact
3 | <%= render 'contact_justin' %>
4 |
--------------------------------------------------------------------------------
/app/views/static_pages/help.html.erb:
--------------------------------------------------------------------------------
1 | <% provide(:title, 'Help') %>
2 | Help
3 |
4 | Please comment about this tutorial in either the
5 | fat-code-refactoring-techniques Github Issues
6 | or in the pull requests .
7 |
8 | <%= render 'contact_justin' %>
9 |
10 | Additional References:
11 |
19 |
20 |
--------------------------------------------------------------------------------
/app/views/static_pages/home.html.erb:
--------------------------------------------------------------------------------
1 | <% if signed_in? %>
2 |
3 |
4 |
5 | <%= render 'shared/user_info' %>
6 |
7 |
8 | <%= render 'shared/stats' %>
9 |
10 |
11 | <%= render 'shared/micropost_form' %>
12 |
13 |
14 |
15 |
Micropost Feed
16 | <%= render 'shared/feed' %>
17 |
18 |
19 | <% else %>
20 |
21 |
Welcome to the Kid Safe Microblogging App
22 |
23 |
28 |
29 | <%= link_to "Sign up now!", signup_path,
30 | class: "btn btn-large btn-primary" %>
31 |
32 |
33 | <%= link_to image_tag("rails.png", alt: "Rails"), 'http://rubyonrails.org/' %>
34 | <% end %>
35 |
--------------------------------------------------------------------------------
/app/views/static_pages/show.html.erb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakacode/fat-code-refactoring-techniques/a578449db549b4d367d354d2f94f1fd5c5bb5f8d/app/views/static_pages/show.html.erb
--------------------------------------------------------------------------------
/app/views/users/_follow.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_for(current_user.relationships.build(followed_id: @user.id),
2 | remote: true) do |f| %>
3 | <%= f.hidden_field :followed_id %>
4 | <%= f.submit "Follow", class: "btn btn-large btn-primary" %>
5 | <% end %>
6 |
--------------------------------------------------------------------------------
/app/views/users/_follow_form.html.erb:
--------------------------------------------------------------------------------
1 | <% unless current_user?(@user) %>
2 |
3 | <% if current_user.following?(@user) %>
4 | <%= render 'unfollow' %>
5 | <% else %>
6 | <%= render 'follow' %>
7 | <% end %>
8 |
9 | <% end %>
10 |
--------------------------------------------------------------------------------
/app/views/users/_unfollow.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_for(current_user.relationships.find_by(followed_id: @user.id),
2 | html: { method: :delete },
3 | remote: true) do |f| %>
4 | <%= f.submit "Unfollow", class: "btn btn-large" %>
5 | <% end %>
6 |
--------------------------------------------------------------------------------
/app/views/users/_user.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= gravatar_for user, size: 52 %>
3 | <%= link_to user.name, user %>
4 | <% if current_user.admin? && !current_user?(user) %>
5 | | <%= link_to "delete", user, method: :delete,
6 | data: { confirm: "You sure?" } %>
7 | <% end %>
8 |
9 |
--------------------------------------------------------------------------------
/app/views/users/edit.html.erb:
--------------------------------------------------------------------------------
1 | <% provide(:title, "Edit user") %>
2 | Update your profile
3 |
4 |
5 |
6 | <%= form_for(@user) do |f| %>
7 | <%= render 'shared/error_messages', object: f.object %>
8 |
9 | <%= f.label :name %>
10 | <%= f.text_field :name %>
11 |
12 | <%= f.label :email %>
13 | <%= f.text_field :email %>
14 |
15 | <%= f.label :password %>
16 | <%= f.password_field :password %>
17 |
18 | <%= f.label :password_confirmation, "Confirm Password" %>
19 | <%= f.password_field :password_confirmation %>
20 |
21 | <%= f.submit "Save changes", class: "btn btn-large btn-primary" %>
22 | <% end %>
23 |
24 | <%= gravatar_for @user %>
25 |
change
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/views/users/index.html.erb:
--------------------------------------------------------------------------------
1 | <% provide(:title, 'All users') %>
2 | All users
3 |
4 | <%= will_paginate %>
5 |
6 |
7 | <%= render @users %>
8 |
9 |
10 | <%= will_paginate %>
11 |
--------------------------------------------------------------------------------
/app/views/users/new.html.erb:
--------------------------------------------------------------------------------
1 | <% provide(:title, 'Sign up') %>
2 | Sign up
3 |
4 |
5 |
6 | <%= form_for(@user) do |f| %>
7 | <%= render 'shared/error_messages', object: f.object %>
8 |
9 | <%= f.label :name %>
10 | <%= f.text_field :name %>
11 |
12 | <%= f.label :email %>
13 | <%= f.text_field :email %>
14 |
15 | <%= f.label :password %>
16 | <%= f.password_field :password %>
17 |
18 | <%= f.label :password_confirmation, "Confirmation" %>
19 | <%= f.password_field :password_confirmation %>
20 |
21 | <%= f.submit "Create my account", class: "btn btn-large btn-primary" %>
22 | <% end %>
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/views/users/show.html.erb:
--------------------------------------------------------------------------------
1 | <% provide(:title, @user.name) %>
2 |
3 |
4 |
5 |
6 | <%= gravatar_for @user, size: 125 %>
7 | <%= @user.name %>
8 | <% if @user.minor? %>
9 |
10 | A Minor
11 | <% if @user.profanity_count > 0 %>
12 |
13 |
14 | <%= pluralize(@user.profanity_count, "Profanity") %>
15 |
16 | <% end %>
17 | <% end %>
18 |
19 |
20 |
21 | <%= render 'shared/stats' %>
22 |
23 |
24 |
25 | <%= render 'follow_form' if signed_in? %>
26 | <% if @user.microposts.any? %>
27 |
Microposts (<%= @user.microposts.count %>)
28 |
29 | <%= render @microposts %>
30 |
31 | <%= will_paginate @microposts %>
32 | <% end %>
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/views/users/show_follow.html.erb:
--------------------------------------------------------------------------------
1 | <% provide(:title, @presenter.title) %>
2 |
3 | <% cache @presenter.cache_key do %>
4 |
5 |
23 |
24 |
<%= @presenter.subtitle %>
25 | <% if @presenter.users.any? %>
26 |
27 | <%= render collection: @presenter.users, partial: 'users/user' %>
28 |
29 | <%= will_paginate @presenter.users %>
30 | <% end %>
31 |
32 |
33 | <% end %>
34 |
--------------------------------------------------------------------------------
/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/git-railsconf.zsh:
--------------------------------------------------------------------------------
1 | #!/bin/zsh
2 |
3 | # cd to top level of this git repo, then run
4 |
5 | # . bin/git-railsconf.zsh
6 |
7 | # BE SURE TO SET RAILSCONF_DEMO=
8 | export RAILSCONF_DEMO=`pwd`
9 |
10 | git-child-sha() {
11 | branch=${1:-master}
12 | git log --ancestry-path --format=%H ${commit}..$branch | tail -1
13 | }
14 |
15 | git-advance-history() {
16 | branch=${1:-master}
17 | sha=$(git-child-sha $branch)
18 | git --no-pager show --pretty --quiet $sha
19 | git checkout $sha
20 | }
21 |
22 | git-advance-history-reset-soft() {
23 | branch=${1:-master}
24 | git reset --hard HEAD
25 | git-advance-history $branch
26 | git-advance-history $branch
27 | git reset --soft HEAD\^
28 | }
29 |
30 |
31 |
32 | # START HERE
33 | railsconf-start() {
34 | cd $RAILSCONF_DEMO
35 | git checkout railsconf-start
36 | git-advance-history railsconf-finish
37 | git reset --soft HEAD\^
38 | }
39 |
40 |
41 | # ADVANCE BY USING THIS
42 | # Assumes starting point
43 | # 1. is NOT a branch
44 | # 2. has files checked out
45 | # NOTE: goes a git reset --hard, so you lose any changes!
46 | railsconf-advance-history() {
47 | git-advance-history-reset-soft railsconf-finish
48 | }
49 |
50 |
51 |
--------------------------------------------------------------------------------
/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 SampleApp::Application
5 |
--------------------------------------------------------------------------------
/config/application.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../boot', __FILE__)
2 |
3 | # Pick the frameworks you want:
4 | require "active_record/railtie"
5 | require "action_controller/railtie"
6 | require "action_mailer/railtie"
7 | require "sprockets/railtie"
8 | # require "rails/test_unit/railtie"
9 |
10 | # Assets should be precompiled for production (so we don't need the gems loaded then)
11 | Bundler.require(*Rails.groups(assets: %w(development test)))
12 |
13 | module SampleApp
14 | class Application < Rails::Application
15 | # Settings in config/environments/* take precedence over those specified here.
16 | # Application configuration should go into files in config/initializers
17 | # -- all .rb files in that directory are automatically loaded.
18 |
19 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
20 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
21 | # config.time_zone = 'Central Time (US & Canada)'
22 |
23 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
24 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
25 | # config.i18n.default_locale = :de
26 | # I18n.enforce_available_locales = true
27 | I18n.enforce_available_locales = true
28 |
29 | config.assets.precompile += %w(*.png *.jpg *.jpeg *.gif)
30 |
31 | config.autoload_paths += %W(
32 | #{config.root}/app/presenters
33 | )
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/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.exists?(ENV['BUNDLE_GEMFILE'])
5 |
--------------------------------------------------------------------------------
/config/cucumber.yml:
--------------------------------------------------------------------------------
1 | <%
2 | rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : ""
3 | rerun_opts = rerun.to_s.strip.empty? ? "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}"
4 | std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} --strict --tags ~@wip"
5 | %>
6 | default: <%= std_opts %> features
7 | wip: --tags @wip:3 --wip features
8 | rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip
9 |
--------------------------------------------------------------------------------
/config/database.yml.example:
--------------------------------------------------------------------------------
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 | development:
7 | adapter: sqlite3
8 | database: db/development.sqlite3
9 | pool: 5
10 | timeout: 5000
11 |
12 | # Warning: The database defined as "test" will be erased and
13 | # re-generated from your development database when you run "rake".
14 | # Do not set this db to the same as development or production.
15 | test: &test
16 | adapter: sqlite3
17 | database: db/test.sqlite3
18 | pool: 5
19 | timeout: 5000
20 |
21 | production:
22 | adapter: sqlite3
23 | database: db/production.sqlite3
24 | pool: 5
25 | timeout: 5000
26 |
27 | cucumber:
28 | <<: *test
--------------------------------------------------------------------------------
/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the rails application.
2 | require File.expand_path('../application', __FILE__)
3 |
4 | # Initialize the rails application.
5 | SampleApp::Application.initialize!
6 |
--------------------------------------------------------------------------------
/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | SampleApp::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 = false
15 |
16 | # Don't care if the mailer can't send.
17 | config.action_mailer.raise_delivery_errors = false
18 |
19 | # Print deprecation notices to the Rails logger.
20 | config.active_support.deprecation = :log
21 |
22 | # Raise an error on page load if there are pending migrations
23 | config.active_record.migration_error = :page_load
24 |
25 | # Debug mode disables concatenation and preprocessing of assets.
26 | config.assets.debug = true
27 | end
28 |
--------------------------------------------------------------------------------
/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | SampleApp::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 thread 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 | config.serve_static_assets = false
23 |
24 | # Compress JavaScripts and CSS.
25 | config.assets.js_compressor = :uglifier
26 | # config.assets.css_compressor = :sass
27 |
28 | # Whether to fallback to assets pipeline if a precompiled asset is missed.
29 | config.assets.compile = false
30 |
31 | # Generate digests for assets URLs.
32 | config.assets.digest = true
33 |
34 | # Version of your assets, change this if you want to expire all your assets.
35 | config.assets.version = '1.0'
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 = false
43 |
44 | # Set to :debug to see everything in the log.
45 | config.log_level = :debug
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 = :mem_cache_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 | # Precompile additional assets.
60 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
61 | # config.assets.precompile += %w( search.js )
62 |
63 | # Ignore bad email addresses and do not raise email delivery errors.
64 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
65 | # config.action_mailer.raise_delivery_errors = false
66 |
67 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
68 | # the I18n.default_locale when a translation can not be found).
69 | config.i18n.fallbacks = true
70 |
71 | # Send deprecation notices to registered listeners.
72 | config.active_support.deprecation = :notify
73 |
74 | # Disable automatic flushing of the log to improve performance.
75 | # config.autoflush_log = false
76 |
77 | # Use default logging formatter so that PID and timestamp are not suppressed.
78 | config.log_formatter = ::Logger::Formatter.new
79 | end
80 |
--------------------------------------------------------------------------------
/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | SampleApp::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_assets = 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 | # Speed up tests by lowering bcrypt's cost function.
38 | ActiveModel::SecurePassword.min_cost = true
39 | end
40 |
--------------------------------------------------------------------------------
/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/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/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 | # Mime::Type.register_alias "text/html", :iphone
6 |
--------------------------------------------------------------------------------
/config/initializers/secret_token.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Make sure your secret_key_base is kept private
4 | # if you're sharing your code publicly, such as by adding
5 | # .secret to your .gitignore file.
6 |
7 | require 'securerandom'
8 |
9 | def secure_token
10 | token_file = Rails.root.join('.secret')
11 | if File.exist?(token_file)
12 | # Use the existing token.
13 | File.read(token_file).chomp
14 | else
15 | # Generate a new token and store it in token_file.
16 | token = SecureRandom.hex(64)
17 | File.write(token_file, token)
18 | token
19 | end
20 | end
21 |
22 | SampleApp::Application.config.secret_key_base = secure_token
--------------------------------------------------------------------------------
/config/initializers/session_store.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | SampleApp::Application.config.session_store :cookie_store, key: '_sample_app_session'
4 |
--------------------------------------------------------------------------------
/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 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # To learn more, please read the Rails Internationalization guide
20 | # available at http://guides.rubyonrails.org/i18n.html.
21 |
22 | en:
23 | hello: "Hello world"
24 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | SampleApp::Application.routes.draw do
2 | resources :users do
3 | scope module: 'users' do
4 | member do
5 | get :followers, controller: 'followers'
6 | get :following, controller: 'following'
7 | end
8 | end
9 | end
10 |
11 | resources :sessions, only: [:new, :create, :destroy]
12 | resources :microposts, only: [:create, :destroy]
13 | resources :relationships, only: [:create, :destroy]
14 | root to: 'static_pages#home'
15 | match '/signup', to: 'users#new', via: 'get'
16 | match '/signin', to: 'sessions#new', via: 'get'
17 | match '/signout', to: 'sessions#destroy', via: 'delete'
18 | match '/help', to: 'static_pages#help', via: 'get'
19 | match '/about', to: 'static_pages#about', via: 'get'
20 | match '/contact', to: 'static_pages#contact', via: 'get'
21 | end
22 |
--------------------------------------------------------------------------------
/db/migrate/20130311191400_create_users.rb:
--------------------------------------------------------------------------------
1 | class CreateUsers < ActiveRecord::Migration
2 | def change
3 | create_table :users do |t|
4 | t.string :name
5 | t.string :email
6 |
7 | t.timestamps
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/db/migrate/20130311194153_add_index_to_users_email.rb:
--------------------------------------------------------------------------------
1 | class AddIndexToUsersEmail < ActiveRecord::Migration
2 | def change
3 | add_index :users, :email, unique: true
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20130311201841_add_password_digest_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddPasswordDigestToUsers < ActiveRecord::Migration
2 | def change
3 | add_column :users, :password_digest, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20130314184954_add_remember_token_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddRememberTokenToUsers < ActiveRecord::Migration
2 | def change
3 | add_column :users, :remember_token, :string
4 | add_index :users, :remember_token
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/db/migrate/20130315015932_add_admin_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddAdminToUsers < ActiveRecord::Migration
2 | def change
3 | add_column :users, :admin, :boolean
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20130315175534_create_microposts.rb:
--------------------------------------------------------------------------------
1 | class CreateMicroposts < ActiveRecord::Migration
2 | def change
3 | create_table :microposts do |t|
4 | t.string :content
5 | t.integer :user_id
6 |
7 | t.timestamps
8 | end
9 | add_index :microposts, [:user_id, :created_at]
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/db/migrate/20130315230445_create_relationships.rb:
--------------------------------------------------------------------------------
1 | class CreateRelationships < ActiveRecord::Migration
2 | def change
3 | create_table :relationships do |t|
4 | t.integer :follower_id
5 | t.integer :followed_id
6 |
7 | t.timestamps
8 | end
9 | add_index :relationships, :follower_id
10 | add_index :relationships, :followed_id
11 | add_index :relationships, [:follower_id, :followed_id], unique: true
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/db/migrate/20140405044857_add_profanity_counter_to_user.rb:
--------------------------------------------------------------------------------
1 | class AddProfanityCounterToUser < ActiveRecord::Migration
2 | def change
3 | add_column :users, :profanity_count, :integer, default: 0
4 | add_column :users, :minor, :boolean, default: false
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/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: 20140405044857) do
15 |
16 | create_table "microposts", force: true do |t|
17 | t.string "content"
18 | t.integer "user_id"
19 | t.datetime "created_at"
20 | t.datetime "updated_at"
21 | end
22 |
23 | add_index "microposts", ["user_id", "created_at"], name: "index_microposts_on_user_id_and_created_at"
24 |
25 | create_table "relationships", force: true do |t|
26 | t.integer "follower_id"
27 | t.integer "followed_id"
28 | t.datetime "created_at"
29 | t.datetime "updated_at"
30 | end
31 |
32 | add_index "relationships", ["followed_id"], name: "index_relationships_on_followed_id"
33 | add_index "relationships", ["follower_id", "followed_id"], name: "index_relationships_on_follower_id_and_followed_id", unique: true
34 | add_index "relationships", ["follower_id"], name: "index_relationships_on_follower_id"
35 |
36 | create_table "users", force: true do |t|
37 | t.string "name"
38 | t.string "email"
39 | t.datetime "created_at"
40 | t.datetime "updated_at"
41 | t.string "password_digest"
42 | t.string "remember_token"
43 | t.boolean "admin"
44 | t.integer "profanity_count", default: 0
45 | t.boolean "minor", default: false
46 | end
47 |
48 | add_index "users", ["email"], name: "index_users_on_email", unique: true
49 | add_index "users", ["remember_token"], name: "index_users_on_remember_token"
50 |
51 | end
52 |
--------------------------------------------------------------------------------
/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # This file should contain all the record creation needed to seed the database with its default values.
2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
3 | #
4 | # Examples:
5 | #
6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
7 | # Mayor.create(name: 'Emanuel', city: cities.first)
8 |
--------------------------------------------------------------------------------
/features/signing_in.feature:
--------------------------------------------------------------------------------
1 | Feature: Signing in
2 |
3 | Scenario: Unsuccessful signin
4 | Given a user visits the signin page
5 | When they submit invalid signin information
6 | Then they should see an error message
7 |
8 | Scenario: Successful signin
9 | Given a user visits the signin page
10 | And the user has an account
11 | When the user submits valid signin information
12 | Then they should see their profile page
13 | And they should see a signout link
--------------------------------------------------------------------------------
/features/step_definitions/authentication_steps.rb:
--------------------------------------------------------------------------------
1 | Given /^a user visits the signin page$/ do
2 | visit signin_path
3 | end
4 |
5 | When /^they submit invalid signin information$/ do
6 | click_button "Sign in"
7 | end
8 |
9 | Then /^they should see an error message$/ do
10 | expect(page).to have_selector('div.alert.alert-error')
11 | end
12 |
13 | Given /^the user has an account$/ do
14 | @user = User.create(name: "Example User", email: "user@example.com",
15 | password: "foobar", password_confirmation: "foobar")
16 | end
17 |
18 | When /^the user submits valid signin information$/ do
19 | fill_in "Email", with: @user.email
20 | fill_in "Password", with: @user.password
21 | click_button "Sign in"
22 | end
23 |
24 | Then /^they should see their profile page$/ do
25 | expect(page).to have_title(@user.name)
26 | end
27 |
28 | Then /^they should see a signout link$/ do
29 | expect(page).to have_link('Sign out', href: signout_path)
30 | end
--------------------------------------------------------------------------------
/features/support/env.rb:
--------------------------------------------------------------------------------
1 | # IMPORTANT: This file is generated by cucumber-rails - edit at your own peril.
2 | # It is recommended to regenerate this file in the future when you upgrade to a
3 | # newer version of cucumber-rails. Consider adding your own code to a new file
4 | # instead of editing this one. Cucumber will automatically load all features/**/*.rb
5 | # files.
6 |
7 | require 'cucumber/rails'
8 |
9 | # Capybara defaults to XPath selectors rather than Webrat's default of CSS3. In
10 | # order to ease the transition to Capybara we set the default here. If you'd
11 | # prefer to use XPath just remove this line and adjust any selectors in your
12 | # steps to use the XPath syntax.
13 | Capybara.default_selector = :css
14 |
15 | # By default, any exception happening in your Rails application will bubble up
16 | # to Cucumber so that your scenario will fail. This is a different from how
17 | # your application behaves in the production environment, where an error page will
18 | # be rendered instead.
19 | #
20 | # Sometimes we want to override this default behaviour and allow Rails to rescue
21 | # exceptions and display an error page (just like when the app is running in production).
22 | # Typical scenarios where you want to do this is when you test your error pages.
23 | # There are two ways to allow Rails to rescue exceptions:
24 | #
25 | # 1) Tag your scenario (or feature) with @allow-rescue
26 | #
27 | # 2) Set the value below to true. Beware that doing this globally is not
28 | # recommended as it will mask a lot of errors for you!
29 | #
30 | ActionController::Base.allow_rescue = false
31 |
32 | # Remove/comment out the lines below if your app doesn't have a database.
33 | # For some databases (like MongoDB and CouchDB) you may need to use :truncation instead.
34 | begin
35 | DatabaseCleaner.strategy = :transaction
36 | rescue NameError
37 | raise "You need to add database_cleaner to your Gemfile (in the :test group) if you wish to use it."
38 | end
39 |
40 | # You may also want to configure DatabaseCleaner to use different strategies for certain features and scenarios.
41 | # See the DatabaseCleaner documentation for details. Example:
42 | #
43 | # Before('@no-txn,@selenium,@culerity,@celerity,@javascript') do
44 | # # { :except => [:widgets] } may not do what you expect here
45 | # # as tCucumber::Rails::Database.javascript_strategy overrides
46 | # # this setting.
47 | # DatabaseCleaner.strategy = :truncation
48 | # end
49 | #
50 | # Before('~@no-txn', '~@selenium', '~@culerity', '~@celerity', '~@javascript') do
51 | # DatabaseCleaner.strategy = :transaction
52 | # end
53 | #
54 |
55 | # Possible values are :truncation and :transaction
56 | # The :transaction strategy is faster, but might give you threading problems.
57 | # See https://github.com/cucumber/cucumber-rails/blob/master/features/choose_javascript_database_strategy.feature
58 | Cucumber::Rails::Database.javascript_strategy = :truncation
59 |
60 |
--------------------------------------------------------------------------------
/lib/assets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakacode/fat-code-refactoring-techniques/a578449db549b4d367d354d2f94f1fd5c5bb5f8d/lib/assets/.keep
--------------------------------------------------------------------------------
/lib/tasks/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakacode/fat-code-refactoring-techniques/a578449db549b4d367d354d2f94f1fd5c5bb5f8d/lib/tasks/.keep
--------------------------------------------------------------------------------
/lib/tasks/cucumber.rake:
--------------------------------------------------------------------------------
1 | # IMPORTANT: This file is generated by cucumber-rails - edit at your own peril.
2 | # It is recommended to regenerate this file in the future when you upgrade to a
3 | # newer version of cucumber-rails. Consider adding your own code to a new file
4 | # instead of editing this one. Cucumber will automatically load all features/**/*.rb
5 | # files.
6 |
7 |
8 | unless ARGV.any? {|a| a =~ /^gems/} # Don't load anything when running the gems:* tasks
9 |
10 | vendored_cucumber_bin = Dir["#{Rails.root}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first
11 | $LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil?
12 |
13 | begin
14 | require 'cucumber/rake/task'
15 |
16 | namespace :cucumber do
17 | Cucumber::Rake::Task.new({:ok => 'db:test:prepare'}, 'Run features that should pass') do |t|
18 | t.binary = vendored_cucumber_bin # If nil, the gem's binary is used.
19 | t.fork = true # You may get faster startup if you set this to false
20 | t.profile = 'default'
21 | end
22 |
23 | Cucumber::Rake::Task.new({:wip => 'db:test:prepare'}, 'Run features that are being worked on') do |t|
24 | t.binary = vendored_cucumber_bin
25 | t.fork = true # You may get faster startup if you set this to false
26 | t.profile = 'wip'
27 | end
28 |
29 | Cucumber::Rake::Task.new({:rerun => 'db:test:prepare'}, 'Record failing features and run only them if any exist') do |t|
30 | t.binary = vendored_cucumber_bin
31 | t.fork = true # You may get faster startup if you set this to false
32 | t.profile = 'rerun'
33 | end
34 |
35 | desc 'Run all features'
36 | task :all => [:ok, :wip]
37 |
38 | task :statsetup do
39 | require 'rails/code_statistics'
40 | ::STATS_DIRECTORIES << %w(Cucumber\ features features) if File.exist?('features')
41 | ::CodeStatistics::TEST_TYPES << "Cucumber features" if File.exist?('features')
42 | end
43 | end
44 | desc 'Alias for cucumber:ok'
45 | task :cucumber => 'cucumber:ok'
46 |
47 | task :default => :cucumber
48 |
49 | task :features => :cucumber do
50 | STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***"
51 | end
52 |
53 | # In case we don't have ActiveRecord, append a no-op task that we can depend upon.
54 | task 'db:test:prepare' do
55 | end
56 |
57 | task :stats => 'cucumber:statsetup'
58 | rescue LoadError
59 | desc 'cucumber rake task not available (cucumber not installed)'
60 | task :cucumber do
61 | abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin'
62 | end
63 | end
64 |
65 | end
66 |
--------------------------------------------------------------------------------
/lib/tasks/sample_data.rake:
--------------------------------------------------------------------------------
1 | namespace :db do
2 | desc "Fill database with sample data"
3 | task populate: :environment do
4 | make_users
5 | make_microposts
6 | make_relationships
7 | end
8 | end
9 |
10 | def make_users
11 | admin = User.create!(name: "Example User",
12 | email: "example@railstutorial.org",
13 | password: "foobar",
14 | password_confirmation: "foobar",
15 | admin: true)
16 | minor = User.create!(name: "Little Punk",
17 | email: "littlepunk@sugarranchmaui.com",
18 | password: "foobar",
19 | password_confirmation: "foobar",
20 | admin: false,
21 | minor: true)
22 | 99.times do |n|
23 | name = Faker::Name.name
24 | email = "example-#{n+1}@railstutorial.org"
25 | password = "password"
26 | User.create!(name: name,
27 | email: email,
28 | password: password,
29 | password_confirmation: password)
30 | end
31 | end
32 |
33 | def make_microposts
34 | users = User.all(limit: 6)
35 | 50.times do
36 | content = Faker::Lorem.sentence(5)
37 | users.each { |user| user.microposts.create!(content: content) }
38 | end
39 | end
40 |
41 | def make_relationships
42 | users = User.all
43 | user = users.first
44 | followed_users = users[2..50]
45 | followers = users[3..40]
46 | followed_users.each { |followed| user.follow!(followed) }
47 | followers.each { |follower| follower.follow!(user) }
48 | end
49 |
--------------------------------------------------------------------------------
/log/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakacode/fat-code-refactoring-techniques/a578449db549b4d367d354d2f94f1fd5c5bb5f8d/log/.keep
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
The page you were looking for doesn't exist.
23 |
You may have mistyped the address or the page may have moved.
24 |
25 | If you are the application owner check the logs for more information.
26 |
27 |
28 |
--------------------------------------------------------------------------------
/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
The change you wanted was rejected.
23 |
Maybe you tried to change something you didn't have access to.
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
We're sorry, but something went wrong.
23 |
24 | If you are the application owner check the logs for more information.
25 |
26 |
27 |
--------------------------------------------------------------------------------
/public/assets/application-4962059d8f80f9bb096692bacc29c4e8.css.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakacode/fat-code-refactoring-techniques/a578449db549b4d367d354d2f94f1fd5c5bb5f8d/public/assets/application-4962059d8f80f9bb096692bacc29c4e8.css.gz
--------------------------------------------------------------------------------
/public/assets/application-eeb856e3fe2c8f879c91d0e81d59cb40.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakacode/fat-code-refactoring-techniques/a578449db549b4d367d354d2f94f1fd5c5bb5f8d/public/assets/application-eeb856e3fe2c8f879c91d0e81d59cb40.js.gz
--------------------------------------------------------------------------------
/public/assets/glyphicons-halflings-c806376f05e4ccabe2c5315a8e95667c.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakacode/fat-code-refactoring-techniques/a578449db549b4d367d354d2f94f1fd5c5bb5f8d/public/assets/glyphicons-halflings-c806376f05e4ccabe2c5315a8e95667c.png
--------------------------------------------------------------------------------
/public/assets/glyphicons-halflings-white-62b67d9edee3db90d18833087f848d6e.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakacode/fat-code-refactoring-techniques/a578449db549b4d367d354d2f94f1fd5c5bb5f8d/public/assets/glyphicons-halflings-white-62b67d9edee3db90d18833087f848d6e.png
--------------------------------------------------------------------------------
/public/assets/manifest-802de9eb1c853769101852422b620883.json:
--------------------------------------------------------------------------------
1 | {"files":{"rails-231a680f23887d9dd70710ea5efd3c62.png":{"logical_path":"rails.png","mtime":"2013-03-06T18:36:31-08:00","size":6646,"digest":"231a680f23887d9dd70710ea5efd3c62"},"application-eeb856e3fe2c8f879c91d0e81d59cb40.js":{"logical_path":"application.js","mtime":"2013-09-06T09:23:56-07:00","size":363970,"digest":"eeb856e3fe2c8f879c91d0e81d59cb40"},"application-4962059d8f80f9bb096692bacc29c4e8.css":{"logical_path":"application.css","mtime":"2013-09-06T13:20:19-07:00","size":134254,"digest":"4962059d8f80f9bb096692bacc29c4e8"},"glyphicons-halflings-white-62b67d9edee3db90d18833087f848d6e.png":{"logical_path":"glyphicons-halflings-white.png","mtime":"2013-06-24T16:40:09-07:00","size":8777,"digest":"62b67d9edee3db90d18833087f848d6e"},"glyphicons-halflings-c806376f05e4ccabe2c5315a8e95667c.png":{"logical_path":"glyphicons-halflings.png","mtime":"2013-06-24T16:40:09-07:00","size":12799,"digest":"c806376f05e4ccabe2c5315a8e95667c"}},"assets":{"rails.png":"rails-231a680f23887d9dd70710ea5efd3c62.png","application.js":"application-eeb856e3fe2c8f879c91d0e81d59cb40.js","application.css":"application-4962059d8f80f9bb096692bacc29c4e8.css","glyphicons-halflings-white.png":"glyphicons-halflings-white-62b67d9edee3db90d18833087f848d6e.png","glyphicons-halflings.png":"glyphicons-halflings-c806376f05e4ccabe2c5315a8e95667c.png"}}
--------------------------------------------------------------------------------
/public/assets/rails-231a680f23887d9dd70710ea5efd3c62.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakacode/fat-code-refactoring-techniques/a578449db549b4d367d354d2f94f1fd5c5bb5f8d/public/assets/rails-231a680f23887d9dd70710ea5efd3c62.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakacode/fat-code-refactoring-techniques/a578449db549b4d367d354d2f94f1fd5c5bb5f8d/public/favicon.ico
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
2 | #
3 | # To ban all spiders from the entire site uncomment the next two lines:
4 | # User-agent: *
5 | # Disallow: /
6 |
--------------------------------------------------------------------------------
/script/cucumber:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | vendored_cucumber_bin = Dir["#{File.dirname(__FILE__)}/../vendor/{gems,plugins}/cucumber*/bin/cucumber"].first
4 | if vendored_cucumber_bin
5 | load File.expand_path(vendored_cucumber_bin)
6 | else
7 | require 'rubygems' unless ENV['NO_RUBYGEMS']
8 | require 'cucumber'
9 | load Cucumber::BINARY
10 | end
11 |
--------------------------------------------------------------------------------
/spec/controllers/microposts_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | describe MicropostsController do
3 | let(:minor) { FactoryGirl.create :user, minor: true, profanity_count: 0 }
4 | let(:adult) { FactoryGirl.create :user, minor: false, profanity_count: 0 }
5 | let(:profanity_content) { "Dad is a poopface and fartface!" }
6 | let(:clean_content) { "Dad is the best!" }
7 |
8 | def flash_now
9 | flash.instance_variable_get(:@now)
10 | end
11 |
12 | context "does contain two word profanity" do
13 | subject(:service) { MicropostCreationService.new(minor, profanity_content) }
14 | context "a minor" do
15 | before { sign_in minor, no_capybara: true }
16 |
17 | it "gives a flash with poopface and fartface" do
18 | post :create, micropost: { content: profanity_content }
19 |
20 | # Demonstrate testing flash.now
21 | expect(flash_now[:error]).to match /poopface/
22 | expect(flash_now[:error]).to match /fartface/
23 | expect(flash_now[:error]).to match /2 times/
24 |
25 | # Demonstrate checking rendered template
26 | expect(response).to render_template('static_pages/home')
27 |
28 | # Demonstrate checking instance variable
29 | expect(assigns(:micropost).content).to eq(profanity_content)
30 |
31 | expect(response.status).to eq(200)
32 | end
33 |
34 | it "increases the minor's profanity count by 2" do
35 | expect do
36 | post :create, micropost: { content: profanity_content }
37 | end.to change { minor.reload.profanity_count }.by(2)
38 | end
39 |
40 | it "does not increase the number of posts" do
41 | expect do
42 | post :create, micropost: { content: profanity_content }
43 | end.not_to change { Micropost.count }
44 | end
45 | end
46 |
47 | context "adult" do
48 | before { sign_in adult, no_capybara: true }
49 |
50 | it "gives a flash with poopface and fartface" do
51 | post :create, micropost: { content: profanity_content }
52 | expect(flash[:success]).to eq("Micropost created!")
53 | expect(response).to redirect_to(root_url)
54 | end
55 |
56 | it "does not change the profanity count" do
57 | expect do
58 | post :create, micropost: { content: profanity_content }
59 | end.not_to change { adult.reload.profanity_count }
60 | end
61 |
62 | it "does increase the number of posts" do
63 | expect do
64 | post :create, micropost: { content: profanity_content }
65 | end.to change { Micropost.count }.by(1)
66 | end
67 | end
68 | end
69 | context "no profanity" do
70 | context "minor" do
71 | before { sign_in minor, no_capybara: true }
72 |
73 | it "gives a flash with poopface and fartface" do
74 | post :create, micropost: { content: clean_content }
75 | expect(flash[:success]).to eq("Micropost created!")
76 | expect(response).to redirect_to(root_url)
77 | end
78 |
79 | it "does not change the profanity count" do
80 | expect do
81 | post :create, micropost: { content: clean_content }
82 | end.not_to change { minor.reload.profanity_count }
83 | end
84 |
85 | it "does increase the number of posts" do
86 | expect do
87 | post :create, micropost: { content: clean_content }
88 | end.to change { Micropost.count }.by(1)
89 | end
90 | end
91 | end
92 | end
93 |
--------------------------------------------------------------------------------
/spec/controllers/relationships_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe RelationshipsController do
4 |
5 | let(:user) { FactoryGirl.create(:user) }
6 | let(:other_user) { FactoryGirl.create(:user) }
7 |
8 | before { sign_in user, no_capybara: true }
9 |
10 | describe "creating a relationship with Ajax" do
11 |
12 | it "should increment the Relationship count" do
13 | expect do
14 | xhr :post, :create, relationship: { followed_id: other_user.id }
15 | end.to change(Relationship, :count).by(1)
16 | end
17 |
18 | it "should respond with success" do
19 | xhr :post, :create, relationship: { followed_id: other_user.id }
20 | expect(response).to be_success
21 | end
22 | end
23 |
24 | describe "destroying a relationship with Ajax" do
25 |
26 | before { user.follow!(other_user) }
27 | let(:relationship) do
28 | user.relationships.find_by(followed_id: other_user.id)
29 | end
30 |
31 | it "should decrement the Relationship count" do
32 | expect do
33 | xhr :delete, :destroy, id: relationship.id
34 | end.to change(Relationship, :count).by(-1)
35 | end
36 |
37 | it "should respond with success" do
38 | xhr :delete, :destroy, id: relationship.id
39 | expect(response).to be_success
40 | end
41 | end
42 | end
--------------------------------------------------------------------------------
/spec/decorators/micropost_decorator_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe MicropostDecorator do
4 |
5 | describe "posted_ago" do
6 | let(:user) { FactoryGirl.create(:user) }
7 | it "prints time ago in words" do
8 | micropost = Micropost.create(content: "Lorem ipsum", user_id: user.id)
9 | expect(micropost.decorate.posted_ago).to match /Posted less than a minute ago./
10 | end
11 | end
12 |
13 | describe "profanity_violation_msg" do
14 | let(:user) { FactoryGirl.create(:user, profanity_count: 20) }
15 | it "creates a message" do
16 | micropost = Micropost.create(content: "Lorem poopface, fartface", user_id: user.id)
17 | profanity_violation_msg = micropost.decorate.profanity_violation_msg
18 | expect(profanity_violation_msg).to match /poopface/
19 | expect(profanity_violation_msg).to match /fartface/
20 | expect(profanity_violation_msg).to match /20 times/
21 | expect(profanity_violation_msg).to match /Your parents have been notified!/
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/spec/factories.rb:
--------------------------------------------------------------------------------
1 | FactoryGirl.define do
2 | factory :user do
3 | sequence(:name) { |n| "Person #{n}" }
4 | sequence(:email) { |n| "person_#{n}@example.com"}
5 | password "foobar"
6 | password_confirmation "foobar"
7 |
8 | factory :admin do
9 | admin true
10 | end
11 | end
12 |
13 | factory :micropost do
14 | content "Lorem ipsum"
15 | user
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/spec/helpers/application_helper_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe ApplicationHelper do
4 |
5 | describe "full_title" do
6 | it "should include the page title" do
7 | expect(full_title("foo")).to match(/foo/)
8 | end
9 |
10 | it "should include the base title" do
11 | expect(full_title("foo")).to match(/^Kid Blogger/)
12 | end
13 |
14 | it "should not include a bar for the home page" do
15 | expect(full_title("")).not_to match(/\|/)
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/spec/models/concerns/emailable_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 |
4 | describe User do
5 |
6 | before do
7 | @user = User.new(name: "Example User", email: "user@example.com",
8 | password: "foobar", password_confirmation: "foobar")
9 | end
10 |
11 | subject { @user }
12 |
13 | describe "#email_domain" do
14 | it "should equal example.com" do
15 | expect(@user.email_domain).to eq("example.com")
16 | end
17 | end
18 |
19 | describe "Email Validation" do
20 | describe "when email is not present" do
21 | before { @user.email = " " }
22 | it { should_not be_valid }
23 | end
24 |
25 | describe "when email format is invalid" do
26 | it "should be invalid" do
27 | addresses = %w[user@foo,com user_at_foo.org example.user@foo.
28 | foo@bar_baz.com foo@bar+baz.com foo@bar..com]
29 | addresses.each do |invalid_address|
30 | @user.email = invalid_address
31 | expect(@user).not_to be_valid
32 | end
33 | end
34 | end
35 |
36 | describe "when email format is valid" do
37 | it "should be valid" do
38 | addresses = %w[user@foo.COM A_US-ER@f.b.org frst.lst@foo.jp a+b@baz.cn]
39 | addresses.each do |valid_address|
40 | @user.email = valid_address
41 | expect(@user).to be_valid
42 | end
43 | end
44 | end
45 |
46 | describe "when email address is already taken" do
47 | before do
48 | user_with_same_email = @user.dup
49 | user_with_same_email.email = @user.email.upcase
50 | user_with_same_email.save
51 | end
52 |
53 | it { should_not be_valid }
54 | end
55 | end
56 | end
57 |
58 | describe "User email queries" do
59 | before do
60 | @u1 = FactoryGirl.create(:user, email: "foobar@baz.com")
61 | @u2 = FactoryGirl.create(:user, email: "foobaz@bar.com")
62 | @u3 = FactoryGirl.create(:user, email: "abcdef@ghi.com")
63 | end
64 |
65 | describe ".by_email" do
66 | context "Using lower case" do
67 | it "finds the correct user" do
68 | expect(User.by_email(@u2.email).id).to eq(@u2.id)
69 | end
70 | end
71 |
72 | context "Using wrong case" do
73 | it "finds the correct user" do
74 | expect(User.by_email(@u1.email.upcase).id).to eq(@u1.id)
75 | end
76 | end
77 |
78 | context "Using no user" do
79 | it "finds no user" do
80 | expect(User.by_email("junk")).to be_nil
81 | end
82 | end
83 | end
84 |
85 | describe ".by_email_wildcard" do
86 | context "Using lower case" do
87 | it "finds the correct user" do
88 | expect(User.by_email_wildcard("foo").pluck(:id)).to match_array([@u1.id, @u2.id])
89 | end
90 | end
91 |
92 | context "Using wrong case" do
93 | it "finds the correct user" do
94 | expect(User.by_email_wildcard("FoO").pluck(:id)).to match_array([@u1.id, @u2.id])
95 | end
96 | end
97 |
98 | context "Using no match" do
99 | it "finds the correct user" do
100 | expect(User.by_email_wildcard("junk")).to be_empty
101 | end
102 | end
103 | end
104 | end
105 |
106 |
--------------------------------------------------------------------------------
/spec/models/micropost_spec.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: microposts
4 | #
5 | # id :integer not null, primary key
6 | # content :string(255)
7 | # user_id :integer
8 | # created_at :datetime
9 | # updated_at :datetime
10 | #
11 |
12 | require 'spec_helper'
13 |
14 | describe Micropost do
15 |
16 | let(:user) { FactoryGirl.create(:user) }
17 | before do
18 | # This code is not idiomatically correct.
19 | @micropost = Micropost.new(content: "Lorem ipsum", user_id: user.id)
20 | end
21 |
22 | subject { @micropost }
23 |
24 | it { should respond_to(:content) }
25 | it { should respond_to(:user_id) }
26 |
27 | describe "when user_id is not present" do
28 | before { @micropost.user_id = nil }
29 | it { should_not be_valid }
30 | end
31 |
32 | describe "with blank content" do
33 | before { @micropost.content = " " }
34 | it { should_not be_valid }
35 | end
36 |
37 | describe "with content that is too long" do
38 | before { @micropost.content = "a" * 141 }
39 | it { should_not be_valid }
40 | end
41 | end
42 |
43 | describe "profanity checking" do
44 | let(:profane_content) { "Dad is a poopface and fartface!" }
45 | let(:clean_content) { "Dad is the best!" }
46 | let(:minor) { FactoryGirl.create :user, minor: true }
47 | let(:adult) { FactoryGirl.create :user, minor: false }
48 | context "user is a minor" do
49 | context "with profanity" do
50 | before { @micropost = Micropost.new(content: profane_content, user: minor) }
51 | it "returns false" do
52 | expect(@micropost).not_to be_valid
53 | end
54 | it "has validation error for content" do
55 | @micropost.save
56 | content_errors = @micropost.errors[:content][0]
57 | expect(content_errors).to match /poopface/
58 | expect(content_errors).to match /fartface/
59 | end
60 | end
61 | context "without profanity" do
62 | it "is valid" do
63 | micropost = Micropost.new(content: clean_content, user: minor)
64 | expect(micropost).to be_valid
65 | end
66 | end
67 | end
68 | context "user is an adult" do
69 | context "with profanity" do
70 | it "is valid" do
71 | micropost = Micropost.new(content: profane_content, user: adult)
72 | expect(micropost).to be_valid
73 | end
74 | end
75 | context "without profanity" do
76 | it "is valid" do
77 | micropost = Micropost.new(content: clean_content, user: adult)
78 | expect(micropost).to be_valid
79 | end
80 | end
81 | end
82 | end
83 |
84 | describe "Micropost#save_with_profanity_callbacks" do
85 | let(:profanity_content) { "Dad is a poopface and fartface!" }
86 | let(:clean_content) { "Dad is the best!" }
87 | let(:minor) { FactoryGirl.create :user, minor: true, profanity_count: 0 }
88 | let(:adult) { FactoryGirl.create :user, minor: false }
89 |
90 | context "user is a minor" do
91 | context "with profanity" do
92 | before { @micropost = Micropost.new(content: profanity_content, user: minor) }
93 | it "returns false" do
94 | expect(@micropost.save_with_profanity_callbacks).to be_false
95 | end
96 |
97 | it "has profanity validation errors" do
98 | @micropost.save_with_profanity_callbacks
99 | expect(@micropost.profanity_validation_error?).to be_true
100 | expect(@micropost).not_to be_persisted
101 | end
102 |
103 | it "has profanity updated the user's profanity count" do
104 | @micropost.save_with_profanity_callbacks
105 | expect(@micropost.user.profanity_count).to eq(2)
106 | expect(@micropost.user).to be_persisted
107 | end
108 | end
109 |
110 | context "without profanity" do
111 | before { @micropost = Micropost.new(content: clean_content, user: minor) }
112 |
113 | it "returns true" do
114 | expect(@micropost.save_with_profanity_callbacks).to be_true
115 | end
116 |
117 | it "returns saves the @micropost" do
118 | @micropost.save_with_profanity_callbacks
119 | expect(@micropost).to be_persisted
120 | expect(@micropost.user.profanity_count).to eq(0)
121 | end
122 | end
123 | end
124 |
125 | context "user is an adult" do
126 | context "with profanity" do
127 | before { @micropost = Micropost.new(content: profanity_content, user: adult) }
128 |
129 | it "returns true" do
130 | expect(@micropost.save_with_profanity_callbacks).to be_true
131 | end
132 |
133 | it "returns saves the @micropost" do
134 | @micropost.save_with_profanity_callbacks
135 | expect(@micropost).to be_persisted
136 | end
137 | end
138 | end
139 | end
140 |
141 |
--------------------------------------------------------------------------------
/spec/models/relationship_spec.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: relationships
4 | #
5 | # id :integer not null, primary key
6 | # follower_id :integer
7 | # followed_id :integer
8 | # created_at :datetime
9 | # updated_at :datetime
10 | #
11 |
12 | require 'spec_helper'
13 |
14 | describe Relationship do
15 |
16 | let(:follower) { FactoryGirl.create(:user) }
17 | let(:followed) { FactoryGirl.create(:user) }
18 | let(:relationship) { follower.relationships.build(followed_id: followed.id) }
19 |
20 | subject { relationship }
21 |
22 | it { should be_valid }
23 |
24 | describe "follower methods" do
25 | it { should respond_to(:follower) }
26 | it { should respond_to(:followed) }
27 | its(:follower) { should eq follower }
28 | its(:followed) { should eq followed }
29 | end
30 |
31 | describe "when followed id is not present" do
32 | before { relationship.followed_id = nil }
33 | it { should_not be_valid }
34 | end
35 |
36 | describe "when follower id is not present" do
37 | before { relationship.follower_id = nil }
38 | it { should_not be_valid }
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/spec/models/user_spec.rb:
--------------------------------------------------------------------------------
1 | # == Schema Information
2 | #
3 | # Table name: users
4 | #
5 | # id :integer not null, primary key
6 | # name :string(255)
7 | # email :string(255)
8 | # created_at :datetime
9 | # updated_at :datetime
10 | # password_digest :string(255)
11 | # remember_token :string(255)
12 | # admin :boolean
13 | # profanity_count :integer default(0)
14 | # minor :boolean default(FALSE)
15 | #
16 |
17 | require 'spec_helper'
18 |
19 | describe User do
20 |
21 | before do
22 | @user = User.new(name: "Example User", email: "user@example.com",
23 | password: "foobar", password_confirmation: "foobar")
24 | end
25 |
26 | subject { @user }
27 |
28 | it { should respond_to(:name) }
29 | it { should respond_to(:email) }
30 | it { should respond_to(:password_digest) }
31 | it { should respond_to(:password) }
32 | it { should respond_to(:password_confirmation) }
33 | it { should respond_to(:remember_token) }
34 | it { should respond_to(:authenticate) }
35 | it { should respond_to(:admin) }
36 | it { should respond_to(:microposts) }
37 | it { should respond_to(:feed) }
38 | it { should respond_to(:relationships) }
39 | it { should respond_to(:followed_users) }
40 | it { should respond_to(:reverse_relationships) }
41 | it { should respond_to(:followers) }
42 | it { should respond_to(:following?) }
43 | it { should respond_to(:follow!) }
44 | it { should respond_to(:unfollow!) }
45 |
46 | it { should be_valid }
47 | it { should_not be_admin }
48 |
49 | describe "with admin attribute set to 'true'" do
50 | before do
51 | @user.save!
52 | @user.toggle!(:admin)
53 | end
54 |
55 | it { should be_admin }
56 | end
57 |
58 | describe "when name is not present" do
59 | before { @user.name = " " }
60 | it { should_not be_valid }
61 | end
62 |
63 | describe "when name is too long" do
64 | before { @user.name = "a" * 51 }
65 | it { should_not be_valid }
66 | end
67 |
68 | describe "when password is not present" do
69 | before do
70 | @user = User.new(name: "Example User", email: "user@example.com",
71 | password: " ", password_confirmation: " ")
72 | end
73 | it { should_not be_valid }
74 | end
75 |
76 | describe "when password doesn't match confirmation" do
77 | before { @user.password_confirmation = "mismatch" }
78 | it { should_not be_valid }
79 | end
80 |
81 | describe "with a password that's too short" do
82 | before { @user.password = @user.password_confirmation = "a" * 5 }
83 | it { should be_invalid }
84 | end
85 |
86 | describe "return value of authenticate method" do
87 | before { @user.save }
88 | let(:found_user) { User.find_by(email: @user.email) }
89 |
90 | describe "with valid password" do
91 | it { should eq found_user.authenticate(@user.password) }
92 | end
93 |
94 | describe "with invalid password" do
95 | let(:user_for_invalid_password) { found_user.authenticate("invalid") }
96 |
97 | it { should_not eq user_for_invalid_password }
98 | specify { expect(user_for_invalid_password).to be_false }
99 | end
100 | end
101 |
102 | describe "remember token" do
103 | before { @user.save }
104 | its(:remember_token) { should_not be_blank }
105 | end
106 |
107 | describe "micropost associations" do
108 |
109 | before { @user.save }
110 | let!(:older_micropost) do
111 | FactoryGirl.create(:micropost, user: @user, created_at: 1.day.ago)
112 | end
113 | let!(:newer_micropost) do
114 | FactoryGirl.create(:micropost, user: @user, created_at: 1.hour.ago)
115 | end
116 |
117 | it "should have the right microposts in the right order" do
118 | expect(@user.microposts.to_a).to eq [newer_micropost, older_micropost]
119 | end
120 |
121 | it "should destroy associated microposts" do
122 | microposts = @user.microposts.to_a
123 | @user.destroy
124 | expect(microposts).not_to be_empty
125 | microposts.each do |micropost|
126 | expect(Micropost.where(id: micropost.id)).to be_empty
127 | end
128 | end
129 |
130 | describe "status" do
131 | let(:unfollowed_post) do
132 | FactoryGirl.create(:micropost, user: FactoryGirl.create(:user))
133 | end
134 | let(:followed_user) { FactoryGirl.create(:user) }
135 |
136 | before do
137 | @user.follow!(followed_user)
138 | 3.times { followed_user.microposts.create!(content: "Lorem ipsum") }
139 | end
140 |
141 | its(:feed) { should include(newer_micropost) }
142 | its(:feed) { should include(older_micropost) }
143 | its(:feed) { should_not include(unfollowed_post) }
144 | its(:feed) do
145 | followed_user.microposts.each do |micropost|
146 | should include(micropost)
147 | end
148 | end
149 | end
150 | end
151 |
152 | describe "following" do
153 | let(:other_user) { FactoryGirl.create(:user) }
154 | before do
155 | @user.save
156 | @user.follow!(other_user)
157 | end
158 |
159 | it { should be_following(other_user) }
160 | its(:followed_users) { should include(other_user) }
161 |
162 | describe "followed user" do
163 | subject { other_user }
164 | its(:followers) { should include(@user) }
165 | end
166 |
167 | describe "and unfollowing" do
168 | before { @user.unfollow!(other_user) }
169 |
170 | it { should_not be_following(other_user) }
171 | its(:followed_users) { should_not include(other_user) }
172 | end
173 | end
174 | end
175 |
176 | describe "User#update_for_using_profanity" do
177 | let(:profanity_words) { ['poop', 'poopface'] }
178 | context "minor" do
179 | before { @minor = FactoryGirl.create(:user, minor: true) }
180 | it "increments the profanity counter" do
181 | expect do
182 | @minor.update_for_using_profanity(profanity_words)
183 | end.to change(@minor, :profanity_count).by(profanity_words.size)
184 | end
185 | it "sends parent notification of profanity" do
186 | allow(@minor).to receive(:send_parent_notification_of_profanity).and_return(nil)
187 | @minor.update_for_using_profanity(profanity_words)
188 | expect(@minor).to have_received(:send_parent_notification_of_profanity)
189 | end
190 | end
191 | end
192 |
--------------------------------------------------------------------------------
/spec/requests/authentication_pages_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "Authentication" do
4 |
5 | subject { page }
6 |
7 | describe "signin page" do
8 | before { visit signin_path }
9 |
10 | it { should have_content('Sign in') }
11 | it { should have_title('Sign in') }
12 | end
13 |
14 | describe "signin" do
15 | before { visit signin_path }
16 |
17 | describe "with invalid information" do
18 | before { click_button "Sign in" }
19 |
20 | it { should have_title('Sign in') }
21 | it { should have_error_message('Invalid') }
22 |
23 | describe "after visiting another page" do
24 | before { click_link "Home" }
25 | it { should_not have_selector('div.alert.alert-error') }
26 | end
27 | end
28 |
29 | describe "with valid information" do
30 | let(:user) { FactoryGirl.create(:user) }
31 | before do
32 | fill_in "Email", with: user.email.upcase
33 | fill_in "Password", with: user.password
34 | click_button "Sign in"
35 | end
36 |
37 | it { should have_title(user.name) }
38 | it { should have_link('Users', href: users_path) }
39 | it { should have_link('Profile', href: user_path(user)) }
40 | it { should have_link('Settings', href: edit_user_path(user)) }
41 | it { should have_link('Sign out', href: signout_path) }
42 | it { should_not have_link('Sign in', href: signin_path) }
43 |
44 | describe "followed by signout" do
45 | before { click_link "Sign out" }
46 | it { should have_link('Sign in') }
47 | end
48 | end
49 | end
50 |
51 | describe "authorization" do
52 |
53 | describe "for non-signed-in users" do
54 | let(:user) { FactoryGirl.create(:user) }
55 |
56 | describe "when attempting to visit a protected page" do
57 | before do
58 | visit edit_user_path(user)
59 | fill_in "Email", with: user.email
60 | fill_in "Password", with: user.password
61 | click_button "Sign in"
62 | end
63 |
64 | describe "after signing in" do
65 |
66 | it "should render the desired protected page" do
67 | expect(page).to have_title('Edit user')
68 | end
69 | end
70 | end
71 |
72 | describe "in the Users controller" do
73 |
74 | describe "visiting the edit page" do
75 | before { visit edit_user_path(user) }
76 | it { should have_title('Sign in') }
77 | end
78 |
79 | describe "submitting to the update action" do
80 | before { patch user_path(user) }
81 | specify { expect(response).to redirect_to(signin_path) }
82 | end
83 |
84 | describe "visiting the user index" do
85 | before { visit users_path }
86 | it { should have_title('Sign in') }
87 | end
88 |
89 | describe "visiting the following page" do
90 | before { visit following_user_path(user) }
91 | it { should have_title('Sign in') }
92 | end
93 |
94 | describe "visiting the followers page" do
95 | before { visit followers_user_path(user) }
96 | it { should have_title('Sign in') }
97 | end
98 | end
99 |
100 | describe "in the Microposts controller" do
101 |
102 | describe "submitting to the create action" do
103 | before { post microposts_path }
104 | specify { expect(response).to redirect_to(signin_path) }
105 | end
106 |
107 | describe "submitting to the destroy action" do
108 | before { delete micropost_path(FactoryGirl.create(:micropost)) }
109 | specify { expect(response).to redirect_to(signin_path) }
110 | end
111 | end
112 |
113 | describe "in the Relationships controller" do
114 | describe "submitting to the create action" do
115 | before { post relationships_path }
116 | specify { expect(response).to redirect_to(signin_path) }
117 | end
118 |
119 | describe "submitting to the destroy action" do
120 | before { delete relationship_path(1) }
121 | specify { expect(response).to redirect_to(signin_path) }
122 | end
123 | end
124 | end
125 |
126 | describe "as wrong user" do
127 | let(:user) { FactoryGirl.create(:user) }
128 | let(:wrong_user) { FactoryGirl.create(:user, email: "wrong@example.com") }
129 | before { sign_in user, no_capybara: true }
130 |
131 | describe "submitting a GET request to the Users#edit action" do
132 | before { get edit_user_path(wrong_user) }
133 | specify { expect(response.body).not_to match(full_title('Edit user')) }
134 | specify { expect(response).to redirect_to(root_url) }
135 | end
136 |
137 | describe "submitting a PATCH request to the Users#update action" do
138 | before { patch user_path(wrong_user) }
139 | specify { expect(response).to redirect_to(root_url) }
140 | end
141 | end
142 |
143 | describe "as non-admin user" do
144 | let(:user) { FactoryGirl.create(:user) }
145 | let(:non_admin) { FactoryGirl.create(:user) }
146 |
147 | before { sign_in non_admin, no_capybara: true }
148 |
149 | describe "submitting a DELETE request to the Users#destroy action" do
150 | before { delete user_path(user) }
151 | specify { expect(response).to redirect_to(root_url) }
152 | end
153 | end
154 | end
155 | end
--------------------------------------------------------------------------------
/spec/requests/micropost_pages_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "Micropost pages" do
4 |
5 | subject { page }
6 |
7 | let(:user) { FactoryGirl.create(:user) }
8 | before { sign_in user }
9 |
10 | describe "micropost creation" do
11 | before { visit root_path }
12 |
13 | describe "with invalid information" do
14 |
15 | it "should not create a micropost" do
16 | expect { click_button "Post" }.not_to change(Micropost, :count)
17 | end
18 |
19 | describe "error messages" do
20 | before { click_button "Post" }
21 | it { should have_content('error') }
22 | end
23 | end
24 |
25 | describe "with valid information" do
26 |
27 | before { fill_in 'micropost_content', with: "Lorem ipsum" }
28 | it "should create a micropost" do
29 | expect { click_button "Post" }.to change(Micropost, :count).by(1)
30 | end
31 | end
32 | end
33 |
34 | describe "micropost destruction" do
35 | before { FactoryGirl.create(:micropost, user: user) }
36 |
37 | describe "as correct user" do
38 | before { visit root_path }
39 |
40 | it "should delete a micropost" do
41 | expect { click_link "delete" }.to change(Micropost, :count).by(-1)
42 | end
43 | end
44 | end
45 | end
46 |
--------------------------------------------------------------------------------
/spec/requests/static_pages_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "Static pages" do
4 |
5 | subject { page }
6 |
7 | describe "Home page" do
8 | before { visit root_path }
9 |
10 | it { should have_content('Kid Safe Microblogging App') }
11 | it { should have_title(full_title('')) }
12 | it { should_not have_title('| Home') }
13 |
14 | describe "for signed-in users" do
15 | let(:user) { FactoryGirl.create(:user) }
16 | before do
17 | FactoryGirl.create(:micropost, user: user, content: "Lorem")
18 | FactoryGirl.create(:micropost, user: user, content: "Ipsum")
19 | sign_in user
20 | visit root_path
21 | end
22 |
23 | it "should render the user's feed" do
24 | user.feed.each do |item|
25 | expect(page).to have_selector("li##{item.id}", text: item.content)
26 | end
27 | end
28 |
29 | describe "follower/following counts" do
30 | let(:other_user) { FactoryGirl.create(:user) }
31 | before do
32 | other_user.follow!(user)
33 | visit root_path
34 | end
35 |
36 | it { should have_link("0 following", href: following_user_path(user)) }
37 | it { should have_link("1 followers", href: followers_user_path(user)) }
38 | end
39 | end
40 | end
41 |
42 | describe "Help page" do
43 | before { visit help_path }
44 |
45 | it { should have_content('Help') }
46 | it { should have_title(full_title('Help')) }
47 | end
48 |
49 | describe "About page" do
50 | before { visit about_path }
51 |
52 | it { should have_content('About') }
53 | it { should have_title(full_title('About Us')) }
54 | end
55 |
56 | describe "Contact page" do
57 | before { visit contact_path }
58 |
59 | it { should have_selector('h1', text: 'Contact') }
60 | it { should have_title(full_title('Contact')) }
61 | end
62 |
63 |
64 | end
65 |
--------------------------------------------------------------------------------
/spec/requests/user_pages_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "User pages" do
4 |
5 | subject { page }
6 |
7 | describe "index" do
8 | let(:user) { FactoryGirl.create(:user) }
9 | before(:each) do
10 | sign_in user
11 | visit users_path
12 | end
13 |
14 | it { should have_title('All users') }
15 | it { should have_content('All users') }
16 |
17 | describe "pagination" do
18 |
19 | before(:all) { 30.times { FactoryGirl.create(:user) } }
20 | after(:all) { User.delete_all }
21 |
22 | it { should have_selector('div.pagination') }
23 |
24 | it "should list each user" do
25 | User.paginate(page: 1).each do |user|
26 | expect(page).to have_selector('li', text: user.name)
27 | end
28 | end
29 | end
30 |
31 | describe "delete links" do
32 |
33 | it { should_not have_link('delete') }
34 |
35 | describe "as an admin user" do
36 | let(:admin) { FactoryGirl.create(:admin) }
37 | before do
38 | sign_in admin
39 | visit users_path
40 | end
41 |
42 | it { should have_link('delete', href: user_path(User.first)) }
43 | it "should be able to delete another user" do
44 | expect do
45 | click_link('delete', match: :first)
46 | end.to change(User, :count).by(-1)
47 | end
48 | it { should_not have_link('delete', href: user_path(admin)) }
49 | end
50 | end
51 | end
52 |
53 | describe "profile page" do
54 | let(:user) { FactoryGirl.create(:user) }
55 | let!(:m1) { FactoryGirl.create(:micropost, user: user, content: "Foo") }
56 | let!(:m2) { FactoryGirl.create(:micropost, user: user, content: "Bar") }
57 |
58 | before { visit user_path(user) }
59 |
60 | it { should have_content(user.name) }
61 | it { should have_title(user.name) }
62 |
63 | describe "microposts" do
64 | it { should have_content(m1.content) }
65 | it { should have_content(m2.content) }
66 | it { should have_content(user.microposts.count) }
67 | end
68 |
69 | describe "follow/unfollow buttons" do
70 | let(:other_user) { FactoryGirl.create(:user) }
71 | before { sign_in user }
72 |
73 | describe "following a user" do
74 | before { visit user_path(other_user) }
75 |
76 | it "should increment the followed user count" do
77 | expect do
78 | click_button "Follow"
79 | end.to change(user.followed_users, :count).by(1)
80 | end
81 |
82 | it "should increment the other user's followers count" do
83 | expect do
84 | click_button "Follow"
85 | end.to change(other_user.followers, :count).by(1)
86 | end
87 |
88 | describe "toggling the button" do
89 | before { click_button "Follow" }
90 | it { should have_xpath("//input[@value='Unfollow']") }
91 | end
92 | end
93 |
94 | describe "unfollowing a user" do
95 | before do
96 | user.follow!(other_user)
97 | visit user_path(other_user)
98 | end
99 |
100 | it "should decrement the followed user count" do
101 | expect do
102 | click_button "Unfollow"
103 | end.to change(user.followed_users, :count).by(-1)
104 | end
105 |
106 | it "should decrement the other user's followers count" do
107 | expect do
108 | click_button "Unfollow"
109 | end.to change(other_user.followers, :count).by(-1)
110 | end
111 |
112 | describe "toggling the button" do
113 | before { click_button "Unfollow" }
114 | it { should have_xpath("//input[@value='Follow']") }
115 | end
116 | end
117 | end
118 | end
119 |
120 | describe "signup page" do
121 | before { visit signup_path }
122 |
123 | it { should have_content('Sign up') }
124 | it { should have_title(full_title('Sign up')) }
125 | end
126 |
127 | describe "signup" do
128 |
129 | before { visit signup_path }
130 |
131 | let(:submit) { "Create my account" }
132 |
133 | describe "with invalid information" do
134 | it "should not create a user" do
135 | expect { click_button submit }.not_to change(User, :count)
136 | end
137 | end
138 |
139 | describe "with valid information" do
140 | before do
141 | fill_in "Name", with: "Example User"
142 | fill_in "Email", with: "user@example.com"
143 | fill_in "Password", with: "foobar"
144 | fill_in "Confirmation", with: "foobar"
145 | end
146 |
147 | it "should create a user" do
148 | expect { click_button submit }.to change(User, :count).by(1)
149 | end
150 |
151 | describe "after saving the user" do
152 | before { click_button submit }
153 | let(:user) { User.find_by(email: 'user@example.com') }
154 |
155 | it { should have_link('Sign out') }
156 | it { should have_title(user.name) }
157 | it { should have_selector('div.alert.alert-success', text: 'Welcome') }
158 | end
159 | end
160 | end
161 |
162 | describe "edit" do
163 | let(:user) { FactoryGirl.create(:user) }
164 | before do
165 | sign_in user
166 | visit edit_user_path(user)
167 | end
168 |
169 | describe "page" do
170 | it { should have_content("Update your profile") }
171 | it { should have_title("Edit user") }
172 | it { should have_link('change', href: 'http://gravatar.com/emails') }
173 | end
174 |
175 | describe "with invalid information" do
176 | before { click_button "Save changes" }
177 |
178 | it { should have_content('error') }
179 | end
180 |
181 | describe "with valid information" do
182 | let(:new_name) { "New Name" }
183 | let(:new_email) { "new@example.com" }
184 | before do
185 | fill_in "Name", with: new_name
186 | fill_in "Email", with: new_email
187 | fill_in "Password", with: user.password
188 | fill_in "Confirm Password", with: user.password
189 | click_button "Save changes"
190 | end
191 |
192 | it { should have_title(new_name) }
193 | it { should have_selector('div.alert.alert-success') }
194 | it { should have_link('Sign out', href: signout_path) }
195 | specify { expect(user.reload.name).to eq new_name }
196 | specify { expect(user.reload.email).to eq new_email }
197 | end
198 |
199 | describe "forbidden attributes" do
200 | let(:params) do
201 | { user: { admin: true, password: user.password,
202 | password_confirmation: user.password } }
203 | end
204 | before do
205 | sign_in user, no_capybara: true
206 | patch user_path(user), params
207 | end
208 | specify { expect(user.reload).not_to be_admin }
209 | end
210 | end
211 |
212 | describe "following/followers" do
213 | let(:user) { FactoryGirl.create(:user) }
214 | let(:other_user) { FactoryGirl.create(:user) }
215 | before { user.follow!(other_user) }
216 |
217 | describe "followed users" do
218 | before do
219 | sign_in user
220 | visit following_user_path(user)
221 | end
222 |
223 | it { should have_title(full_title('Following')) }
224 | it { should have_selector('h3', text: 'Following') }
225 | it { should have_link(other_user.name, href: user_path(other_user)) }
226 | end
227 |
228 | describe "followers" do
229 | before do
230 | sign_in other_user
231 | visit followers_user_path(other_user)
232 | end
233 |
234 | it { should have_title(full_title('Followers')) }
235 | it { should have_selector('h3', text: 'Followers') }
236 | it { should have_link(user.name, href: user_path(user)) }
237 | end
238 | end
239 | end
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'spork'
3 |
4 | Spork.prefork do
5 | ENV["RAILS_ENV"] ||= 'test'
6 | require File.expand_path("../../config/environment", __FILE__)
7 | require 'rspec/rails'
8 | require 'rspec/autorun'
9 | require 'draper/test/rspec_integration'
10 |
11 | # Requires supporting ruby files with custom matchers and macros, etc,
12 | # in spec/support/ and its subdirectories.
13 | Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
14 |
15 | # Checks for pending migrations before tests are run.
16 | # If you are not using ActiveRecord, you can remove this line.
17 | ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)
18 |
19 | RSpec.configure do |config|
20 | # ## Mock Framework
21 | #
22 | # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
23 | #
24 | # config.mock_with :mocha
25 | # config.mock_with :flexmock
26 | # config.mock_with :rr
27 |
28 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
29 | config.fixture_path = "#{::Rails.root}/spec/fixtures"
30 |
31 | # If you're not using ActiveRecord, or you'd prefer not to run each of your
32 | # examples within a transaction, remove the following line or assign false
33 | # instead of true.
34 | config.use_transactional_fixtures = true
35 |
36 | # If true, the base class of anonymous controllers will be inferred
37 | # automatically. This will be the default behavior in future versions of
38 | # rspec-rails.
39 | config.infer_base_class_for_anonymous_controllers = false
40 |
41 | # Run specs in random order to surface order dependencies. If you find an
42 | # order dependency and want to debug it, you can fix the order by providing
43 | # the seed, which is printed after each run.
44 | # --seed 1234
45 | config.order = "random"
46 | # Include the Capybara DSL so that specs in spec/requests still work.
47 | config.include Capybara::DSL
48 | # Disable the old-style object.should syntax.
49 | config.expect_with :rspec do |c|
50 | c.syntax = :expect
51 | end
52 | end
53 | end
54 |
55 | Spork.each_run do
56 | # This code will be run each time you run your specs.
57 |
58 | end
59 |
--------------------------------------------------------------------------------
/spec/support/utilities.rb:
--------------------------------------------------------------------------------
1 | include ApplicationHelper
2 |
3 | def sign_in(user, options={})
4 | if options[:no_capybara]
5 | # Sign in when not using Capybara as well.
6 | remember_token = User.new_remember_token
7 | cookies[:remember_token] = remember_token
8 | user.update_attribute(:remember_token, User.hash(remember_token))
9 | else
10 | visit signin_path
11 | fill_in "Email", with: user.email
12 | fill_in "Password", with: user.password
13 | click_button "Sign in"
14 | end
15 | end
16 |
17 | RSpec::Matchers.define :have_error_message do |message|
18 | match do |page|
19 | expect(page).to have_selector('div.alert.alert-error', text: message)
20 | end
21 | end
--------------------------------------------------------------------------------
/vendor/assets/javascripts/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakacode/fat-code-refactoring-techniques/a578449db549b4d367d354d2f94f1fd5c5bb5f8d/vendor/assets/javascripts/.keep
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shakacode/fat-code-refactoring-techniques/a578449db549b4d367d354d2f94f1fd5c5bb5f8d/vendor/assets/stylesheets/.keep
--------------------------------------------------------------------------------