├── .gitignore ├── Gemfile ├── Gemfile.lock ├── Procfile ├── README.md ├── README.rdoc ├── Rakefile ├── app ├── assets │ └── images │ │ └── .keep ├── controllers │ ├── application_controller.rb │ ├── articles_controller.rb │ ├── concerns │ │ └── .keep │ └── v1 │ │ └── articles_controller.rb ├── mailers │ └── .keep └── models │ ├── .keep │ ├── article.rb │ └── concerns │ └── .keep ├── bin ├── bundle ├── rails ├── rake ├── setup └── spring ├── client ├── .bowerrc ├── .editorconfig ├── .gitignore ├── .jshintrc ├── .yo-rc.json ├── bower.json ├── e2e │ ├── .jshintrc │ ├── main.po.js │ └── main.spec.js ├── gulp │ ├── .jshintrc │ ├── build.js │ ├── conf.js │ ├── e2e-tests.js │ ├── inject.js │ ├── scripts.js │ ├── server.js │ ├── unit-tests.js │ └── watch.js ├── gulpfile.js ├── karma.conf.js ├── protractor.conf.js └── src │ ├── app │ ├── components │ │ ├── articles │ │ │ ├── articles.controller.js │ │ │ ├── articles.factory.js │ │ │ └── articles.html │ │ ├── githubContributor │ │ │ └── githubContributor.service.js │ │ ├── malarkey │ │ │ ├── malarkey.css │ │ │ └── malarkey.directive.js │ │ ├── navbar │ │ │ ├── navbar.css │ │ │ ├── navbar.directive.js │ │ │ └── navbar.html │ │ └── webDevTec │ │ │ └── webDevTec.service.js │ ├── index.config.js │ ├── index.constants.js │ ├── index.css │ ├── index.module.js │ ├── index.route.js │ ├── index.run.js │ └── main │ │ ├── main.controller.js │ │ ├── main.controller.spec.js │ │ └── main.html │ ├── assets │ └── images │ │ ├── angular.png │ │ ├── bootstrap.png │ │ ├── browsersync.png │ │ ├── gulp.png │ │ ├── jasmine.png │ │ ├── karma.png │ │ ├── protractor.png │ │ ├── ui-bootstrap.png │ │ └── yeoman.png │ ├── favicon.ico │ └── index.html ├── config.ru ├── config ├── application.rb ├── boot.rb ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── filter_parameter_logging.rb │ ├── secret_token.rb │ └── wrap_parameters.rb ├── locales │ └── en.yml ├── routes.rb └── secrets.yml ├── db ├── migrate │ └── 20150718132359_create_articles.rb ├── schema.rb └── seeds.rb ├── lib ├── assets │ └── .keep └── tasks │ └── .keep ├── log └── .keep ├── package.json └── test ├── controllers ├── .keep └── articles_controller_test.rb ├── fixtures ├── .keep └── articles.yml ├── helpers └── .keep ├── integration └── .keep ├── mailers └── .keep ├── models ├── .keep └── article_test.rb └── test_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore all logfiles and tempfiles. 11 | /log/* 12 | !/log/.keep 13 | /tmp 14 | 15 | node_modules 16 | public 17 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | 4 | gem 'rails', '4.2.3' 5 | 6 | gem 'rails-api' 7 | 8 | gem 'spring', :group => :development 9 | 10 | 11 | gem 'pg' 12 | 13 | 14 | 15 | # To use ActiveModel has_secure_password 16 | # gem 'bcrypt', '~> 3.1.7' 17 | 18 | # To use Jbuilder templates for JSON 19 | # gem 'jbuilder' 20 | 21 | # Use unicorn as the app server 22 | # gem 'unicorn' 23 | 24 | # Deploy with Capistrano 25 | # gem 'capistrano', :group => :development 26 | 27 | # To use debugger 28 | # gem 'ruby-debug19', :require => 'ruby-debug' 29 | gem 'rails_12factor', group: :production 30 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actionmailer (4.2.3) 5 | actionpack (= 4.2.3) 6 | actionview (= 4.2.3) 7 | activejob (= 4.2.3) 8 | mail (~> 2.5, >= 2.5.4) 9 | rails-dom-testing (~> 1.0, >= 1.0.5) 10 | actionpack (4.2.3) 11 | actionview (= 4.2.3) 12 | activesupport (= 4.2.3) 13 | rack (~> 1.6) 14 | rack-test (~> 0.6.2) 15 | rails-dom-testing (~> 1.0, >= 1.0.5) 16 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 17 | actionview (4.2.3) 18 | activesupport (= 4.2.3) 19 | builder (~> 3.1) 20 | erubis (~> 2.7.0) 21 | rails-dom-testing (~> 1.0, >= 1.0.5) 22 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 23 | activejob (4.2.3) 24 | activesupport (= 4.2.3) 25 | globalid (>= 0.3.0) 26 | activemodel (4.2.3) 27 | activesupport (= 4.2.3) 28 | builder (~> 3.1) 29 | activerecord (4.2.3) 30 | activemodel (= 4.2.3) 31 | activesupport (= 4.2.3) 32 | arel (~> 6.0) 33 | activesupport (4.2.3) 34 | i18n (~> 0.7) 35 | json (~> 1.7, >= 1.7.7) 36 | minitest (~> 5.1) 37 | thread_safe (~> 0.3, >= 0.3.4) 38 | tzinfo (~> 1.1) 39 | arel (6.0.2) 40 | builder (3.2.2) 41 | erubis (2.7.0) 42 | globalid (0.3.5) 43 | activesupport (>= 4.1.0) 44 | i18n (0.7.0) 45 | json (1.8.3) 46 | loofah (2.0.2) 47 | nokogiri (>= 1.5.9) 48 | mail (2.6.3) 49 | mime-types (>= 1.16, < 3) 50 | mime-types (2.6.1) 51 | mini_portile (0.6.2) 52 | minitest (5.7.0) 53 | nokogiri (1.6.6.2) 54 | mini_portile (~> 0.6.0) 55 | pg (0.18.2) 56 | rack (1.6.4) 57 | rack-test (0.6.3) 58 | rack (>= 1.0) 59 | rails (4.2.3) 60 | actionmailer (= 4.2.3) 61 | actionpack (= 4.2.3) 62 | actionview (= 4.2.3) 63 | activejob (= 4.2.3) 64 | activemodel (= 4.2.3) 65 | activerecord (= 4.2.3) 66 | activesupport (= 4.2.3) 67 | bundler (>= 1.3.0, < 2.0) 68 | railties (= 4.2.3) 69 | sprockets-rails 70 | rails-api (0.4.0) 71 | actionpack (>= 3.2.11) 72 | railties (>= 3.2.11) 73 | rails-deprecated_sanitizer (1.0.3) 74 | activesupport (>= 4.2.0.alpha) 75 | rails-dom-testing (1.0.6) 76 | activesupport (>= 4.2.0.beta, < 5.0) 77 | nokogiri (~> 1.6.0) 78 | rails-deprecated_sanitizer (>= 1.0.1) 79 | rails-html-sanitizer (1.0.2) 80 | loofah (~> 2.0) 81 | rails_12factor (0.0.3) 82 | rails_serve_static_assets 83 | rails_stdout_logging 84 | rails_serve_static_assets (0.0.4) 85 | rails_stdout_logging (0.0.3) 86 | railties (4.2.3) 87 | actionpack (= 4.2.3) 88 | activesupport (= 4.2.3) 89 | rake (>= 0.8.7) 90 | thor (>= 0.18.1, < 2.0) 91 | rake (10.4.2) 92 | spring (1.3.6) 93 | sprockets (3.2.0) 94 | rack (~> 1.0) 95 | sprockets-rails (2.3.2) 96 | actionpack (>= 3.0) 97 | activesupport (>= 3.0) 98 | sprockets (>= 2.8, < 4.0) 99 | thor (0.19.1) 100 | thread_safe (0.3.5) 101 | tzinfo (1.2.2) 102 | thread_safe (~> 0.1) 103 | 104 | PLATFORMS 105 | ruby 106 | 107 | DEPENDENCIES 108 | pg 109 | rails (= 4.2.3) 110 | rails-api 111 | rails_12factor 112 | spring 113 | 114 | BUNDLED WITH 115 | 1.10.5 116 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | bin/rails s 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The purpose of this guide is to teach how to create an Angular app that uses a Rails::API Backend. We'll build the client-side app using Gulp and deploy to Heroku with the source all contained in the same repository. This is a great stack that utilizes the robustness of Rails as a JSON API and the agility of Angular as a client-side application. 2 | 3 | I've grown very fond of using Rails as a single-purpose JSON API for it's great authentication (devise) and testing capability (RSpec, Capybara). Angular is great at consuming RESTful APIs using the official [ngResource](https://docs.angularjs.org/api/ngResource) module. 4 | 5 | ## Prerequisites 6 | 7 | * RVM / A Stable Ruby Version 8 | * Yeoman, Gulp, Bower `npm install -g yo gulp bower` 9 | 10 | ## Bootstrap the API 11 | 12 | We'll be using the [Rails::API gem](https://github.com/rails-api/rails-api) for this application as we'll be using Rails as a RESTFul, JSON API. Since we're using Angular as the client application we don't need much of the middleware or asset management Rails provides by default. Rails::API slims-down the backend and gives us exactly what we need. 13 | 14 | $ gem install rails-api 15 | 16 | Generate a new Rails::API app and run bundler. 17 | 18 | ``` 19 | $ rails-api new railsAngular --database=postgresql 20 | 21 | $ cd railsAngular 22 | 23 | $ bundle install 24 | ``` 25 | 26 | #### Scaffold a resource and test API 27 | 28 | Scaffold a resource that we'll wire up to Angular and consume. This can be anything you want but I recommend including at least a few fields to test with. 29 | 30 | ``` 31 | $ bin/rails g scaffold articles title:string body:text 32 | ``` 33 | 34 | #### API Scope and Namespace 35 | 36 | Modify routes to use a scope of /api and a namespace of V1. We'll also make it default to JSON. 37 | 38 | ``` 39 | scope '/api' do 40 | namespace :v1, defaults: { format: :json } do 41 | 42 | resources :articles, except: [:new, :edit] 43 | 44 | end 45 | end 46 | ``` 47 | 48 | Update the controller to use the new V1 namespace. 49 | 50 | ``` 51 | $ mkdir app/controllers/v1 52 | 53 | $ mv app/controllers/articles_controller.rb app/controllers/v1/articles_controller.rb 54 | ``` 55 | 56 | In app/controllers/v1/articles_controller.rb, update 57 | 58 | ``` 59 | class ArticlesController 60 | ``` 61 | 62 | to: 63 | 64 | ``` 65 | class V1::ArticlesController 66 | ``` 67 | 68 | #### Setup Database 69 | 70 | I recommend creating a few seeds by adding to db/seeds.rb. This will help us when we test the end point to request the resources and we'll eventually see these through our UI. 71 | 72 | ``` 73 | $ echo "Article.create(title: 'Test Article', body: 'A test article. Cool!')" >> db/seeds.rb 74 | 75 | $ rake db:create 76 | 77 | $ rake db:migrate 78 | 79 | $ rake db:seed 80 | ``` 81 | 82 | #### Test the Configuration 83 | 84 | Test the new resource by navigating to articles GET #index. 85 | 86 | ``` 87 | $ bin/rails s 88 | ``` 89 | 90 | http://localhost:3000/api/v1/articles 91 | 92 | You should see nice JSON output of the Article resources. 93 | 94 | ## Deploy to Heroku 95 | 96 | Refer to the Heroku documentation about getting started with a Rails application. We install the rails_12factor gem for production, initialize the git repo, and then create the Heroku app. After we deploy to Heroku, we need to setup the Rails database. 97 | 98 | https://devcenter.heroku.com/articles/getting-started-with-rails4 99 | 100 | ``` 101 | $ echo "gem 'rails_12factor', group: :production" >> Gemfile 102 | 103 | $ bundle install 104 | 105 | $ git init 106 | 107 | $ git add . 108 | 109 | $ git commit -m "init" 110 | 111 | $ heroku create 112 | 113 | $ git push heroku master 114 | 115 | $ heroku run rake db:migrate && rake db:seed 116 | 117 | $ heroku open 118 | ``` 119 | 120 | Open up your Heroku app in a browser and again test the resource you created. The output should be exactly as we see it when we deploy locally. 121 | 122 | https://fast-forest-6196.herokuapp.com/api/v1/articles 123 | 124 | ## Create Angular App 125 | 126 | We're going to put all of the source of our front-end Angular application in a client directory. We'll install the generator-gulp-angular, make the directory, and bootstrap the application. 127 | 128 | ``` 129 | $ npm install -g generator-gulp-angular 130 | 131 | $ mkdir client && cd $_ 132 | 133 | $ yo gulp-angular railsAngular 134 | ``` 135 | 136 | I selected most of the default values. You can customize the options as you see fit. 137 | 138 | * Angular 1.4.0 139 | * all default Angular modules 140 | * jQuery 2.x 141 | * ngResource 142 | * UI Router 143 | * Bootstrap 144 | * Angular UI Bootstrap 145 | * Base CSS 146 | * Standard Javascript 147 | * Standard HTML 148 | 149 | #### Setup local development environment 150 | 151 | For the local dev environment, I followed this tutorial: 152 | 153 | http://www.angularonrails.com/how-to-wire-up-ruby-on-rails-and-angularjs-as-a-single-page-application-gulp-version/ 154 | 155 | Next, we're going to edit client/gulp/server.js to do several things. 156 | 157 | 1. Configure [proxy middleware](https://www.npmjs.com/package/http-proxy-middleware#star) with a context of '/api' that targets the Rails API application. 158 | 2. Set browserSync to run the client application on port 9000 since the Rails server uses 3000 by default. 159 | 3. Add Gulp tasks to start rails and to start the entire full-stack application. 160 | 161 | ``` 162 | ... 163 | 164 | var proxyMiddleware = require('http-proxy-middleware'); 165 | + var exec = require('child_process').exec; 166 | 167 | ... 168 | 169 | var server = { 170 | baseDir: baseDir, 171 | routes: routes, 172 | + middleware: [ 173 | + proxyMiddleware('/api', { target: 'http://localhost:3000' }) 174 | + ] 175 | }; 176 | 177 | /* 178 | * You can add a proxy to your backend by uncommenting the line bellow. 179 | * You just have to configure a context which will we redirected and the target url. 180 | * Example: $http.get('/users') requests will be automatically proxified. 181 | * 182 | * For more details and option, https://github.com/chimurai/http-proxy-middleware/blob/v0.0.5/README.md 183 | */ 184 | // server.middleware = proxyMiddleware('/users', {target: 'http://jsonplaceholder.typicode.com', proxyHost: 'jsonplaceholder.typicode.com'}); 185 | 186 | browserSync.instance = browserSync.init({ 187 | + port: 9000, 188 | startPath: '/', 189 | server: server, 190 | browser: browser 191 | }); 192 | 193 | ... 194 | 195 | + gulp.task('rails', function() { 196 | + exec("../bin/rails s"); 197 | + }); 198 | 199 | + gulp.task('serve:full-stack', ['rails', 'serve']); 200 | ``` 201 | 202 | Now we can run our new serve:full-stack task from the client directory and test that our client app loads and that the API is accessible from the proxy. 203 | 204 | ``` 205 | $ gulp serve:full-stack 206 | ``` 207 | 208 | Let's test the 3 changes we made: 209 | 210 | 1. The front-end server should come up on port 9000 instead of 3000. 211 | 2. Test the proxy by navigating to http://localhost:9000/api/v1/articles. You should see a response from the API. 212 | 3. Rails is running on port 3000. 213 | 214 | ## Acessing the API from Client 215 | 216 | Edit client/app/src/index.route.js and add an 'articles' state: 217 | 218 | ``` 219 | ... 220 | .state('articles', { 221 | url: '/articles', 222 | templateUrl: 'app/components/articles/articles.html', 223 | controller: 'ArticlesController' 224 | }); 225 | ... 226 | ``` 227 | 228 | I like to create "Vertical Modules" where all of the source realted to a component is contained in the same directory. 229 | 230 | ``` 231 | $ mkdir client/src/app/components/articles 232 | ``` 233 | 234 | Create a factory that will consume our API resource. By default, Angular resource does not have a method for updating so we'll create that. See the [official documentation for Angular $resource](https://docs.angularjs.org/api/ngResource/service/$resource#creating-a-custom-put-request) for more information. 235 | 236 | ``` 237 | $ touch client/src/app/components/articles/articles.factory.js 238 | ``` 239 | 240 | ``` 241 | 'use strict'; 242 | 243 | angular.module('angularRails') 244 | .factory('Articles', function ($resource) { 245 | return $resource('api/v1/articles/:articleId', { 246 | articleId: '@id' 247 | }, { 248 | update: { 249 | method: 'PUT' 250 | } 251 | }); 252 | }); 253 | 254 | ``` 255 | 256 | Now we'll create a basic view that will iterate over all of the article resources and output their basic attributes. 257 | 258 | ``` 259 | touch src/app/components/articles/articles.html 260 | ``` 261 | 262 | ``` 263 |
264 | 265 |
266 | 267 |
268 | 269 |

