39 changed files with 644 additions and 479 deletions
-4
View File
@@ -6,9 +6,5 @@ tmp/
.powrc
.rbenv-version
.DS_Store
public/extjs
public/assets
public/images/icons
config/database.yml
vendor/
.sass-cache/
+1 -1
View File
@@ -1 +1 @@
2.1.1
ruby-2.1.1
+26
View File
@@ -0,0 +1,26 @@
FROM ruby:2.1
# throw errors if Gemfile has been modified since Gemfile.lock
RUN bundle config --global frozen 1
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
RUN apt-get update && apt-get install -y nodejs --no-install-recommends && \
apt-get install -y mysql-client postgresql-client sqlite3 --no-install-recommends && \
apt-get install -y zip unzip --no-install-recommends && \
rm -rf /var/lib/apt/lists/*
COPY Gemfile /usr/src/app/
COPY Gemfile.lock /usr/src/app/
RUN bundle install
COPY install_extjs.rb /usr/src/app/
RUN /usr/src/app/install_extjs.rb
COPY install_famfamfam.rb /usr/src/app/
RUN /usr/src/app/install_famfamfam.rb
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
+23 -23
View File
@@ -1,50 +1,50 @@
source 'https://rubygems.org'
gem 'rails', '3.2.19'
gem 'rails', '3.2.13'
gem 'netzke-cancan'
gem 'netzke-core'
gem 'netzke-basepack'
gem 'netzke-core', '~>0.8.0'
gem 'netzke-basepack', '~>0.8.0'
gem 'acts_as_loggable', :git => 'https://github.com/spacemunkay/acts_as_loggable.git'
gem 'bootstrap-will_paginate'
gem 'bootstrap-will_paginate', '~> 0.0.6'
gem 'cancan'
gem 'decent_exposure', '~> 1.0.1'
gem 'devise', '~> 2.0.4'
gem 'haml-rails'
gem 'jquery-rails'
gem 'haml-rails', '~> 0.3.4'
gem 'jquery-rails', '~> 2.0'
gem 'pg', '~> 0.17.1'
gem 'will_paginate'
gem 'jbuilder'
gem 'will_paginate', '~> 3.0.3'
gem 'jbuilder', '~> 2.0.3'
# Gems used only for assets and not required
# in production environments by default.
group :assets do
gem 'coffee-rails'
gem 'sprockets-rails', '=2.0.0.backport1'
gem 'sprockets', '=2.2.2.backport2'
gem 'sass-rails', github: 'guilleiguaran/sass-rails', branch: 'backport'
gem 'bootstrap-sass'
gem 'uglifier'
gem 'coffee-rails', '~> 3.2.1'
gem "twitter-bootstrap-rails", "~> 2.0.3"
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
# gem 'therubyracer'
gem 'uglifier', '>= 1.0.3'
end
group :development, :test do
gem 'rspec-rails'
gem 'factory_girl_rails'
gem 'pry'
gem 'faker'
gem 'rspec-rails', '~> 2.14.0'
gem 'factory_girl_rails', '~> 1.2'
gem 'pry', '~> 0.9.8'
gem 'faker', '~> 1.2.0'
end
group :test do
# gem 'shoulda-matchers', '~> 1.0.0'
gem 'shoulda-matchers', '~> 1.0.0'
gem 'capybara', '~> 2.2.1'
gem 'poltergeist', '~> 1.5.0'
gem 'database_cleaner', '~> 1.2.0'
gem 'launchy', '~> 2.4.2'
gem 'spork'
gem 'spork', '~> 0.9.2'
#guard dependency for Mac OS 10
gem 'rb-fsevent', :require => false if RUBY_PLATFORM =~ /darwin/i
gem 'guard-spork'
gem 'guard-rspec'
gem 'guard-spork', '~> 1.5.1'
gem 'guard-rspec', '~> 4.2.6'
end
+130 -134
View File
@@ -1,13 +1,3 @@
GIT
remote: git://github.com/guilleiguaran/sass-rails.git
revision: 0393d9e1a6e4a776e61f16fb75f97971b9f4abd2
branch: backport
specs:
sass-rails (4.0.1)
railties (>= 3.2.0, < 5.0)
sass (>= 3.2.0)
sprockets-rails (~> 2.0.0.backport1)
GIT
remote: https://github.com/spacemunkay/acts_as_loggable.git
revision: 8484dc1f7a58699705c5ffb593ebdf700a9f374a
@@ -19,12 +9,12 @@ GIT
GEM
remote: https://rubygems.org/
specs:
actionmailer (3.2.19)
actionpack (= 3.2.19)
mail (~> 2.5.4)
actionpack (3.2.19)
activemodel (= 3.2.19)
activesupport (= 3.2.19)
actionmailer (3.2.13)
actionpack (= 3.2.13)
mail (~> 2.5.3)
actionpack (3.2.13)
activemodel (= 3.2.13)
activesupport (= 3.2.13)
builder (~> 3.0.0)
erubis (~> 2.7.0)
journey (~> 1.0.4)
@@ -32,27 +22,25 @@ GEM
rack-cache (~> 1.2)
rack-test (~> 0.6.1)
sprockets (~> 2.2.1)
activemodel (3.2.19)
activesupport (= 3.2.19)
activemodel (3.2.13)
activesupport (= 3.2.13)
builder (~> 3.0.0)
activerecord (3.2.19)
activemodel (= 3.2.19)
activesupport (= 3.2.19)
activerecord (3.2.13)
activemodel (= 3.2.13)
activesupport (= 3.2.13)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activeresource (3.2.19)
activemodel (= 3.2.19)
activesupport (= 3.2.19)
activesupport (3.2.19)
i18n (~> 0.6, >= 0.6.4)
activeresource (3.2.13)
activemodel (= 3.2.13)
activesupport (= 3.2.13)
activesupport (3.2.13)
i18n (= 0.6.1)
multi_json (~> 1.0)
addressable (2.3.6)
addressable (2.4.0)
arel (3.0.3)
bcrypt (3.1.7)
bcrypt (3.1.10)
bcrypt-ruby (3.1.5)
bcrypt (>= 3.1.3)
bootstrap-sass (3.2.0.1)
sass (~> 3.2)
bootstrap-will_paginate (0.0.10)
will_paginate
builder (3.0.4)
@@ -63,19 +51,18 @@ GEM
rack (>= 1.0.0)
rack-test (>= 0.5.4)
xpath (~> 2.0)
celluloid (0.15.2)
timers (~> 1.1.0)
childprocess (0.5.3)
childprocess (0.5.9)
ffi (~> 1.0, >= 1.0.11)
cliver (0.3.2)
coderay (1.1.0)
coderay (1.1.1)
coffee-rails (3.2.2)
coffee-script (>= 2.2.0)
railties (~> 3.2.0)
coffee-script (2.3.0)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.7.1)
coffee-script-source (1.10.0)
commonjs (0.2.7)
database_cleaner (1.2.0)
decent_exposure (1.0.2)
devise (2.0.6)
@@ -85,70 +72,81 @@ GEM
warden (~> 1.1.1)
diff-lcs (1.2.5)
erubis (2.7.0)
execjs (2.2.1)
factory_girl (4.4.0)
activesupport (>= 3.0.0)
factory_girl_rails (4.4.1)
factory_girl (~> 4.4.0)
execjs (2.6.0)
factory_girl (2.6.4)
activesupport (>= 2.3.9)
factory_girl_rails (1.7.0)
factory_girl (~> 2.6.0)
railties (>= 3.0.0)
faker (1.4.3)
faker (1.2.0)
i18n (~> 0.5)
ffi (1.9.3)
ffi (1.9.10)
formatador (0.2.5)
guard (2.6.1)
guard (2.13.0)
formatador (>= 0.2.4)
listen (~> 2.7)
listen (>= 2.7, <= 4.0)
lumberjack (~> 1.0)
nenv (~> 0.1)
notiffany (~> 0.0)
pry (>= 0.9.12)
shellany (~> 0.0)
thor (>= 0.18.1)
guard-rspec (4.3.1)
guard-rspec (4.2.10)
guard (~> 2.1)
rspec (>= 2.14, < 4.0)
guard-spork (1.5.1)
childprocess (>= 0.2.3)
guard (>= 1.1)
spork (>= 0.8.4)
haml (4.0.5)
tilt
haml-rails (0.4)
haml (3.1.8)
haml-rails (0.3.5)
actionpack (>= 3.1, < 4.1)
activesupport (>= 3.1, < 4.1)
haml (>= 3.1, < 4.1)
haml (~> 3.1)
railties (>= 3.1, < 4.1)
hike (1.2.3)
i18n (0.6.11)
jbuilder (2.1.3)
i18n (0.6.1)
jbuilder (2.0.8)
activesupport (>= 3.0.0, < 5)
multi_json (~> 1.2)
journey (1.0.4)
jquery-rails (3.1.1)
jquery-rails (2.3.0)
railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0)
json (1.8.1)
launchy (2.4.2)
json (1.8.3)
launchy (2.4.3)
addressable (~> 2.3)
listen (2.7.9)
celluloid (>= 0.15.2)
less (2.2.2)
commonjs (~> 0.2.6)
less-rails (2.2.6)
actionpack (>= 3.1)
less (~> 2.2.0)
libv8 (3.3.10.4)
listen (3.0.6)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9)
lumberjack (1.0.9)
rb-inotify (>= 0.9.7)
lumberjack (1.0.10)
mail (2.5.4)
mime-types (~> 1.16)
treetop (~> 1.4.8)
method_source (0.8.2)
mime-types (1.25.1)
mini_portile (0.6.0)
multi_json (1.10.1)
netzke-basepack (0.10.1)
netzke-core (~> 0.10.0)
mini_portile2 (2.0.0)
multi_json (1.11.2)
nenv (0.3.0)
netzke-basepack (0.8.4)
netzke-core (~> 0.8.2)
netzke-cancan (0.8.2)
cancan
netzke-core
netzke-core (0.10.1)
netzke-core (0.8.4)
execjs
uglifier
nokogiri (1.6.3.1)
mini_portile (= 0.6.0)
nokogiri (1.6.7.2)
mini_portile2 (~> 2.0.0.rc2)
notiffany (0.0.8)
nenv (~> 0.1)
shellany (~> 0.0)
orm_adapter (0.0.7)
pg (0.17.1)
poltergeist (1.5.1)
@@ -157,83 +155,84 @@ GEM
multi_json (~> 1.0)
websocket-driver (>= 0.2.0)
polyglot (0.3.5)
pry (0.10.1)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
pry (0.9.12.6)
coderay (~> 1.0)
method_source (~> 0.8)
slop (~> 3.4)
rack (1.4.5)
rack-cache (1.2)
rack (1.4.7)
rack-cache (1.6.1)
rack (>= 0.4)
rack-ssl (1.3.4)
rack
rack-test (0.6.2)
rack-test (0.6.3)
rack (>= 1.0)
rails (3.2.19)
actionmailer (= 3.2.19)
actionpack (= 3.2.19)
activerecord (= 3.2.19)
activeresource (= 3.2.19)
activesupport (= 3.2.19)
rails (3.2.13)
actionmailer (= 3.2.13)
actionpack (= 3.2.13)
activerecord (= 3.2.13)
activeresource (= 3.2.13)
activesupport (= 3.2.13)
bundler (~> 1.0)
railties (= 3.2.19)
railties (3.2.19)
actionpack (= 3.2.19)
activesupport (= 3.2.19)
railties (= 3.2.13)
railties (3.2.13)
actionpack (= 3.2.13)
activesupport (= 3.2.13)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (>= 0.14.6, < 2.0)
rake (10.3.2)
rb-fsevent (0.9.4)
rb-inotify (0.9.5)
rake (10.5.0)
rb-fsevent (0.9.7)
rb-inotify (0.9.7)
ffi (>= 0.5.0)
rdoc (3.12.2)
json (~> 1.4)
rspec (3.0.0)
rspec-core (~> 3.0.0)
rspec-expectations (~> 3.0.0)
rspec-mocks (~> 3.0.0)
rspec-core (3.0.4)
rspec-support (~> 3.0.0)
rspec-expectations (3.0.4)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.0.0)
rspec-mocks (3.0.4)
rspec-support (~> 3.0.0)
rspec-rails (3.0.2)
rspec (2.14.1)
rspec-core (~> 2.14.0)
rspec-expectations (~> 2.14.0)
rspec-mocks (~> 2.14.0)
rspec-core (2.14.8)
rspec-expectations (2.14.5)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.14.6)
rspec-rails (2.14.2)
actionpack (>= 3.0)
activemodel (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-core (~> 3.0.0)
rspec-expectations (~> 3.0.0)
rspec-mocks (~> 3.0.0)
rspec-support (~> 3.0.0)
rspec-support (3.0.4)
sass (3.4.0)
rspec-core (~> 2.14.0)
rspec-expectations (~> 2.14.0)
rspec-mocks (~> 2.14.0)
shellany (0.0.1)
shoulda-matchers (1.0.0)
slop (3.6.0)
spork (0.9.2)
sprockets (2.2.2.backport2)
sprockets (2.2.3)
hike (~> 1.2)
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
sprockets-rails (2.0.0.backport1)
actionpack (>= 3.0)
activesupport (>= 3.0)
sprockets (~> 2.2.2.backport2)
therubyracer (0.10.2)
libv8 (~> 3.3.10)
thor (0.19.1)
tilt (1.4.1)
timers (1.1.0)
treetop (1.4.15)
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.41)
uglifier (2.5.3)
twitter-bootstrap-rails (2.0.9)
actionpack (>= 3.1)
less-rails (~> 2.2.2)
railties (>= 3.1)
therubyracer (~> 0.10.1)
tzinfo (0.3.46)
uglifier (2.7.2)
execjs (>= 0.3.0)
json (>= 1.8.0)
warden (1.1.1)
rack (>= 1.0)
websocket-driver (0.3.4)
websocket-driver (0.6.3)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
will_paginate (3.0.7)
xpath (2.0.0)
nokogiri (~> 1.3)
@@ -243,34 +242,31 @@ PLATFORMS
DEPENDENCIES
acts_as_loggable!
bootstrap-sass
bootstrap-will_paginate
bootstrap-will_paginate (~> 0.0.6)
cancan
capybara (~> 2.2.1)
coffee-rails
coffee-rails (~> 3.2.1)
database_cleaner (~> 1.2.0)
decent_exposure (~> 1.0.1)
devise (~> 2.0.4)
factory_girl_rails
faker
guard-rspec
guard-spork
haml-rails
jbuilder
jquery-rails
factory_girl_rails (~> 1.2)
faker (~> 1.2.0)
guard-rspec (~> 4.2.6)
guard-spork (~> 1.5.1)
haml-rails (~> 0.3.4)
jbuilder (~> 2.0.3)
jquery-rails (~> 2.0)
launchy (~> 2.4.2)
netzke-basepack
netzke-basepack (~> 0.8.0)
netzke-cancan
netzke-core
netzke-core (~> 0.8.0)
pg (~> 0.17.1)
poltergeist (~> 1.5.0)
pry
rails (= 3.2.19)
rb-fsevent
rspec-rails
sass-rails!
spork
sprockets (= 2.2.2.backport2)
sprockets-rails (= 2.0.0.backport1)
uglifier
will_paginate
pry (~> 0.9.8)
rails (= 3.2.13)
rspec-rails (~> 2.14.0)
shoulda-matchers (~> 1.0.0)
spork (~> 0.9.2)
twitter-bootstrap-rails (~> 2.0.3)
uglifier (>= 1.0.3)
will_paginate (~> 3.0.3)
+30 -42
View File
@@ -1,60 +1,48 @@
# About
A web application for bicycle collectives to track bicycles, bicycle work history, volunteer hours, volunteer work history, and volunteers currently in the shop.
See an overview video of what it looks like and how it works here: https://www.youtube.com/watch?v=0-JjM6d9nK4.
See a live demo here: <http://bikeshed.wvcompletestreets.org/> Thanks @fspc for setting that up!
## Instructions/Guides
See an overview video of what the desktop view looks like and how it works here: https://www.youtube.com/watch?v=0-JjM6d9nK4.
### Adding a User
To add a user, a user must sign themselves up from the initial login screen. An admin can later log in to grant that user admin privileges afterward, if desired.
## Overview/Instructions/Guides
### Adding a Bike
How to add a bike to the database: https://www.youtube.com/watch?v=1IchWUdMQ90.
## Views
The app has two different views. One for "core" volunteers which I consider admins, and another for "customer" volunteers which I consider users. Because of the Netzke/ExtJS framework I used, the current UI is non-intuitive and I feel like is only adequate for admins who can take the time to learn the system. I'm in the process of designing a simpler mobile friendly UI with the essential functionality that's intended for users. You can see those changes on this [branch]( https://github.com/spacemunkay/BikeShed/tree/mobile-ui)
## Deployment
Currently, at the Velocipede collective we have it running on a computer in the shop on the local network. The reason for this, is so that it's only accessible from within the shop. I'm weighing out different methods for ensuring that users can only log in from within the shop (and not from home), such as a shop password that only admins can see and would make visible from within the physical bike shop. I also don't have to worry about security issues while the app is in development. In the future, I'd like to see the app hosted much like http://freehub.bikekitchen.org/.
## Ideas
At Velocipede, with a mobile friendly UI for users, I'm hoping we can get donations of old, unused smart phones, connected via local wifi, that can be used as stations to log a volunteer's work and hours. They can be secured by gluing cables into the earphone jacks to deter theft if necessary. This might be another way that the collective can promote reuse of materials.
See [guides](doc/guides.md)
# Developer Setup
1. use rvm
1. allow the .rvmrc file
1. `gem install bundler`
1. `bundle`
1. Install Postgres (Mac OSX instructions below)
1. Copy over DB config: `cp config/database.yml.example config/database.yml`
1. Update config with your database (velocipede), user (velocipede), and password.
1. `rake db:create db:migrate`
1. `rake db:seed`
1. Download extJS 4.1 (A version of 4.1 is hosted here: http://my.jasondenney.com/extjs-4.1.1.zip) Latest versions at http://www.sencha.com/products/extjs. Unzip and place where ever you like.
1. Link to your extJS folder path under `public/extjs`: (From app root) `ln -s /MY/PATH/extjs/ public/extjs`
1. `rails s`
## Running with Docker (recommended)
1. These instructions haven't been tested, please provide corrections!
1. Install Docker Toolbox <https://www.docker.com/toolbox>
1. Make sure you have a machine running: `docker-machine start default && eval "$(docker-machine env default)"`
1. Execute `docker-compose build`
1. Execute `docker-compose run web rake db:create db:migrate db:seed`
1. Execute `docker-compose up`
1. If using Docker Toolbox, use `docker-machine ip default` to get the IP where the server is running.
1. Test the Rails server is running with by visiting `<INSERT IP>:8080` in your browser.
# Postgres 9.2 Mac OSX Install
1. Install homebrew `ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"`
1. `brew install postgres`
1. First time db initialization `initdb /usr/local/var/postgres -E utf8`
1. Start Postgres `pg_ctl -D /usr/local/var/postgres -l /usr/local/var/postgres/server.log start`
1. Create your PG user `createuser -d -P velocipede`
1. Create your database `createdb -U velocipede --owner=velocipede velocipede`
1. Create your test database `createdb -U velocipede --owner=velocipede velocipede_test`
### Alternative Dockerfile
# Testing
See <https://github.com/fspc/bikeshed> for an alternative docker setup.
1. Install phantomjs `brew install phantomjs`
1. Run tests with `rspec`
### Developer Workflow
The project directory should already be mounted inside the container, so you should be able to make live changes. However, since the project is running in the 'web' container, you need to prepend commands with `docker-compose run web`.
# Optional
Add icons
You'll likely want to add the following aliases:
```
alias dm='docker-machine'
alias dc='docker-compose'
alias dcrw='docker-compose run web'
```
1. Download icons from http://www.famfamfam.com/lab/icons/silk/
1. Link to the icons under `public/images/icons`: (From app root) `ln -s /MY/PATH/famfamfam_silk_icons/icons public/images/icons`
That way your commands can be shortened to:
```
dcrw rake routes
dcrw rails console
dcrw rspec
```
If there's a better way, I'm all ears. Alternatively you could ssh into the machine with `dcrw bash`.
# License
Velocipede is released under the MIT license (http://opensource.org/licenses/MIT)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

+2 -2
View File
@@ -12,8 +12,8 @@
//
//= require jquery
//= require jquery_ujs
// require twitter/bootstrap/bootstrap-button
// require twitter/bootstrap/bootstrap-modal
//= require twitter/bootstrap/bootstrap-button
//= require twitter/bootstrap/bootstrap-modal
//= require utils
//= require bootstrap-datepicker
//= require bootstrap-timepicker
+1 -1
View File
@@ -1,6 +1,6 @@
//when signed out, or session expires forward to sign in page
Ext.Ajax.on('requestexception', function(conn, response, options) {
if (response.status === 401) { window.location = '/users/sign_in'; }
if (response.status === 401 && response.statusText === "Unauthorized") { window.location = '/users/sign_in'; }
}, this);
//Override default netzke time entry field
+15
View File
@@ -0,0 +1,15 @@
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the top of the
* compiled file, but it's generally better to create a new file per style scope.
*
*= require_self
*= require datepicker
*= require bootstrap-timepicker
*= require bootstrap_and_overrides
*/
@@ -1,99 +0,0 @@
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the top of the
* compiled file, but it's generally better to create a new file per style scope.
*
*= require_self
*= require bootstrap
*= require datepicker
*= require bootstrap-timepicker
*= require bootstrap_and_overrides
*/
@import 'bootstrap-sprockets';
@import 'bootstrap';
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
margin: 0;
}
body {
background-color: #fff;
color: #333;
font-size: 13px;
line-height: 18px;
}
.x-boundlist-item {
white-space: nowrap;
}
.container {
max-width: 940px;
margin: 0 auto;
}
.container img {
display: block;
margin: 0 auto;
}
p {
text-align: center;
padding: 20px 0;
}
a {
text-decoration: none;
}
.check_box {
width: 30px;
height: 30px;
background-color: white;
}
label {
font-size: x-large;
}
/* navigation styling */
.navbar-default {
background-color: #428bca;
border-color: #428bca;
border-radius: 0px;
}
.navbar > .container-fluid .navbar-brand {
margin-left: -15px;
}
.navbar-default .navbar-brand {
color: #ffffff;
}
.navbar-default .navbar-nav > li > a {
color: #ffffff;
list-style: none;
}
.navbar-brand {
float: left;
padding: 15px 15px;
font-size: 18px;
line-height: 20px;
height: 50px;
}
.navbar-default a:hover {
color: #d1b84a !important;
}
+47
View File
@@ -0,0 +1,47 @@
@import "twitter/bootstrap/bootstrap";
body {
padding-top: 60px;
padding-left: 20px;
}
@import "twitter/bootstrap/responsive";
// Set the correct sprite paths
@iconSpritePath: asset-path('twitter/bootstrap/glyphicons-halflings.png');
@iconWhiteSpritePath: asset-path('twitter/bootstrap/glyphicons-halflings-white.png');
// Set the Font Awesome (Font Awesome is default. You can disable by commenting below lines)
// Note: If you use asset_path() here, your compiled boostrap_and_overrides.css will not
// have the proper paths. So for now we use the absolute path.
@fontAwesomeEotPath: '/assets/fontawesome-webfont.eot';
@fontAwesomeWoffPath: '/assets/fontawesome-webfont.woff';
@fontAwesomeTtfPath: '/assets/fontawesome-webfont.ttf';
@fontAwesomeSvgPath: '/assets/fontawesome-webfont.svg';
// Font Awesome
@import "fontawesome";
// Your custom LESS stylesheets goes here
//
// Since bootstrap was imported above you have access to its mixins which
// you may use and inherit here
//
// If you'd like to override bootstrap's own variables, you can do so here as well
// See http://twitter.github.com/bootstrap/less.html for their names and documentation
//
// Example:
// @linkColor: #ff0000;
[data-toggle="buttons-radio"] > .btn > input[type="radio"],
[data-toggle="buttons"] > .btn > input[type="checkbox"] {
display: none;
}
.inline-block {
display: inline-block;
}
.control-group.error .btn-group > .btn {
color: #b94a48;
border-color: #b94a48;
}
-25
View File
@@ -1,25 +0,0 @@
// Set the correct sprite paths
$iconSpritePath: asset-path('twitter/bootstrap/glyphicons-halflings.png');
$iconWhiteSpritePath: asset-path('twitter/bootstrap/glyphicons-halflings-white.png');
// Set the Font Awesome (Font Awesome is default. You can disable by commenting below lines)
// Note: If you use asset_path() here, your compiled boostrap_and_overrides.css will not
// have the proper paths. So for now we use the absolute path.
$fontAwesomeEotPath: '/assets/fontawesome-webfont.eot';
$fontAwesomeWoffPath: '/assets/fontawesome-webfont.woff';
$fontAwesomeTtfPath: '/assets/fontawesome-webfont.ttf';
$fontAwesomeSvgPath: '/assets/fontawesome-webfont.svg';
[data-toggle="buttons-radio"] > .btn > input[type="radio"],
[data-toggle="buttons"] > .btn > input[type="checkbox"] {
display: none;
}
.inline-block {
display: inline-block;
}
.control-group.error .btn-group > .btn {
color: #b94a48;
border-color: #b94a48;
}
@@ -2,6 +2,4 @@ class ApplicationController < ActionController::Base
before_filter :authenticate_user!
protect_from_forgery
layout 'application'
end
@@ -1,10 +1,3 @@
class AuthenticatedController < ApplicationController
before_filter :authenticate_user!
before_filter :get_user_bike
def get_user_bike
@bike = current_user.bike
end
end
+6
View File
@@ -0,0 +1,6 @@
class SiteController < ApplicationController
def index
@bike = current_user.bike
end
end
+2
View File
@@ -1,3 +1,5 @@
%a{ class: "btn btn-default btn-lg", href: root_path}
%span{ class:"icon-home"}
%h2 Add Bike
%p
+2
View File
@@ -2,6 +2,8 @@
%p
%a{class: "btn btn-lg btn-block btn-info", href: new_bike_path} Add Another Bike?
%a{ class: "btn btn-default btn-lg", href: root_path}
%span{ class:"icon-home"}
%h2 #{@bike.shop_id}: #{@bike.bike_brand}
%h2 #{@bike.model}
+2 -2
View File
@@ -6,13 +6,13 @@
<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
<p>
<%= link_to "Sign up", new_registration_path(resource_name), class: "btn btn-block btn-default btn-lg" %><br />
<%= link_to "Sign up", new_registration_path(resource_name), class: "btn btn-block btn-default" %><br />
</p>
<% end -%>
<%- if devise_mapping.recoverable? && controller_name != 'passwords' %>
<p>
<%= link_to "Forgot your password?", new_password_path(resource_name), class: "btn btn-block btn-default btn-lg" %><br />
<%= link_to "Forgot your password?", new_password_path(resource_name), class: "btn btn-block btn-default" %><br />
</p>
<% end -%>
+14 -18
View File
@@ -1,39 +1,36 @@
<%= stylesheet_link_tag "bootstrap", :media => "all" %>
<%= stylesheet_link_tag "application", :media => "all" %>
<div class="container">
<%= image_tag 'velo_logo.png' %>
<p><a href="http://madewithloveinbaltimore.org" class="made">Made with &hearts; in Baltimore</a></p>
<%= stylesheet_link_tag "bootstrap_and_overrides", :media => "all" %>
<h2>Velocipede</h2>
<a href="http://madewithloveinbaltimore.org">Made with &hearts; in Baltimore</a>
<br>
<br>
<%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %>
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<%= f.text_field :username, placeholder: "Username", class: "form-control input-lg", autofocus: true %>
<%= f.text_field :username, placeholder: "Username", class: "form-control input-lg"%>
</div>
<div class="form-group">
<%= f.password_field :password, placeholder: "Password", class: "form-control input-lg" %>
</div>
<div class="form-group remember">
<div class="form-group">
<% if devise_mapping.rememberable? -%>
<label>
<%= f.check_box :remember_me, class: 'check_box' %> Remember Me
<%= f.check_box :remember_me %> Remember Me
</label>
<% end -%>
</div>
</div> <!-- end column -->
</div> <!-- end row -->
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12">
<div class="form-group">
<p>
<%= f.submit "Sign in", class:"btn btn-lg btn-primary col-xs-12 col-sm-12 col-md-12" %>
<%= f.submit "Sign in", class:"btn btn-lg btn-primary" %>
</p>
<p> Or quickly... </p>
<div class="btn-group">
<input id="checkin" name="checkin" type="button" value="CHECK IN" class="btn btn-lg btn-success">
<input id="checkout" name="checkout" type="button" value="CHECK OUT" class="btn btn-lg btn-danger">
</div>
</div>
</div> <!-- end column -->
</div> <!-- end row -->
<% end %>
<%= render "links" %>
@@ -49,4 +46,3 @@
<% end %>
</p>
<% end %>
</div>
+12 -29
View File
@@ -5,44 +5,27 @@
%meta{ name: "viewport", content: "width=device-width", "initial-scale" => "1.0"}
%title= content_for?(:title) ? yield(:title) : "Velocipede"
= csrf_meta_tags
= stylesheet_link_tag "bootstrap", "application", :media => "all"
= stylesheet_link_tag "bootstrap_and_overrides", "datepicker", "bootstrap-timepicker", :media => "all"
/[if lt IE 9]
= javascript_include_tag "http://html5shim.googlecode.com/svn/trunk/html5.js"
:css
body {
padding-top: 5px;
}
.x-boundlist-item {
white-space: nowrap;
}
%body
%nav{class: "navbar navbar-default", role: "navigation"}
%div{class: "container-fluid"}
/[Brand and toggle get grouped for better mobile display]
%div{class: "navbar-header"}
%button{type: "button", class: "navbar-toggle", 'data-toggle' => "collapse", 'data-target' => "#bs-example-navbar-collapse-1"}
%span{class: "sr-only"} Toggle navigation
%span.icon-bar
%span.icon-bar
%span.icon-bar
%a{class:"navbar-brand", href:"/"}
= image_tag 'velo_logo.png'
- if user_signed_in?
/[Collect the nav links, forms, and other content for toggling]
#bs-example-navbar-collapse-1.collapse.navbar-collapse
%ul{class: "nav navbar-nav"}
%li
%a{href: new_time_entry_path} Add Time Entry
%li
%a{href: time_entries_path} View Timesheet
%li
%a{href: new_bike_path} Add Bike
%li
%a{href: bike_path(@bike)} View Your Bike
%ul{class: "nav navbar-nav navbar-right"}
%li
%a{href: destroy_user_session_path } Log Out
.container
.content
- if flash[:notice]
%p{:class => 'notice'}= flash[:notice]
- if flash[:alert]
%p{:class => 'alert'}= flash[:alert]
.row
.span12
= yield
%footer
+21
View File
@@ -0,0 +1,21 @@
= stylesheet_link_tag "bootstrap_and_overrides", :media => "all"
%h2 Dashboard
%p
%p
%a{class: "btn btn-lg btn-block btn-primary", href: new_time_entry_path} Add Time Entry
%p
%a{class: "btn btn-lg btn-block btn-primary", href: time_entries_path} View Timesheet
- if can? :manage, Bike
%p
%a{class: "btn btn-lg btn-block btn-primary", href: new_bike_path} Add Bike
- if !@bike.nil?
%p
%a{class: "btn btn-lg btn-block btn-primary", href: bike_path(@bike)} View Your Bike
%p
%p
%a{class: "btn btn-lg btn-block btn-primary hidden-xs", href: admin_index_path} Desktop View
%p
%input{id: "index_logout", value: "Logout", type: "button", class: "btn btn-lg btn-block btn-danger", "data-url" => destroy_user_session_path }
+2
View File
@@ -1,3 +1,5 @@
%a{ class: "btn btn-default btn-lg", href: root_path}
%span{ class:"icon-home"}
%h2 Task List
%h3
%a{ href: bike_path(@task_list.item)}#{@task_list.item.shop_id} #{@task_list.item.bike_brand}
+2
View File
@@ -1,3 +1,5 @@
%a{ class: "btn btn-default btn-lg", href: root_path}
%span{ class:"icon-home"}
%h2 Your Timesheet
%dl.dl-horizontal
+19 -18
View File
@@ -1,32 +1,33 @@
%a{ class: "btn btn-default btn-lg", href: root_path}
%span{ class:"icon-home"}
%h2 Add Time Entry
%p
.control-group
.controls.bootstrap-timepicker.col-xs-12.col-sm-12.col-md-12.input-group
.span{ class: "input-group-addon", style: "min-width: 100px; font-size: large"} Date
%input{id: "date_id", placeholder: "Date", type: "text", class: "datepicker input-large col-xs-12 col-sm-12 col-md-12 form-control" }
.controls
%input{id: "date_id", placeholder: "Date", type: "text", class: "datepicker input-small" }
.help-block
.control-group
.controls{ class: "bootstrap-timepicker col-xs-12 col-sm-12 col-md-12 input-group"}
.span{ class: "input-group-addon", style: "min-width: 100px; font-size: large"} Start
%input{id: "start_time_id", placeholder: "Time ID", type: "text", class: "input-large col-xs-12 col-sm-12 col-md-12 form-control" }
.controls{ class: "bootstrap-timepicker"}
%label Start
%input{id: "start_time_id", placeholder: "Time ID", type: "text", class: "input-small" }
.hidden{ id: "start_date" }
.help-block
.control-group
.controls{ class: "bootstrap-timepicker col-xs-12 col-sm-12 col-md-12 input-group"}
.span{ class: "input-group-addon", style: "min-width: 100px; font-size: large"} End
%input{id: "end_time_id", placeholder: "Time ID", type: "text", class: "input-large col-xs-9 col-sm-9 col-md-9 form-control"}
.controls
%label End
%input{id: "end_time_id", placeholder: "Time ID", type: "text", class: "input-small" }
.hidden{ id: "end_date" }
.help-block
.control-group{ class: "col-xs-12 col-sm-12 col-md-12"}
.control-group
.controls
.btn-group{ "data-toggle" => "buttons-radio", class: "btn-group btn-group-justified"}
%label{ class: "btn-lg col-xs-4 col-sm-4 col-md-4"}
%input{ type: "radio", name: "action_id", value: 1, radius: 20} Volunteer
%label{ class: "btn-lg col-xs-4 col-sm-4 col-md-4"}
%input{ type: "radio", name: "action_id", value: 2, radius: 20} Personal
%label{ class: "btn-lg col-xs-4 col-sm-4 col-md-4"}
%input{ type: "radio", name: "action_id", value: 3, radius: 20} Staff
.btn-group{ "data-toggle" => "buttons-radio"}
%label{ class: "btn btn-default"}
%input{ type: "radio", name: "action_id", value: 1} Volunteer
%label{ class: "btn btn-default"}
%input{ type: "radio", name: "action_id", value: 2} Personal
%label{ class: "btn btn-default"}
%input{ type: "radio", name: "action_id", value: 3} Staff
.hidden{ id: "log_action_id" }
.help-block
.control-group
@@ -35,7 +36,7 @@
= select_tag(:bike_id, options_for_select(@bikes))
.control-group
.controls
%textarea{id: "description_id", placeholder: "Work description", class: "input-lg col-xs-12 col-sm-12 col-md-12" }
%textarea{id: "description_id", placeholder: "Work description", class: "input-lg" }
.control-group
.controls
%input{id: "add_time_entry_submit", value: "Add Time Entry", type: "button", class: "btn btn-lg btn-block btn-primary", "data-url" => "#{api_create_time_entry_path}", "data-forward" => "#{"."}"}
+4 -1
View File
@@ -3,7 +3,10 @@ require File.expand_path('../boot', __FILE__)
require 'rails/all'
if defined?(Bundler)
Bundler.require(*Rails.groups)
# If you precompile assets before deploying to production, use this line
Bundler.require(*Rails.groups(:assets => %w(development test)))
# If you want your assets lazily compiled in production, use this line
# Bundler.require(:default, :assets, Rails.env)
end
module Velocipede
+10 -14
View File
@@ -1,23 +1,19 @@
development:
development: &default
adapter: postgresql
database: velocipede
username: velocipede
password:
host: 127.0.0.1
encoding: unicode
pool: 5
database: <%= ENV['RDS_DB_NAME'] || 'postgres' %>
username: <%= ENV['RDS_USERNAME'] || 'postgres' %>
password: <%= ENV['RDS_PASSWORD'] %>
host: <%= ENV['RDS_HOSTNAME'] || 'db' %>
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
adapter: postgresql
<<: *default
database: velocipede_test
username: velocipede
password:
host: 127.0.0.1
production:
adapter: postgresql
database: velocipede
username: velocipede
password:
host: 127.0.0.1
<<: *default
database: velocipede_production
+1 -2
View File
@@ -3,8 +3,7 @@ Velocipede::Application.routes.draw do
devise_for :users
netzke
root :to => 'time_entries#new'
root :to => 'site#index'
get 'admin/index', to: 'panel#index', as: "admin_index"
get 'bikes/new', to: 'bikes#new', as: "new_bike"
-1
View File
@@ -1 +0,0 @@
# No API yet
+20
View File
@@ -0,0 +1,20 @@
# Overview
## Guides
### Adding a User
To add a user, a user must sign themselves up from the initial login screen. An admin can later log in to grant that user admin privileges afterward, if desired.
### Adding a Bike
How to add a bike to the database via the desktop view: <https://www.youtube.com/watch?v=1IchWUdMQ90>.
## Views
The app has two different views. One for "core" volunteers which I consider admins, and another for "customer" volunteers which I consider users. Because of the Netzke/ExtJS framework I used, the current UI is non-intuitive and I feel like is only adequate for admins who can take the time to learn the system. I'm in the process of designing a simpler mobile friendly UI with the essential functionality that's intended for users.
## Deployment
Currently, at the Velocipede collective we have it running on a computer in the shop on the local network. The reason for this, is so that it's only accessible from within the shop. I'm weighing out different methods for ensuring that users can only log in from within the shop (and not from home), such as a shop password that only admins can see and would make visible from within the physical bike shop. I also don't have to worry about security issues while the app is in development. In the future, I'd like to see the app hosted much like <http://freehub.bikekitchen.org/>.
## Ideas
At Velocipede, with a mobile friendly UI for users, I'm hoping we can get donations of old, unused smart phones, connected via local wifi, that can be used as stations to log a volunteer's work and hours. They can be secured by gluing cables into the earphone jacks to deter theft if necessary. This might be another way that the collective can promote reuse of materials.
+35
View File
@@ -0,0 +1,35 @@
# Local Developer Setup
1. use rvm
1. allow the .rvmrc file
1. `gem install bundler`
1. `bundle`
1. Install Postgres (Mac OSX instructions below)
1. Copy over DB config: `cp config/database.yml.example config/database.yml`
1. Update config with your database (velocipede), user (velocipede), and password.
1. `rake db:create db:migrate`
1. `rake db:seed`
1. Download extJS 4.1 (A version of 4.1 is hosted here: http://my.jasondenney.com/extjs-4.1.1.zip) Latest versions at http://www.sencha.com/products/extjs. Unzip and place where ever you like.
1. Link to your extJS folder path under `public/extjs`: (From app root) `ln -s /MY/PATH/extjs/ public/extjs`
1. `rails s`
# Postgres 9.2 Mac OSX Install
1. Install homebrew `ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"`
1. `brew install postgres`
1. First time db initialization `initdb /usr/local/var/postgres -E utf8`
1. Start Postgres `pg_ctl -D /usr/local/var/postgres -l /usr/local/var/postgres/server.log start`
1. Create your PG user `createuser -d -P velocipede`
1. Create your database `createdb -U velocipede --owner=velocipede velocipede`
1. Create your test database `createdb -U velocipede --owner=velocipede velocipede_test`
# Testing
1. Install phantomjs `brew install phantomjs`
1. Run tests with `rspec`
# Optional
Add icons
1. Download icons from http://www.famfamfam.com/lab/icons/silk/
1. Link to the icons under `public/images/icons`: (From app root) `ln -s /MY/PATH/famfamfam_silk_icons/icons public/images/icons`
+12
View File
@@ -0,0 +1,12 @@
db:
image: postgres
ports:
- "5432"
web:
volumes:
- .:/usr/src/app
build: .
ports:
- "8080:3000"
links:
- db
+20
View File
@@ -0,0 +1,20 @@
#!/usr/bin/env ruby
require 'fileutils'
extjs_url = "http://my.jasondenney.com/extjs-4.1.1.zip"
download_dir = '/tmp'
dest_dir ='/usr/lib'
extjs_download_path = File.join(download_dir,'extjs-4.1.1.zip')
files = Dir.glob(File.join(dest_dir, 'extjs', '*'))
if files.empty? and Dir.glob(extjs_download_path).empty?
`wget -P #{download_dir} #{extjs_url}`
raise "Failed downloading #{extjs_url}" if Dir.glob(extjs_download_path).empty?
end
if files.empty?
puts `unzip #{extjs_download_path} -d #{dest_dir}/`
FileUtils.mv(File.join(dest_dir, 'ext-4.1.1a'), File.join(dest_dir, 'extjs'))
end
raise "Failed unzipping #{extjs_download_path}" if Dir.glob(File.join(dest_dir,'extjs', '*')).empty?
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env ruby
require 'fileutils'
fam_url = "http://www.famfamfam.com/lab/icons/silk/famfamfam_silk_icons_v013.zip"
download_dir = '/tmp'
dest_dir = "/usr/lib/famfamfam"
fam_download_path = File.join(download_dir,'famfamfam_silk_icons_v013.zip')
files = Dir.glob(File.join(dest_dir, '*'))
if files.empty? and Dir.glob(fam_download_path).empty?
`wget -P #{download_dir} #{fam_url}`
raise "Failed downloading #{fam_url}" if Dir.glob(fam_download_path).empty?
end
if files.empty?
puts `unzip #{fam_download_path} -d #{dest_dir}/`
end
raise "Failed unzipping #{fam_download_path}" if Dir.glob(File.join(dest_dir, '*')).empty?
+109
View File
@@ -0,0 +1,109 @@
namespace :import do
desc "Import a CSV list of bikes. Call via 'rake import:bikes_csv['myFilename.csv']'"
task :bikes_csv, [:filename] => :environment do |t, args|
require 'csv'
# Expected Headers:
# Shop Specific ID Number, Program Type/Purpose, Gone (Is it in the shop?),Date In,Date Out,Price/Value,Make/Brand,Model,for Whom,for Whom Zip Code,Comment
#
# 2,Shop,Yes,Missing,2/17/2008,$90,Univega,Novo Sport Chronology,Darrell Forbes,Missing,,bikes for the World,
@loggable_type = 'Bike'
@log_action_type= 'ActsAsLoggable::BikeAction'
@logger_type = 'User'
@logger_id = User.find_by_username('admin')
@acquired_log_action_id = ActsAsLoggable::BikeAction.find_by_action('ACQUIRED')
@note_log_action_id = ActsAsLoggable::BikeAction.find_by_action('NOTE')
@completed_log_action_id = ActsAsLoggable::BikeAction.find_by_action('COMPLETED')
@sold_log_action_id = ActsAsLoggable::BikeAction.find_by_action('SOLD')
@build_bike_matchers = ["build-a-bike"]
@sale_matchers = ["as-is","shop"]
@parts_matchers = ["parts"]
def create_acquired_log(bike_id, date)
ActsAsLoggable::Log.new(
loggable_type: @loggable_type,
loggable_id: bike_id,
logger_type: @logger_type
logger_id: @logger_id
log_action_type: @log_action_type
log_aciton_id: @acquired_log_aciton_id,
start_date: date,
end_date: date,
)
end
def create_note_log(bike_id, date, note)
ActsAsLoggable::Log.new(
loggable_type: @loggable_type,
loggable_id: bike_id,
logger_type: @logger_type
logger_id: @logger_id
log_action_type: @log_action_type
log_aciton_id: @note_log_action_id,
start_date: date,
end_date: date,
description: note,
)
end
def create_sold_log(bike_id, date)
ActsAsLoggable::Log.new(
loggable_type: @loggable_type,
loggable_id: bike_id,
logger_type: @logger_type
logger_id: @logger_id
log_action_type: @log_action_type
log_aciton_id: @sold_log_action_id,
start_date: date,
end_date: date,
)
end
def create_completed_log(bike_id, date)
ActsAsLoggable::Log.new(
loggable_type: @loggable_type,
loggable_id: bike_id,
logger_type: @logger_type
logger_id: @logger_id
log_action_type: @log_action_type
log_aciton_id: @completed_log_action_id,
start_date: date,
end_date: date,
)
end
def get_bike_purpose_id(purpose)
case purpose.downcase
when *@build_bike_matchers
when *@sale_matchers
when *@parts_matchers
else
end
end
CSV.foreach(args[:filename], headers: true) do | row |
bike_id = row["Shop Specific ID Number"]
bike_purpose_id = get_bike_purpose_id(row["Program Type/Purpose"])
bike_value = get_bike_value(row["Price/Value"])
bike_brand_id = get_bike_brand(row["Make/Brand"])
bike_model = get_bike_model(row["Model"])
bike = Bike.find_or_create_by_shop_id_and_bike_purpose_id_and_value_and_bike_brand_id_and_model(
bike_id,
bike_purpose_id,
bike_value,
bike_brand_id,
bike_model
)
#Create aquired log
#
#Create For whom log
#
#Create note/comment log
#
#Create SOLD/completed log
#
end
end
end
+1
View File
@@ -0,0 +1 @@
/usr/lib/extjs
+1
View File
@@ -0,0 +1 @@
/usr/lib/famfamfam/icons/
+3 -2
View File
@@ -16,7 +16,9 @@ FactoryGirl.define do
value 200
bike_condition { FactoryGirl.create(:bike_condition) }
bike_purpose { FactoryGirl.create(:bike_purpose) }
factory :seed_bike do
end
factory :seed_bike, class: Bike do
sequence(:shop_id) {|n| n}
sequence :serial_number do |n|
"#{Faker::Code.isbn}-#{n}"
@@ -33,4 +35,3 @@ FactoryGirl.define do
bike_purpose_id { BikePurpose.all.sample.id }
end
end
end
+4 -4
View File
@@ -8,14 +8,14 @@ FactoryGirl.define do
first_name 'Michael'
last_name 'Scott'
sequence(:bike_id) { |n| n }
after(:build) do |r|
after_build do |r|
r.roles << (Role.find_by_role("user") || FactoryGirl.create(:role_user))
end
factory :staff do
username "staff"
first_name 'Staff'
after(:build) do |r|
after_build do |r|
r.roles << (Role.find_by_role("staff") || FactoryGirl.create(:role_staff))
end
end
@@ -23,7 +23,7 @@ FactoryGirl.define do
factory :admin do
username "admin"
first_name 'Admin'
after(:build) do |r|
after_build do |r|
r.roles << (Role.find_by_role("admin") || FactoryGirl.create(:role_admin))
end
end
@@ -31,7 +31,7 @@ FactoryGirl.define do
factory :bike_admin do
username "bike_admin"
first_name 'BikeAdmin'
after(:build) do |r|
after_build do |r|
r.roles << (Role.find_by_role("bike_admin") || FactoryGirl.create(:role_bike_admin))
end
end