Articles

270 | 271 |
272 |

{{ article.title }}

273 |

{{ article.body }}

274 |
275 | 276 |
277 | 278 | ``` 279 | 280 | We'll create a basic controller that uses the factory to query all of the Article resources. 281 | 282 | ``` 283 | $ touch src/app/components/articles/articles.controller.js 284 | ``` 285 | 286 | ``` 287 | angular.module('angularRails') 288 | .controller('ArticlesController', function ($scope, Articles) { 289 | 290 | Articles.query(function (res) { 291 | $scope.articles = res; 292 | }); 293 | 294 | }); 295 | ``` 296 | 297 | #### Test the UI 298 | 299 | Run gulp:full-stack again and navigate to your newly created state on the frontend (http://localhost:9000/#/articles). Your Angular application should be correctly consuming and display the Article resources. 300 | 301 | ![angular-on-rails-api](http://grantgeorge.io/content/images/2015/07/Screen-Shot-2015-07-19-at-9-44-20-AM.png) 302 | 303 | ## Deploy the entire stack to Heroku 304 | 305 | Let's deploy our awesome full-stack application! We want Gulp to build the production files in a directory called 'public'. Rails, by default, will load these files at the root route. 306 | 307 | Edit the gulpfile conf file at client/gulp/conf.js and change 308 | 309 | ``` 310 | exports.paths = { 311 | src: 'src', 312 | dist: 'dist', 313 | tmp: '.tmp', 314 | e2e: 'e2e' 315 | }; 316 | ``` 317 | 318 | to: 319 | 320 | ``` 321 | exports.paths = { 322 | src: 'src', 323 | dist: '../public', 324 | tmp: '.tmp', 325 | e2e: 'e2e' 326 | }; 327 | ``` 328 | 329 | Set the force option to true for the clean task in client/gulp/build.js. This is required since the build directory is a level up from the client directory and a warning is thrown when we clean the directory for building. 330 | 331 | ``` 332 | gulp.task('clean', function (done) { 333 | $.del([path.join(conf.paths.dist, '/'), path.join(conf.paths.tmp, '/')], { force: true }, done); 334 | }); 335 | ``` 336 | 337 | Now we can build the application and you should see the built application in public. 338 | 339 | ``` 340 | $ gulp build 341 | ``` 342 | 343 | #### Configure Multiple Heroku Buildpacks 344 | 345 | The following section refers to the [official Heroku documentation on using multiple buildpacks](https://devcenter.heroku.com/articles/using-multiple-buildpacks-for-an-app). 346 | 347 | Add the ruby and node heroku buildpacks: 348 | 349 | ``` 350 | $ heroku buildpacks:set https://github.com/heroku/heroku-buildpack-ruby 351 | $ heroku buildpacks:add --index 1 https://github.com/heroku/heroku-buildpack-nodejs 352 | ``` 353 | 354 | Verify that you have the correct buildpack configuration. Note that you must have both buildpacks set and in the correct order. This ensures that it will install and build our client app but use the Rails app as the webserver. 355 | 356 | ``` 357 | $ heroku buildpacks 358 | ➜ railsAngular git:(master) heroku buildpacks 359 | === angular-rails-gulp-tutorial Buildpack URLs 360 | 1. https://github.com/heroku/heroku-buildpack-nodejs 361 | 2. https://github.com/heroku/heroku-buildpack-ruby 362 | ``` 363 | 364 | Set the node environment to production. 365 | 366 | ``` 367 | $ heroku config:set NODE_ENV=production 368 | ``` 369 | 370 | Create a Procfile in root of project to run the WEBrick webserver 371 | 372 | ``` 373 | $ bin/rails s 374 | ``` 375 | 376 | Deploy! 377 | 378 | ``` 379 | $ git add -A 380 | 381 | $ git commit -m "testing deploy" 382 | 383 | $ git push heroku 384 | ``` 385 | 386 | Open your deployed Heroku application and verify everything works! 387 | 388 | ``` 389 | $ heroku open 390 | ``` 391 | 392 | Navigate to articles page. It should work exactly as you have it locally but with built client assets. 393 | 394 | https://angular-rails-gulp-tutorial.herokuapp.com/#/articles 395 | 396 | ![angular-on-rails-gulp-heroku-assets](http://grantgeorge.io/content/images/2015/07/Screen-Shot-2015-07-19-at-9-53-54-AM.png) 397 | 398 | ## Debugging 399 | 400 | If you're having issues with the deploy I recommend tailing the Heroku logs while you deploy. Additionally, I recommend installing papertrail. Papertrail allows you to search the logs and offers many features that can be useful as your application grows. 401 | 402 | ``` 403 | $ heroku logs --tail 404 | 405 | $ heroku addons:create papertrail 406 | 407 | $ heroku addons:open papertrail 408 | ``` 409 | 410 | ![heroku-papertrail-addon-in-action](http://grantgeorge.io/content/images/2015/07/Screen-Shot-2015-07-19-at-9-56-16-AM.png) 411 | 412 | ## Where to go from here? 413 | 414 | Start building your application using the lightweight Rails API framework and the ever-popular Angular! This is a great stack that utilizes the robustness of Rails as a JSON API and the agility of Angular as a client-side application. There are many things you can do to improve your application: setup a test infrastructure, configure Puma as a webserver, and configure a logging application (I recommend papertrail as it works well with Heroku) 415 | 416 | Have fun! 417 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | == README 2 | 3 | This README would normally document whatever steps are necessary to get the 4 | application up and running. 5 | 6 | Things you may want to cover: 7 | 8 | * Ruby version 9 | 10 | * System dependencies 11 | 12 | * Configuration 13 | 14 | * Database creation 15 | 16 | * Database initialization 17 | 18 | * How to run the test suite 19 | 20 | * Services (job queues, cache servers, search engines, etc.) 21 | 22 | * Deployment instructions 23 | 24 | * ... 25 | 26 | 27 | Please feel free to use a different markup language if you do not plan to run 28 | rake doc:app. 29 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/app/assets/images/.keep -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::API 2 | end 3 | -------------------------------------------------------------------------------- /app/controllers/articles_controller.rb: -------------------------------------------------------------------------------- 1 | class V1::ArticlesController < ApplicationController 2 | before_action :set_article, only: [:show, :update, :destroy] 3 | 4 | # GET /articles 5 | # GET /articles.json 6 | def index 7 | @articles = Article.all 8 | 9 | render json: @articles 10 | end 11 | 12 | # GET /articles/1 13 | # GET /articles/1.json 14 | def show 15 | render json: @article 16 | end 17 | 18 | # POST /articles 19 | # POST /articles.json 20 | def create 21 | @article = Article.new(article_params) 22 | 23 | if @article.save 24 | render json: @article, status: :created, location: @article 25 | else 26 | render json: @article.errors, status: :unprocessable_entity 27 | end 28 | end 29 | 30 | # PATCH/PUT /articles/1 31 | # PATCH/PUT /articles/1.json 32 | def update 33 | @article = Article.find(params[:id]) 34 | 35 | if @article.update(article_params) 36 | head :no_content 37 | else 38 | render json: @article.errors, status: :unprocessable_entity 39 | end 40 | end 41 | 42 | # DELETE /articles/1 43 | # DELETE /articles/1.json 44 | def destroy 45 | @article.destroy 46 | 47 | head :no_content 48 | end 49 | 50 | private 51 | 52 | def set_article 53 | @article = Article.find(params[:id]) 54 | end 55 | 56 | def article_params 57 | params.require(:article).permit(:title, :body) 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/v1/articles_controller.rb: -------------------------------------------------------------------------------- 1 | class V1::ArticlesController < ApplicationController 2 | before_action :set_article, only: [:show, :update, :destroy] 3 | 4 | # GET /articles 5 | # GET /articles.json 6 | def index 7 | @articles = Article.all 8 | 9 | render json: @articles 10 | end 11 | 12 | # GET /articles/1 13 | # GET /articles/1.json 14 | def show 15 | render json: @article 16 | end 17 | 18 | # POST /articles 19 | # POST /articles.json 20 | def create 21 | @article = Article.new(article_params) 22 | 23 | if @article.save 24 | render json: @article, status: :created, location: @article 25 | else 26 | render json: @article.errors, status: :unprocessable_entity 27 | end 28 | end 29 | 30 | # PATCH/PUT /articles/1 31 | # PATCH/PUT /articles/1.json 32 | def update 33 | @article = Article.find(params[:id]) 34 | 35 | if @article.update(article_params) 36 | head :no_content 37 | else 38 | render json: @article.errors, status: :unprocessable_entity 39 | end 40 | end 41 | 42 | # DELETE /articles/1 43 | # DELETE /articles/1.json 44 | def destroy 45 | @article.destroy 46 | 47 | head :no_content 48 | end 49 | 50 | private 51 | 52 | def set_article 53 | @article = Article.find(params[:id]) 54 | end 55 | 56 | def article_params 57 | params.require(:article).permit(:title, :body) 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /app/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/app/mailers/.keep -------------------------------------------------------------------------------- /app/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/app/models/.keep -------------------------------------------------------------------------------- /app/models/article.rb: -------------------------------------------------------------------------------- 1 | class Article < ActiveRecord::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/app/models/concerns/.keep -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path("../spring", __FILE__) 4 | rescue LoadError 5 | end 6 | APP_PATH = File.expand_path('../../config/application', __FILE__) 7 | require_relative '../config/boot' 8 | require 'rails/commands' 9 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path("../spring", __FILE__) 4 | rescue LoadError 5 | end 6 | require_relative '../config/boot' 7 | require 'rake' 8 | Rake.application.run 9 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | 4 | # path to your application root. 5 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 6 | 7 | Dir.chdir APP_ROOT do 8 | # This script is a starting point to setup your application. 9 | # Add necessary setup steps to this file: 10 | 11 | puts "== Installing dependencies ==" 12 | system "gem install bundler --conservative" 13 | system "bundle check || bundle install" 14 | 15 | # puts "\n== Copying sample files ==" 16 | # unless File.exist?("config/database.yml") 17 | # system "cp config/database.yml.sample config/database.yml" 18 | # end 19 | 20 | puts "\n== Preparing database ==" 21 | system "bin/rake db:setup" 22 | 23 | puts "\n== Removing old logs and tempfiles ==" 24 | system "rm -f log/*" 25 | system "rm -rf tmp/cache" 26 | 27 | puts "\n== Restarting application server ==" 28 | system "touch tmp/restart.txt" 29 | end 30 | -------------------------------------------------------------------------------- /bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require "rubygems" 8 | require "bundler" 9 | 10 | if match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m) 11 | Gem.paths = { "GEM_PATH" => [Bundler.bundle_path.to_s, *Gem.path].uniq } 12 | gem "spring", match[1] 13 | require "spring/binstub" 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /client/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "./bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /client/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | bower_components/ 3 | .sass-cache/ 4 | .idea/ 5 | .tmp/ 6 | dist/ 7 | -------------------------------------------------------------------------------- /client/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "strict": true, 3 | "bitwise": true, 4 | "curly": true, 5 | "eqeqeq": true, 6 | "latedef": false, 7 | "noarg": true, 8 | "undef": true, 9 | "unused": true, 10 | "validthis": true, 11 | "jasmine": true, 12 | "globals": { 13 | "angular": false, 14 | "inject": false, 15 | "module": false 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /client/.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-gulp-angular": { 3 | "version": "0.12.1", 4 | "props": { 5 | "angularVersion": "~1.4.0", 6 | "angularModules": [ 7 | { 8 | "key": "animate", 9 | "module": "ngAnimate" 10 | }, 11 | { 12 | "key": "cookies", 13 | "module": "ngCookies" 14 | }, 15 | { 16 | "key": "touch", 17 | "module": "ngTouch" 18 | }, 19 | { 20 | "key": "sanitize", 21 | "module": "ngSanitize" 22 | } 23 | ], 24 | "jQuery": { 25 | "key": "jquery2" 26 | }, 27 | "resource": { 28 | "key": "angular-resource", 29 | "module": "ngResource" 30 | }, 31 | "router": { 32 | "key": "ui-router", 33 | "module": "ui.router" 34 | }, 35 | "ui": { 36 | "key": "bootstrap", 37 | "module": null 38 | }, 39 | "bootstrapComponents": { 40 | "key": "ui-bootstrap", 41 | "module": "ui.bootstrap" 42 | }, 43 | "cssPreprocessor": { 44 | "key": "none", 45 | "extension": "css" 46 | }, 47 | "jsPreprocessor": { 48 | "key": "none", 49 | "extension": "js", 50 | "srcExtension": "js" 51 | }, 52 | "htmlPreprocessor": { 53 | "key": "none", 54 | "extension": "html" 55 | }, 56 | "foundationComponents": { 57 | "name": null, 58 | "version": null, 59 | "key": null, 60 | "module": null 61 | }, 62 | "paths": { 63 | "src": "src", 64 | "dist": "dist", 65 | "e2e": "e2e", 66 | "tmp": ".tmp" 67 | } 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /client/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angularRails", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "angular-animate": "~1.4.0", 6 | "angular-cookies": "~1.4.0", 7 | "angular-touch": "~1.4.0", 8 | "angular-sanitize": "~1.4.0", 9 | "jquery": "~2.1.4", 10 | "angular-resource": "~1.4.0", 11 | "angular-ui-router": "~0.2.15", 12 | "bootstrap": "~3.3.4", 13 | "angular-bootstrap": "~0.13.0", 14 | "malarkey": "yuanqing/malarkey#~1.3.0", 15 | "toastr": "~2.1.1", 16 | "moment": "~2.10.3", 17 | "animate.css": "~3.3.0", 18 | "angular": "~1.4.0" 19 | }, 20 | "devDependencies": { 21 | "angular-mocks": "~1.4.0" 22 | }, 23 | "overrides": { 24 | "bootstrap": { 25 | "main": [ 26 | "dist/css/bootstrap.css", 27 | "dist/js/bootstrap.js" 28 | ] 29 | } 30 | }, 31 | "resolutions": { 32 | "jquery": "~2.1.4", 33 | "angular": "~1.4.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /client/e2e/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.jshintrc", 3 | "globals": { 4 | "browser": false, 5 | "element": false, 6 | "by": false, 7 | "$": false, 8 | "$$": false 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /client/e2e/main.po.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file uses the Page Object pattern to define the main page for tests 3 | * https://docs.google.com/presentation/d/1B6manhG0zEXkC-H-tPo2vwU06JhL8w9-XCF9oehXzAQ 4 | */ 5 | 6 | 'use strict'; 7 | 8 | var MainPage = function() { 9 | this.jumbEl = element(by.css('.jumbotron')); 10 | this.h1El = this.jumbEl.element(by.css('h1')); 11 | this.imgEl = this.jumbEl.element(by.css('img')); 12 | this.thumbnailEls = element(by.css('body')).all(by.repeater('awesomeThing in main.awesomeThings')); 13 | }; 14 | 15 | module.exports = new MainPage(); 16 | -------------------------------------------------------------------------------- /client/e2e/main.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('The main view', function () { 4 | var page; 5 | 6 | beforeEach(function () { 7 | browser.get('/index.html'); 8 | page = require('./main.po'); 9 | }); 10 | 11 | it('should include jumbotron with correct data', function() { 12 | expect(page.h1El.getText()).toBe('\'Allo, \'Allo!'); 13 | expect(page.imgEl.getAttribute('src')).toMatch(/assets\/images\/yeoman.png$/); 14 | expect(page.imgEl.getAttribute('alt')).toBe('I\'m Yeoman'); 15 | }); 16 | 17 | it('should list more than 5 awesome things', function () { 18 | expect(page.thumbnailEls.count()).toBeGreaterThan(5); 19 | }); 20 | 21 | }); 22 | -------------------------------------------------------------------------------- /client/gulp/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.jshintrc", 3 | "node": true 4 | } 5 | -------------------------------------------------------------------------------- /client/gulp/build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var gulp = require('gulp'); 5 | var conf = require('./conf'); 6 | 7 | var $ = require('gulp-load-plugins')({ 8 | pattern: ['gulp-*', 'main-bower-files', 'uglify-save-license', 'del'] 9 | }); 10 | 11 | gulp.task('partials', function () { 12 | return gulp.src([ 13 | path.join(conf.paths.src, '/app/**/*.html'), 14 | path.join(conf.paths.tmp, '/serve/app/**/*.html') 15 | ]) 16 | .pipe($.minifyHtml({ 17 | empty: true, 18 | spare: true, 19 | quotes: true 20 | })) 21 | .pipe($.angularTemplatecache('templateCacheHtml.js', { 22 | module: 'angularRails', 23 | root: 'app' 24 | })) 25 | .pipe(gulp.dest(conf.paths.tmp + '/partials/')); 26 | }); 27 | 28 | gulp.task('html', ['inject', 'partials'], function () { 29 | var partialsInjectFile = gulp.src(path.join(conf.paths.tmp, '/partials/templateCacheHtml.js'), { read: false }); 30 | var partialsInjectOptions = { 31 | starttag: '', 32 | ignorePath: path.join(conf.paths.tmp, '/partials'), 33 | addRootSlash: false 34 | }; 35 | 36 | var htmlFilter = $.filter('*.html'); 37 | var jsFilter = $.filter('**/*.js'); 38 | var cssFilter = $.filter('**/*.css'); 39 | var assets; 40 | 41 | return gulp.src(path.join(conf.paths.tmp, '/serve/*.html')) 42 | .pipe($.inject(partialsInjectFile, partialsInjectOptions)) 43 | .pipe(assets = $.useref.assets()) 44 | .pipe($.rev()) 45 | .pipe(jsFilter) 46 | .pipe($.ngAnnotate()) 47 | .pipe($.uglify({ preserveComments: $.uglifySaveLicense })).on('error', conf.errorHandler('Uglify')) 48 | .pipe(jsFilter.restore()) 49 | .pipe(cssFilter) 50 | .pipe($.csso()) 51 | .pipe(cssFilter.restore()) 52 | .pipe(assets.restore()) 53 | .pipe($.useref()) 54 | .pipe($.revReplace()) 55 | .pipe(htmlFilter) 56 | .pipe($.minifyHtml({ 57 | empty: true, 58 | spare: true, 59 | quotes: true, 60 | conditionals: true 61 | })) 62 | .pipe(htmlFilter.restore()) 63 | .pipe(gulp.dest(path.join(conf.paths.dist, '/'))) 64 | .pipe($.size({ title: path.join(conf.paths.dist, '/'), showFiles: true })); 65 | }); 66 | 67 | // Only applies for fonts from bower dependencies 68 | // Custom fonts are handled by the "other" task 69 | gulp.task('fonts', function () { 70 | return gulp.src($.mainBowerFiles()) 71 | .pipe($.filter('**/*.{eot,svg,ttf,woff,woff2}')) 72 | .pipe($.flatten()) 73 | .pipe(gulp.dest(path.join(conf.paths.dist, '/fonts/'))); 74 | }); 75 | 76 | gulp.task('other', function () { 77 | var fileFilter = $.filter(function (file) { 78 | return file.stat.isFile(); 79 | }); 80 | 81 | return gulp.src([ 82 | path.join(conf.paths.src, '/**/*'), 83 | path.join('!' + conf.paths.src, '/**/*.{html,css,js}') 84 | ]) 85 | .pipe(fileFilter) 86 | .pipe(gulp.dest(path.join(conf.paths.dist, '/'))); 87 | }); 88 | 89 | gulp.task('clean', function (done) { 90 | $.del([path.join(conf.paths.dist, '/'), path.join(conf.paths.tmp, '/')], { force: true }, done); 91 | }); 92 | 93 | gulp.task('build', ['html', 'fonts', 'other']); 94 | -------------------------------------------------------------------------------- /client/gulp/conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file contains the variables used in other gulp files 3 | * which defines tasks 4 | * By design, we only put there very generic config values 5 | * which are used in several places to keep good readability 6 | * of the tasks 7 | */ 8 | 9 | var gutil = require('gulp-util'); 10 | 11 | /** 12 | * The main paths of your project handle these with care 13 | */ 14 | exports.paths = { 15 | src: 'src', 16 | dist: '../public', 17 | tmp: '.tmp', 18 | e2e: 'e2e' 19 | }; 20 | 21 | /** 22 | * Wiredep is the lib which inject bower dependencies in your project 23 | * Mainly used to inject script tags in the index.html but also used 24 | * to inject css preprocessor deps and js files in karma 25 | */ 26 | exports.wiredep = { 27 | exclude: [/bootstrap.js$/], 28 | directory: 'bower_components' 29 | }; 30 | 31 | /** 32 | * Common implementation for an error handler of a Gulp plugin 33 | */ 34 | exports.errorHandler = function(title) { 35 | 'use strict'; 36 | 37 | return function(err) { 38 | gutil.log(gutil.colors.red('[' + title + ']'), err.toString()); 39 | this.emit('end'); 40 | }; 41 | }; 42 | -------------------------------------------------------------------------------- /client/gulp/e2e-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var gulp = require('gulp'); 5 | var conf = require('./conf'); 6 | 7 | var browserSync = require('browser-sync'); 8 | 9 | var $ = require('gulp-load-plugins')(); 10 | 11 | // Downloads the selenium webdriver 12 | gulp.task('webdriver-update', $.protractor.webdriver_update); 13 | 14 | gulp.task('webdriver-standalone', $.protractor.webdriver_standalone); 15 | 16 | function runProtractor (done) { 17 | var params = process.argv; 18 | var args = params.length > 3 ? [params[3], params[4]] : []; 19 | 20 | gulp.src(path.join(conf.paths.e2e, '/**/*.js')) 21 | .pipe($.protractor.protractor({ 22 | configFile: 'protractor.conf.js', 23 | args: args 24 | })) 25 | .on('error', function (err) { 26 | // Make sure failed tests cause gulp to exit non-zero 27 | throw err; 28 | }) 29 | .on('end', function () { 30 | // Close browser sync server 31 | browserSync.exit(); 32 | done(); 33 | }); 34 | } 35 | 36 | gulp.task('protractor', ['protractor:src']); 37 | gulp.task('protractor:src', ['serve:e2e', 'webdriver-update'], runProtractor); 38 | gulp.task('protractor:dist', ['serve:e2e-dist', 'webdriver-update'], runProtractor); 39 | -------------------------------------------------------------------------------- /client/gulp/inject.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var gulp = require('gulp'); 5 | var conf = require('./conf'); 6 | 7 | var $ = require('gulp-load-plugins')(); 8 | 9 | var wiredep = require('wiredep').stream; 10 | var _ = require('lodash'); 11 | 12 | gulp.task('inject', ['scripts'], function () { 13 | var injectStyles = gulp.src([ 14 | path.join(conf.paths.src, '/app/**/*.css') 15 | ], { read: false }); 16 | 17 | var injectScripts = gulp.src([ 18 | path.join(conf.paths.src, '/app/**/*.module.js'), 19 | path.join(conf.paths.src, '/app/**/*.js'), 20 | path.join('!' + conf.paths.src, '/app/**/*.spec.js'), 21 | path.join('!' + conf.paths.src, '/app/**/*.mock.js') 22 | ]) 23 | .pipe($.angularFilesort()).on('error', conf.errorHandler('AngularFilesort')); 24 | 25 | var injectOptions = { 26 | ignorePath: [conf.paths.src, path.join(conf.paths.tmp, '/serve')], 27 | addRootSlash: false 28 | }; 29 | 30 | return gulp.src(path.join(conf.paths.src, '/*.html')) 31 | .pipe($.inject(injectStyles, injectOptions)) 32 | .pipe($.inject(injectScripts, injectOptions)) 33 | .pipe(wiredep(_.extend({}, conf.wiredep))) 34 | .pipe(gulp.dest(path.join(conf.paths.tmp, '/serve'))); 35 | }); 36 | -------------------------------------------------------------------------------- /client/gulp/scripts.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var gulp = require('gulp'); 5 | var conf = require('./conf'); 6 | 7 | var browserSync = require('browser-sync'); 8 | 9 | var $ = require('gulp-load-plugins')(); 10 | 11 | gulp.task('scripts', function () { 12 | return gulp.src(path.join(conf.paths.src, '/app/**/*.js')) 13 | .pipe($.jshint()) 14 | .pipe($.jshint.reporter('jshint-stylish')) 15 | .pipe(browserSync.reload({ stream: true })) 16 | .pipe($.size()) 17 | }); 18 | -------------------------------------------------------------------------------- /client/gulp/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var gulp = require('gulp'); 5 | var conf = require('./conf'); 6 | 7 | var browserSync = require('browser-sync'); 8 | var browserSyncSpa = require('browser-sync-spa'); 9 | 10 | var util = require('util'); 11 | 12 | var proxyMiddleware = require('http-proxy-middleware'); 13 | var exec = require('child_process').exec; 14 | 15 | function browserSyncInit(baseDir, browser) { 16 | browser = browser === undefined ? 'default' : browser; 17 | 18 | var routes = null; 19 | if(baseDir === conf.paths.src || (util.isArray(baseDir) && baseDir.indexOf(conf.paths.src) !== -1)) { 20 | routes = { 21 | '/bower_components': 'bower_components' 22 | }; 23 | } 24 | 25 | var server = { 26 | baseDir: baseDir, 27 | routes: routes, 28 | middleware: [ 29 | proxyMiddleware('/api', { target: 'http://localhost:3000' }) 30 | ] 31 | }; 32 | 33 | /* 34 | * You can add a proxy to your backend by uncommenting the line bellow. 35 | * You just have to configure a context which will we redirected and the target url. 36 | * Example: $http.get('/users') requests will be automatically proxified. 37 | * 38 | * For more details and option, https://github.com/chimurai/http-proxy-middleware/blob/v0.0.5/README.md 39 | */ 40 | // server.middleware = proxyMiddleware('/users', {target: 'http://jsonplaceholder.typicode.com', proxyHost: 'jsonplaceholder.typicode.com'}); 41 | 42 | browserSync.instance = browserSync.init({ 43 | port: 9000, 44 | startPath: '/', 45 | server: server, 46 | browser: browser 47 | }); 48 | } 49 | 50 | browserSync.use(browserSyncSpa({ 51 | selector: '[ng-app]'// Only needed for angular apps 52 | })); 53 | 54 | gulp.task('serve', ['watch'], function () { 55 | browserSyncInit([path.join(conf.paths.tmp, '/serve'), conf.paths.src]); 56 | }); 57 | 58 | gulp.task('serve:dist', ['build'], function () { 59 | browserSyncInit(conf.paths.dist); 60 | }); 61 | 62 | gulp.task('serve:e2e', ['inject'], function () { 63 | browserSyncInit([conf.paths.tmp + '/serve', conf.paths.src], []); 64 | }); 65 | 66 | gulp.task('serve:e2e-dist', ['build'], function () { 67 | browserSyncInit(conf.paths.dist, []); 68 | }); 69 | 70 | gulp.task('rails', function() { 71 | exec("../bin/rails s"); 72 | }); 73 | 74 | gulp.task('serve:full-stack', ['rails', 'serve']); 75 | -------------------------------------------------------------------------------- /client/gulp/unit-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var gulp = require('gulp'); 5 | var conf = require('./conf'); 6 | 7 | var karma = require('karma'); 8 | 9 | function runTests (singleRun, done) { 10 | karma.server.start({ 11 | configFile: path.join(__dirname, '/../karma.conf.js'), 12 | singleRun: singleRun, 13 | autoWatch: !singleRun 14 | }, function() { 15 | done(); 16 | }); 17 | } 18 | 19 | gulp.task('test', ['scripts'], function(done) { 20 | runTests(true, done); 21 | }); 22 | 23 | gulp.task('test:auto', ['watch'], function(done) { 24 | runTests(false, done); 25 | }); 26 | -------------------------------------------------------------------------------- /client/gulp/watch.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var gulp = require('gulp'); 5 | var conf = require('./conf'); 6 | 7 | var browserSync = require('browser-sync'); 8 | 9 | function isOnlyChange(event) { 10 | return event.type === 'changed'; 11 | } 12 | 13 | gulp.task('watch', ['inject'], function () { 14 | 15 | gulp.watch([path.join(conf.paths.src, '/*.html'), 'bower.json'], ['inject']); 16 | 17 | gulp.watch(path.join(conf.paths.src, '/app/**/*.css'), function(event) { 18 | if(isOnlyChange(event)) { 19 | browserSync.reload(event.path); 20 | } else { 21 | gulp.start('inject'); 22 | } 23 | }); 24 | 25 | gulp.watch(path.join(conf.paths.src, '/app/**/*.js'), function(event) { 26 | if(isOnlyChange(event)) { 27 | gulp.start('scripts'); 28 | } else { 29 | gulp.start('inject'); 30 | } 31 | }); 32 | 33 | gulp.watch(path.join(conf.paths.src, '/app/**/*.html'), function(event) { 34 | browserSync.reload(event.path); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /client/gulpfile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Welcome to your gulpfile! 3 | * The gulp tasks are splitted in several files in the gulp directory 4 | * because putting all here was really too long 5 | */ 6 | 7 | 'use strict'; 8 | 9 | var gulp = require('gulp'); 10 | var wrench = require('wrench'); 11 | 12 | /** 13 | * This will load all js or coffee files in the gulp directory 14 | * in order to load all gulp tasks 15 | */ 16 | wrench.readdirSyncRecursive('./gulp').filter(function(file) { 17 | return (/\.(js|coffee)$/i).test(file); 18 | }).map(function(file) { 19 | require('./gulp/' + file); 20 | }); 21 | 22 | 23 | /** 24 | * Default task clean temporaries directories and launch the 25 | * main optimization build task 26 | */ 27 | gulp.task('default', ['clean'], function () { 28 | gulp.start('build'); 29 | }); 30 | -------------------------------------------------------------------------------- /client/karma.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var conf = require('./gulp/conf'); 5 | 6 | var _ = require('lodash'); 7 | var wiredep = require('wiredep'); 8 | 9 | function listFiles() { 10 | var wiredepOptions = _.extend({}, conf.wiredep, { 11 | dependencies: true, 12 | devDependencies: true 13 | }); 14 | 15 | return wiredep(wiredepOptions).js 16 | .concat([ 17 | path.join(conf.paths.src, '/app/**/*.module.js'), 18 | path.join(conf.paths.src, '/app/**/*.js'), 19 | path.join(conf.paths.src, '/**/*.spec.js'), 20 | path.join(conf.paths.src, '/**/*.mock.js'), 21 | path.join(conf.paths.src, '/**/*.html') 22 | ]); 23 | } 24 | 25 | module.exports = function(config) { 26 | 27 | var configuration = { 28 | files: listFiles(), 29 | 30 | singleRun: true, 31 | 32 | autoWatch: false, 33 | 34 | frameworks: ['jasmine', 'angular-filesort'], 35 | 36 | angularFilesort: { 37 | whitelist: [path.join(conf.paths.src, '/**/!(*.html|*.spec|*.mock).js')] 38 | }, 39 | 40 | ngHtml2JsPreprocessor: { 41 | stripPrefix: 'src/', 42 | moduleName: 'angularRails' 43 | }, 44 | 45 | browsers : ['PhantomJS'], 46 | 47 | plugins : [ 48 | 'karma-phantomjs-launcher', 49 | 'karma-angular-filesort', 50 | 'karma-jasmine', 51 | 'karma-ng-html2js-preprocessor' 52 | ], 53 | 54 | preprocessors: { 55 | 'src/**/*.html': ['ng-html2js'] 56 | } 57 | }; 58 | 59 | // This block is needed to execute Chrome on Travis 60 | // If you ever plan to use Chrome and Travis, you can keep it 61 | // If not, you can safely remove it 62 | // https://github.com/karma-runner/karma/issues/1144#issuecomment-53633076 63 | if(configuration.browsers[0] === 'Chrome' && process.env.TRAVIS) { 64 | configuration.customLaunchers = { 65 | 'chrome-travis-ci': { 66 | base: 'Chrome', 67 | flags: ['--no-sandbox'] 68 | } 69 | }; 70 | configuration.browsers = ['chrome-travis-ci']; 71 | } 72 | 73 | config.set(configuration); 74 | }; 75 | -------------------------------------------------------------------------------- /client/protractor.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var paths = require('./.yo-rc.json')['generator-gulp-angular'].props.paths; 4 | 5 | // An example configuration file. 6 | exports.config = { 7 | // The address of a running selenium server. 8 | //seleniumAddress: 'http://localhost:4444/wd/hub', 9 | //seleniumServerJar: deprecated, this should be set on node_modules/protractor/config.json 10 | 11 | // Capabilities to be passed to the webdriver instance. 12 | capabilities: { 13 | 'browserName': 'chrome' 14 | }, 15 | 16 | baseUrl: 'http://localhost:3000', 17 | 18 | // Spec patterns are relative to the current working directly when 19 | // protractor is called. 20 | specs: [paths.e2e + '/**/*.js'], 21 | 22 | // Options to be passed to Jasmine-node. 23 | jasmineNodeOpts: { 24 | showColors: true, 25 | defaultTimeoutInterval: 30000 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /client/src/app/components/articles/articles.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('angularRails') 5 | .controller('ArticlesController', function ($scope, Articles) { 6 | 7 | Articles.query(function (res) { 8 | $scope.articles = res; 9 | }); 10 | 11 | }); 12 | })(); 13 | -------------------------------------------------------------------------------- /client/src/app/components/articles/articles.factory.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('angularRails') 5 | .factory('Articles', function ($resource) { 6 | return $resource('api/v1/articles/:articleId', { 7 | articleId: '@id' 8 | }, { 9 | update: { 10 | method: 'PUT' 11 | } 12 | }); 13 | }); 14 | })(); 15 | -------------------------------------------------------------------------------- /client/src/app/components/articles/articles.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 | 7 |

Articles

8 | 9 |
10 |

{{ article.title }}

11 |

{{ article.body }}

12 |
13 | 14 |
15 | -------------------------------------------------------------------------------- /client/src/app/components/githubContributor/githubContributor.service.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('angularRails') 6 | .factory('githubContributor', githubContributor); 7 | 8 | /** @ngInject */ 9 | function githubContributor($log, $http) { 10 | var apiHost = 'https://api.github.com/repos/Swiip/generator-gulp-angular'; 11 | 12 | var service = { 13 | apiHost: apiHost, 14 | getContributors: getContributors 15 | }; 16 | 17 | return service; 18 | 19 | function getContributors(limit) { 20 | if (!limit) { 21 | limit = 30; 22 | } 23 | 24 | return $http.get(apiHost + '/contributors?per_page=' + limit) 25 | .then(getContributorsComplete) 26 | .catch(getContributorsFailed); 27 | 28 | function getContributorsComplete(response) { 29 | return response.data; 30 | } 31 | 32 | function getContributorsFailed(error) { 33 | $log.error('XHR Failed for getContributors.\n' + angular.toJson(error.data, true)); 34 | } 35 | } 36 | } 37 | })(); 38 | -------------------------------------------------------------------------------- /client/src/app/components/malarkey/malarkey.css: -------------------------------------------------------------------------------- 1 | .acme-malarkey { 2 | text-transform: capitalize; 3 | color: #cb3837; 4 | } 5 | 6 | .acme-malarkey:after { 7 | animation: cursor-blink 0.4s linear infinite; 8 | content: "|"; 9 | color: #cb3837; 10 | } 11 | 12 | @keyframes cursor-blink { 13 | 1% { 14 | opacity: 0; 15 | } 16 | 40% { 17 | opacity: 0; 18 | } 19 | 60% { 20 | opacity: 1; 21 | } 22 | 100% { 23 | opacity: 1; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /client/src/app/components/malarkey/malarkey.directive.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('angularRails') 6 | .directive('acmeMalarkey', acmeMalarkey); 7 | 8 | /** @ngInject */ 9 | function acmeMalarkey(malarkey) { 10 | var directive = { 11 | restrict: 'E', 12 | scope: { 13 | extraValues: '=', 14 | }, 15 | template: ' ', 16 | link: linkFunc, 17 | controller: MalarkeyController, 18 | controllerAs: 'vm' 19 | }; 20 | 21 | return directive; 22 | 23 | function linkFunc(scope, el, attr, vm) { 24 | var watcher; 25 | var typist = malarkey(el[0], { 26 | typeSpeed: 40, 27 | deleteSpeed: 40, 28 | pauseDelay: 800, 29 | loop: true, 30 | postfix: ' ' 31 | }); 32 | 33 | el.addClass('acme-malarkey'); 34 | 35 | angular.forEach(scope.extraValues, function(value) { 36 | typist.type(value).pause().delete(); 37 | }); 38 | 39 | watcher = scope.$watch('vm.contributors', function() { 40 | angular.forEach(vm.contributors, function(contributor) { 41 | typist.type(contributor.login).pause().delete(); 42 | }); 43 | }); 44 | 45 | scope.$on('$destroy', function () { 46 | watcher(); 47 | }); 48 | } 49 | 50 | /** @ngInject */ 51 | function MalarkeyController($log, githubContributor) { 52 | var vm = this; 53 | 54 | vm.contributors = []; 55 | 56 | activate(); 57 | 58 | function activate() { 59 | return getContributors().then(function() { 60 | $log.info('Activated Contributors View'); 61 | }); 62 | } 63 | 64 | function getContributors() { 65 | return githubContributor.getContributors(10).then(function(data) { 66 | vm.contributors = data; 67 | 68 | return vm.contributors; 69 | }); 70 | } 71 | } 72 | 73 | } 74 | 75 | })(); 76 | -------------------------------------------------------------------------------- /client/src/app/components/navbar/navbar.css: -------------------------------------------------------------------------------- 1 | .acme-navbar-text { 2 | color: white; 3 | } 4 | -------------------------------------------------------------------------------- /client/src/app/components/navbar/navbar.directive.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('angularRails') 6 | .directive('acmeNavbar', acmeNavbar); 7 | 8 | /** @ngInject */ 9 | function acmeNavbar() { 10 | var directive = { 11 | restrict: 'E', 12 | templateUrl: 'app/components/navbar/navbar.html', 13 | scope: { 14 | creationDate: '=' 15 | }, 16 | controller: NavbarController, 17 | controllerAs: 'vm', 18 | bindToController: true 19 | }; 20 | 21 | return directive; 22 | 23 | /** @ngInject */ 24 | function NavbarController(moment) { 25 | var vm = this; 26 | 27 | // "vm.creation" is avaible by directive option "bindToController: true" 28 | vm.relativeDate = moment(vm.creationDate).fromNow(); 29 | } 30 | } 31 | 32 | })(); 33 | -------------------------------------------------------------------------------- /client/src/app/components/navbar/navbar.html: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /client/src/app/components/webDevTec/webDevTec.service.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('angularRails') 6 | .service('webDevTec', webDevTec); 7 | 8 | /** @ngInject */ 9 | function webDevTec() { 10 | var data = [ 11 | { 12 | 'title': 'AngularJS', 13 | 'url': 'https://angularjs.org/', 14 | 'description': 'HTML enhanced for web apps!', 15 | 'logo': 'angular.png' 16 | }, 17 | { 18 | 'title': 'BrowserSync', 19 | 'url': 'http://browsersync.io/', 20 | 'description': 'Time-saving synchronised browser testing.', 21 | 'logo': 'browsersync.png' 22 | }, 23 | { 24 | 'title': 'GulpJS', 25 | 'url': 'http://gulpjs.com/', 26 | 'description': 'The streaming build system.', 27 | 'logo': 'gulp.png' 28 | }, 29 | { 30 | 'title': 'Jasmine', 31 | 'url': 'http://jasmine.github.io/', 32 | 'description': 'Behavior-Driven JavaScript.', 33 | 'logo': 'jasmine.png' 34 | }, 35 | { 36 | 'title': 'Karma', 37 | 'url': 'http://karma-runner.github.io/', 38 | 'description': 'Spectacular Test Runner for JavaScript.', 39 | 'logo': 'karma.png' 40 | }, 41 | { 42 | 'title': 'Protractor', 43 | 'url': 'https://github.com/angular/protractor', 44 | 'description': 'End to end test framework for AngularJS applications built on top of WebDriverJS.', 45 | 'logo': 'protractor.png' 46 | }, 47 | { 48 | 'title': 'Bootstrap', 49 | 'url': 'http://getbootstrap.com/', 50 | 'description': 'Bootstrap is the most popular HTML, CSS, and JS framework for developing responsive, mobile first projects on the web.', 51 | 'logo': 'bootstrap.png' 52 | }, 53 | { 54 | 'title': 'Angular UI Bootstrap', 55 | 'url': 'http://angular-ui.github.io/bootstrap/', 56 | 'description': 'Bootstrap components written in pure AngularJS by the AngularUI Team.', 57 | 'logo': 'ui-bootstrap.png' 58 | } 59 | ]; 60 | 61 | this.getTec = getTec; 62 | 63 | function getTec() { 64 | return data; 65 | } 66 | } 67 | 68 | })(); 69 | -------------------------------------------------------------------------------- /client/src/app/index.config.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('angularRails') 6 | .config(config); 7 | 8 | /** @ngInject */ 9 | function config($logProvider, toastr) { 10 | // Enable log 11 | $logProvider.debugEnabled(true); 12 | 13 | // Set options third-party lib 14 | toastr.options.timeOut = 3000; 15 | toastr.options.positionClass = 'toast-top-right'; 16 | toastr.options.preventDuplicates = true; 17 | toastr.options.progressBar = true; 18 | } 19 | 20 | })(); 21 | -------------------------------------------------------------------------------- /client/src/app/index.constants.js: -------------------------------------------------------------------------------- 1 | /* global malarkey:false, toastr:false, moment:false */ 2 | (function() { 3 | 'use strict'; 4 | 5 | angular 6 | .module('angularRails') 7 | .constant('malarkey', malarkey) 8 | .constant('toastr', toastr) 9 | .constant('moment', moment); 10 | 11 | })(); 12 | -------------------------------------------------------------------------------- /client/src/app/index.css: -------------------------------------------------------------------------------- 1 | .browsehappy { 2 | margin: 0.2em 0; 3 | background: #ccc; 4 | color: #000; 5 | padding: 0.2em 0; 6 | } 7 | 8 | .thumbnail { 9 | height: 200px; 10 | } 11 | 12 | .thumbnail img.pull-right { 13 | width: 50px; 14 | } 15 | -------------------------------------------------------------------------------- /client/src/app/index.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('angularRails', ['ngAnimate', 'ngCookies', 'ngTouch', 'ngSanitize', 'ngResource', 'ui.router', 'ui.bootstrap']); 6 | 7 | })(); 8 | -------------------------------------------------------------------------------- /client/src/app/index.route.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('angularRails') 6 | .config(routeConfig); 7 | 8 | /** @ngInject */ 9 | function routeConfig($stateProvider, $urlRouterProvider) { 10 | $stateProvider 11 | .state('home', { 12 | url: '/', 13 | templateUrl: 'app/main/main.html', 14 | controller: 'MainController', 15 | controllerAs: 'main' 16 | }) 17 | .state('articles', { 18 | url: '/articles', 19 | templateUrl: 'app/components/articles/articles.html', 20 | controller: 'ArticlesController' 21 | }); 22 | 23 | $urlRouterProvider.otherwise('/'); 24 | } 25 | 26 | })(); 27 | -------------------------------------------------------------------------------- /client/src/app/index.run.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('angularRails') 6 | .run(runBlock); 7 | 8 | /** @ngInject */ 9 | function runBlock($log) { 10 | 11 | $log.debug('runBlock end'); 12 | } 13 | 14 | })(); 15 | -------------------------------------------------------------------------------- /client/src/app/main/main.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('angularRails') 6 | .controller('MainController', MainController); 7 | 8 | /** @ngInject */ 9 | function MainController($timeout, webDevTec, toastr) { 10 | var vm = this; 11 | 12 | vm.awesomeThings = []; 13 | vm.classAnimation = ''; 14 | vm.creationDate = 1437227433451; 15 | vm.showToastr = showToastr; 16 | 17 | activate(); 18 | 19 | function activate() { 20 | getWebDevTec(); 21 | $timeout(function() { 22 | vm.classAnimation = 'rubberBand'; 23 | }, 4000); 24 | } 25 | 26 | function showToastr() { 27 | toastr.info('Fork generator-gulp-angular'); 28 | vm.classAnimation = ''; 29 | } 30 | 31 | function getWebDevTec() { 32 | vm.awesomeThings = webDevTec.getTec(); 33 | 34 | angular.forEach(vm.awesomeThings, function(awesomeThing) { 35 | awesomeThing.rank = Math.random(); 36 | }); 37 | } 38 | } 39 | })(); 40 | -------------------------------------------------------------------------------- /client/src/app/main/main.controller.spec.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | describe('controllers', function(){ 5 | 6 | beforeEach(module('angularRails')); 7 | 8 | it('should define more than 5 awesome things', inject(function($controller) { 9 | var vm = $controller('MainController'); 10 | 11 | expect(angular.isArray(vm.awesomeThings)).toBeTruthy(); 12 | expect(vm.awesomeThings.length > 5).toBeTruthy(); 13 | })); 14 | }); 15 | })(); 16 | -------------------------------------------------------------------------------- /client/src/app/main/main.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 | 7 |
8 |

'Allo, 'Allo!

9 |

10 | I'm Yeoman
11 | Always a pleasure scaffolding your apps. 12 |

13 |

14 | 15 |

16 |

17 | With ♥ thanks to the contributions of 18 |

19 |
20 | 21 |
22 |
23 |
24 | {{ awesomeThing.title }} 25 |
26 |

{{ awesomeThing.title }}

27 |

{{ awesomeThing.description }}

28 |

{{ awesomeThing.url }}

29 |
30 |
31 |
32 |
33 | 34 |
35 | -------------------------------------------------------------------------------- /client/src/assets/images/angular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/client/src/assets/images/angular.png -------------------------------------------------------------------------------- /client/src/assets/images/bootstrap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/client/src/assets/images/bootstrap.png -------------------------------------------------------------------------------- /client/src/assets/images/browsersync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/client/src/assets/images/browsersync.png -------------------------------------------------------------------------------- /client/src/assets/images/gulp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/client/src/assets/images/gulp.png -------------------------------------------------------------------------------- /client/src/assets/images/jasmine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/client/src/assets/images/jasmine.png -------------------------------------------------------------------------------- /client/src/assets/images/karma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/client/src/assets/images/karma.png -------------------------------------------------------------------------------- /client/src/assets/images/protractor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/client/src/assets/images/protractor.png -------------------------------------------------------------------------------- /client/src/assets/images/ui-bootstrap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/client/src/assets/images/ui-bootstrap.png -------------------------------------------------------------------------------- /client/src/assets/images/yeoman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/client/src/assets/images/yeoman.png -------------------------------------------------------------------------------- /client/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/client/src/favicon.ico -------------------------------------------------------------------------------- /client/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | angularRails 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run Rails.application 5 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module RailsAngular 10 | class Application < Rails::Application 11 | # Settings in config/environments/* take precedence over those specified here. 12 | # Application configuration should go into files in config/initializers 13 | # -- all .rb files in that directory are automatically loaded. 14 | 15 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 16 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 17 | # config.time_zone = 'Central Time (US & Canada)' 18 | 19 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 20 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 21 | # config.i18n.default_locale = :de 22 | 23 | # Do not swallow errors in after_commit/after_rollback callbacks. 24 | config.active_record.raise_in_transactional_callbacks = true 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # PostgreSQL. Versions 8.2 and up are supported. 2 | # 3 | # Install the pg driver: 4 | # gem install pg 5 | # On OS X with Homebrew: 6 | # gem install pg -- --with-pg-config=/usr/local/bin/pg_config 7 | # On OS X with MacPorts: 8 | # gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config 9 | # On Windows: 10 | # gem install pg 11 | # Choose the win32 build. 12 | # Install PostgreSQL and put its /bin directory on your path. 13 | # 14 | # Configure Using Gemfile 15 | # gem 'pg' 16 | # 17 | default: &default 18 | adapter: postgresql 19 | encoding: unicode 20 | # For details on connection pooling, see rails configuration guide 21 | # http://guides.rubyonrails.org/configuring.html#database-pooling 22 | pool: 5 23 | 24 | development: 25 | <<: *default 26 | database: railsAngular_development 27 | 28 | # The specified database role being used to connect to postgres. 29 | # To create additional roles in postgres see `$ createuser --help`. 30 | # When left blank, postgres will use the default role. This is 31 | # the same name as the operating system user that initialized the database. 32 | #username: railsAngular 33 | 34 | # The password associated with the postgres role (username). 35 | #password: 36 | 37 | # Connect on a TCP socket. Omitted by default since the client uses a 38 | # domain socket that doesn't need configuration. Windows does not have 39 | # domain sockets, so uncomment these lines. 40 | #host: localhost 41 | 42 | # The TCP port the server listens on. Defaults to 5432. 43 | # If your server runs on a different port number, change accordingly. 44 | #port: 5432 45 | 46 | # Schema search path. The server defaults to $user,public 47 | #schema_search_path: myapp,sharedapp,public 48 | 49 | # Minimum log levels, in increasing order: 50 | # debug5, debug4, debug3, debug2, debug1, 51 | # log, notice, warning, error, fatal, and panic 52 | # Defaults to warning. 53 | #min_messages: notice 54 | 55 | # Warning: The database defined as "test" will be erased and 56 | # re-generated from your development database when you run "rake". 57 | # Do not set this db to the same as development or production. 58 | test: 59 | <<: *default 60 | database: railsAngular_test 61 | 62 | # As with config/secrets.yml, you never want to store sensitive information, 63 | # like your database password, in your source code. If your source code is 64 | # ever seen by anyone, they now have access to your database. 65 | # 66 | # Instead, provide the password as a unix environment variable when you boot 67 | # the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database 68 | # for a full rundown on how to provide these environment variables in a 69 | # production deployment. 70 | # 71 | # On Heroku and other platform providers, you may have a full connection URL 72 | # available as an environment variable. For example: 73 | # 74 | # DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" 75 | # 76 | # You can use this database configuration with: 77 | # 78 | # production: 79 | # url: <%= ENV['DATABASE_URL'] %> 80 | # 81 | production: 82 | <<: *default 83 | database: railsAngular_production 84 | username: railsAngular 85 | password: <%= ENV['RAILSANGULAR_DATABASE_PASSWORD'] %> 86 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports and disable caching. 13 | config.consider_all_requests_local = true 14 | config.action_controller.perform_caching = 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 | # This option may cause significant delays in view rendering with a large 27 | # number of complex assets. 28 | config.assets.debug = true 29 | 30 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 31 | # yet still be able to expire them through the digest params. 32 | config.assets.digest = true 33 | 34 | # Adds additional error checking when serving assets at runtime. 35 | # Checks for improperly declared sprockets dependencies. 36 | # Raises helpful error messages. 37 | config.assets.raise_runtime_errors = true 38 | 39 | # Raises error for missing translations 40 | # config.action_view.raise_on_missing_translations = true 41 | end 42 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Enable Rack::Cache to put a simple HTTP cache in front of your application 18 | # Add `rack-cache` to your Gemfile before enabling this. 19 | # For large-scale production use, consider using a caching reverse proxy like 20 | # NGINX, varnish or squid. 21 | # config.action_dispatch.rack_cache = true 22 | 23 | # Disable serving static files from the `/public` folder by default since 24 | # Apache or NGINX already handles this. 25 | config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present? 26 | 27 | # Compress JavaScripts and CSS. 28 | config.assets.js_compressor = :uglifier 29 | # config.assets.css_compressor = :sass 30 | 31 | # Do not fallback to assets pipeline if a precompiled asset is missed. 32 | config.assets.compile = false 33 | 34 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 35 | # yet still be able to expire them through the digest params. 36 | config.assets.digest = true 37 | 38 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 39 | 40 | # Specifies the header that your server uses for sending files. 41 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 42 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 43 | 44 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 45 | # config.force_ssl = true 46 | 47 | # Use the lowest log level to ensure availability of diagnostic information 48 | # when problems arise. 49 | config.log_level = :debug 50 | 51 | # Prepend all log lines with the following tags. 52 | # config.log_tags = [ :subdomain, :uuid ] 53 | 54 | # Use a different logger for distributed setups. 55 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 56 | 57 | # Use a different cache store in production. 58 | # config.cache_store = :mem_cache_store 59 | 60 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 61 | # config.action_controller.asset_host = 'http://assets.example.com' 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 cannot be found). 69 | config.i18n.fallbacks = true 70 | 71 | # Send deprecation notices to registered listeners. 72 | config.active_support.deprecation = :notify 73 | 74 | # Use default logging formatter so that PID and timestamp are not suppressed. 75 | config.log_formatter = ::Logger::Formatter.new 76 | 77 | # Do not dump schema after migrations. 78 | config.active_record.dump_schema_after_migration = false 79 | end 80 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure static file server for tests with Cache-Control for performance. 16 | config.serve_static_files = true 17 | config.static_cache_control = 'public, max-age=3600' 18 | 19 | # Show full error reports and disable caching. 20 | config.consider_all_requests_local = true 21 | config.action_controller.perform_caching = false 22 | 23 | # Raise exceptions instead of rendering exception templates. 24 | config.action_dispatch.show_exceptions = false 25 | 26 | # Disable request forgery protection in test environment. 27 | config.action_controller.allow_forgery_protection = false 28 | 29 | # Tell Action Mailer not to deliver emails to the real world. 30 | # The :test delivery method accumulates sent emails in the 31 | # ActionMailer::Base.deliveries array. 32 | config.action_mailer.delivery_method = :test 33 | 34 | # Randomize the order test cases are executed. 35 | config.active_support.test_order = :random 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | end 43 | -------------------------------------------------------------------------------- /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/secret_token.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rake secret` to generate a secure secret key. 9 | 10 | # Make sure your secret_key_base is kept private 11 | # if you're sharing your code publicly. 12 | 13 | # Although this is not needed for an api-only application, rails4 14 | # requires secret_key_base or secret_token to be defined, otherwise an 15 | # error is raised. 16 | # Using secret_token for rails3 compatibility. Change to secret_key_base 17 | # to avoid deprecation warning. 18 | # Can be safely removed in a rails3 api-only application. 19 | RailsAngular::Application.config.secret_token = '3a66773a67ac2086fd1062c1c20ee4b05fe6debb1818f1ef14e042952d509915000504d3dbd68ae52043739f506922808805f9815a3df419f591dfde30c5f4d0' 20 | -------------------------------------------------------------------------------- /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 4 | 5 | # Enable parameter wrapping for JSON. 6 | # ActiveSupport.on_load(:action_controller) do 7 | # wrap_parameters format: [:json] if respond_to?(:wrap_parameters) 8 | # end 9 | 10 | # To enable root element in JSON for ActiveRecord objects. 11 | # ActiveSupport.on_load(:active_record) do 12 | # self.include_root_in_json = true 13 | # end 14 | -------------------------------------------------------------------------------- /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 | Rails.application.routes.draw do 2 | 3 | scope '/api' do 4 | namespace :v1, defaults: { format: :json } do 5 | 6 | resources :articles, except: [:new, :edit] 7 | 8 | end 9 | end 10 | 11 | # The priority is based upon order of creation: first created -> highest priority. 12 | # See how all your routes lay out with "rake routes". 13 | 14 | # You can have the root of your site routed with "root" 15 | # root 'welcome#index' 16 | 17 | # Example of regular route: 18 | # get 'products/:id' => 'catalog#view' 19 | 20 | # Example of named route that can be invoked with purchase_url(id: product.id) 21 | # get 'products/:id/purchase' => 'catalog#purchase', as: :purchase 22 | 23 | # Example resource route (maps HTTP verbs to controller actions automatically): 24 | # resources :products 25 | 26 | # Example resource route with options: 27 | # resources :products do 28 | # member do 29 | # get 'short' 30 | # post 'toggle' 31 | # end 32 | # 33 | # collection do 34 | # get 'sold' 35 | # end 36 | # end 37 | 38 | # Example resource route with sub-resources: 39 | # resources :products do 40 | # resources :comments, :sales 41 | # resource :seller 42 | # end 43 | 44 | # Example resource route with more complex sub-resources: 45 | # resources :products do 46 | # resources :comments 47 | # resources :sales do 48 | # get 'recent', on: :collection 49 | # end 50 | # end 51 | 52 | # Example resource route with concerns: 53 | # concern :toggleable do 54 | # post 'toggle' 55 | # end 56 | # resources :posts, concerns: :toggleable 57 | # resources :photos, concerns: :toggleable 58 | 59 | # Example resource route within a namespace: 60 | # namespace :admin do 61 | # # Directs /admin/products/* to Admin::ProductsController 62 | # # (app/controllers/admin/products_controller.rb) 63 | # resources :products 64 | # end 65 | end 66 | -------------------------------------------------------------------------------- /config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rake secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: 92986ecf6925911b787600799764532c8afdff30e8bfa2b534078b8d6bc671d6b7ff41c7cbb51dd77987587eb1d1e2e3643face0abb48716d234c549f270bd14 15 | 16 | test: 17 | secret_key_base: 1f34f702bf43840e8ef3b77f8e0880c52874cfce26cf5a5e2fb08608920e307d5e25fbb93033bba7d7d6517ba8eb5bc250dee378ee4045fb21831dcc477e6698 18 | 19 | # Do not keep production secrets in the repository, 20 | # instead read values from the environment. 21 | production: 22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 23 | -------------------------------------------------------------------------------- /db/migrate/20150718132359_create_articles.rb: -------------------------------------------------------------------------------- 1 | class CreateArticles < ActiveRecord::Migration 2 | def change 3 | create_table :articles do |t| 4 | t.string :title 5 | t.text :body 6 | 7 | t.timestamps null: false 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /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: 20150718132359) do 15 | 16 | # These are extensions that must be enabled in order to support this database 17 | enable_extension "plpgsql" 18 | 19 | create_table "articles", force: :cascade do |t| 20 | t.string "title" 21 | t.text "body" 22 | t.datetime "created_at", null: false 23 | t.datetime "updated_at", null: false 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /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 | 9 | Article.create(title: 'Test Article', 10 | body: 'A test article. Cool!') 11 | Article.create(title: 'Donald Trump Resigns from Presidential Candidacy', 12 | body: 'Thanks goodness') 13 | Article.create(title: 'Child’s Description Of Heaven During Near-Death Experience Specifically Mentions Book Deal', 14 | body: 'I\'d read it!') 15 | Article.create(title: 'New Poll Finds 74% Of Americans Would Be Comfortable Blaming Female President For Problems', 16 | body: 'Classic.') 17 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/lib/assets/.keep -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/lib/tasks/.keep -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/log/.keep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angularRails", 3 | "version": "0.0.1", 4 | "dependencies": {}, 5 | "scripts": { 6 | "test": "gulp test", 7 | "postinstall": "cd client && bower install --config.interactive=false && gulp build" 8 | }, 9 | "dependencies": { 10 | "browser-sync": "~2.7.12", 11 | "browser-sync-spa": "~1.0.2", 12 | "bower": "^1.4.1", 13 | "chalk": "~1.0.0", 14 | "concat-stream": "~1.5.0", 15 | "del": "~1.2.0", 16 | "gulp": "~3.9.0", 17 | "gulp-angular-filesort": "~1.1.1", 18 | "gulp-angular-templatecache": "~1.6.0", 19 | "gulp-autoprefixer": "~2.3.1", 20 | "gulp-csso": "~1.0.0", 21 | "gulp-filter": "~2.0.2", 22 | "gulp-flatten": "~0.0.4", 23 | "gulp-inject": "~1.3.1", 24 | "gulp-jshint": "~1.11.0", 25 | "gulp-load-plugins": "~0.10.0", 26 | "gulp-minify-html": "~1.0.3", 27 | "gulp-ng-annotate": "~1.0.0", 28 | "gulp-protractor": "~1.0.0", 29 | "gulp-rename": "~1.2.2", 30 | "gulp-replace": "~0.5.3", 31 | "gulp-rev": "~5.0.0", 32 | "gulp-rev-replace": "~0.4.2", 33 | "gulp-size": "~1.2.1", 34 | "gulp-sourcemaps": "~1.5.2", 35 | "gulp-uglify": "~1.2.0", 36 | "gulp-useref": "~1.2.0", 37 | "gulp-util": "~3.0.5", 38 | "http-proxy-middleware": "^0.2.0", 39 | "jshint-stylish": "~2.0.0", 40 | "karma": "~0.12.36", 41 | "karma-angular-filesort": "~0.1.0", 42 | "karma-jasmine": "~0.3.5", 43 | "karma-ng-html2js-preprocessor": "~0.1.2", 44 | "karma-phantomjs-launcher": "~0.2.0", 45 | "lodash": "~3.9.3", 46 | "main-bower-files": "~2.8.0", 47 | "merge-stream": "~0.1.7", 48 | "require-dir": "~0.3.0", 49 | "uglify-save-license": "~0.4.1", 50 | "wiredep": "~2.2.2", 51 | "wrench": "~1.5.8" 52 | }, 53 | "engines": { 54 | "node": "0.12.7", 55 | "npm": "2.12.1" 56 | }, 57 | "cacheDirectories": [ 58 | "node_modules", 59 | "./client/bower_components" 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/test/controllers/.keep -------------------------------------------------------------------------------- /test/controllers/articles_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ArticlesControllerTest < ActionController::TestCase 4 | setup do 5 | @article = articles(:one) 6 | end 7 | 8 | test "should get index" do 9 | get :index 10 | assert_response :success 11 | assert_not_nil assigns(:articles) 12 | end 13 | 14 | test "should create article" do 15 | assert_difference('Article.count') do 16 | post :create, article: { body: @article.body, title: @article.title } 17 | end 18 | 19 | assert_response 201 20 | end 21 | 22 | test "should show article" do 23 | get :show, id: @article 24 | assert_response :success 25 | end 26 | 27 | test "should update article" do 28 | put :update, id: @article, article: { body: @article.body, title: @article.title } 29 | assert_response 204 30 | end 31 | 32 | test "should destroy article" do 33 | assert_difference('Article.count', -1) do 34 | delete :destroy, id: @article 35 | end 36 | 37 | assert_response 204 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /test/fixtures/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/test/fixtures/.keep -------------------------------------------------------------------------------- /test/fixtures/articles.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | title: MyString 5 | body: MyText 6 | 7 | two: 8 | title: MyString 9 | body: MyText 10 | -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/test/helpers/.keep -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/test/integration/.keep -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/test/mailers/.keep -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grantgeorge/railsAngularTutorial/c7391d365e1eca823fbab0fada7622213d6a5e2c/test/models/.keep -------------------------------------------------------------------------------- /test/models/article_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ArticleTest < ActiveSupport::TestCase 4 | test "the truth" do 5 | assert true 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | require File.expand_path('../../config/environment', __FILE__) 3 | require 'rails/test_help' 4 | 5 | class ActiveSupport::TestCase 6 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 7 | fixtures :all 8 | 9 | # Add more helper methods to be used by all tests here... 10 | end 11 | --------------------------------------------------------------------------------