79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/app/views/projects/_form.html.slim:
--------------------------------------------------------------------------------
1 | = simple_form_for @project do |f|
2 |
3 | = f.input :name, :input_html => { :autofocus => true }
4 | = f.input :description, :input_html => {:class => :span7}
5 | = f.input :contributor_tokens, :label => 'Contributors', \
6 | :input_html => { 'data-pre' => @project.contributors_for_token_input }
7 |
8 | .actions
9 | = f.submit 'Save', :class => 'btn primary'
10 |
11 |
--------------------------------------------------------------------------------
/app/views/projects/edit.html.slim:
--------------------------------------------------------------------------------
1 | .page-header
2 | h1 Admin #{@project.name}
3 |
4 | = render 'form'
5 |
6 | = link_to 'Destroy', project_path(@project), \
7 | :class => 'btn danger', \
8 | :confirm => 'Are you sure?', \
9 | :method => :delete
10 |
11 |
--------------------------------------------------------------------------------
/app/views/projects/new.html.slim:
--------------------------------------------------------------------------------
1 | .page-header
2 | h1 New project
3 |
4 | = render 'form'
5 |
6 |
--------------------------------------------------------------------------------
/app/views/tasks/_form.html.slim:
--------------------------------------------------------------------------------
1 | = simple_form_for [@project, @task] do |f|
2 |
3 | = f.input :title, :input_html => { :autofocus => true, :class => 'span8' }
4 |
5 | = f.input :description, :input_html => { :class => 'span8 monospace', :rows => 10 }, \
6 | :hint => markdown_note('Tasks').html_safe
7 |
8 | = f.input :points, :collection => Task::POINTS
9 |
10 | = f.association :category, :collection => @categories
11 |
12 | = f.input :position, :collection => Board::POSITIONS.values, :prompt => false
13 |
14 | = f.association :contributors, :label => 'Assignees', :include_blank => true, :collection => @project.all_contributors
15 |
16 | .actions
17 | = f.submit 'Save', :class => 'btn primary'
18 |
19 |
--------------------------------------------------------------------------------
/app/views/tasks/edit.html.slim:
--------------------------------------------------------------------------------
1 | .page-header
2 | h1 Editing task
3 |
4 | = render 'form'
5 |
6 | = link_to 'Show', project_task_path, :class => 'btn'
7 |
8 |
--------------------------------------------------------------------------------
/app/views/tasks/index.html.slim:
--------------------------------------------------------------------------------
1 | .page-header
2 | h1 Listing tasks
3 |
4 | table.zebra-striped
5 | tr
6 | th Title
7 | th Points
8 | th Category
9 | th Position
10 | th
11 | th
12 | th
13 |
14 | - @tasks.each do |task|
15 | tr
16 | td= task.title
17 | td= task.points
18 | td= task.category.name if task.category
19 | td= task.position
20 | td= link_to 'Show', project_task_path(@project, task)
21 | td= link_to 'Edit', edit_project_task_path(@project, task)
22 | td= link_to 'Destroy', project_task_path(@project, task), \
23 | :confirm => 'Are you sure?', :method => :delete
24 |
25 |
--------------------------------------------------------------------------------
/app/views/tasks/new.html.slim:
--------------------------------------------------------------------------------
1 | .page-header
2 | h1 New task
3 |
4 | = render 'form'
5 |
6 |
--------------------------------------------------------------------------------
/app/views/tasks/show.html.slim:
--------------------------------------------------------------------------------
1 | .page-header
2 | h1
3 | = @task.title
4 | '
5 | small
6 | ' by #{@task.author.name}
7 | span.task-info-date
8 | = @task.created_at.strftime 'at %d-%m-%Y'
9 |
10 | .task-info-wrapper.clear
11 | .task-info-principal
12 | .task-info-assignees
13 | p
14 | - if !@task.contributors.empty?
15 | ' Assigned to
16 | strong
17 | = @task.contributors.collect(&:name).to_sentence
18 | - else
19 | strong No one is assigned
20 |
21 | .task-info-description
22 | = @task.description.empty? ? 'No description given.' : markdown(@task.description)
23 |
24 | .task-info-side
25 | - if @task.category
26 | p.task-info-category[style="background-color:##{@task.category.color};"] #{@task.category.name}
27 | - else
28 | p.task-info-category[style="background-color:#ffffa5;"] None
29 |
30 | p
31 | - if @task.points
32 | strong
33 | ' #{@task.points}
34 | | point#{ 's' if !@task.points == 1 }
35 | - else
36 | | No punctuated yet
37 |
38 | p
39 | - if @task.position
40 | ' At
41 | strong
42 | ' #{@task.position}
43 | | position
44 | - else
45 | | Out of the board
46 |
47 | .task-actions
48 | = link_to 'Edit', edit_project_task_path(@project, @task), :class => 'btn primary'
49 | = link_to 'Destroy', project_task_path(@project, @task), \
50 | :class => 'btn danger', \
51 | :confirm => 'Are you sure?', \
52 | :method => :delete
53 |
54 | = render '/comments/list'
--------------------------------------------------------------------------------
/autotest/discover.rb:
--------------------------------------------------------------------------------
1 | Autotest.add_discovery { "rails" }
2 | Autotest.add_discovery { "rspec2" }
3 |
--------------------------------------------------------------------------------
/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 KanbanRoots::Application
5 |
--------------------------------------------------------------------------------
/config/application.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../boot', __FILE__)
2 |
3 | require 'rails/all'
4 |
5 | if defined?(Bundler)
6 | Bundler.require *Rails.groups(:assets => %w(development test))
7 | end
8 |
9 | module KanbanRoots
10 | class Application < Rails::Application
11 | # Enable the assets pipeline
12 | config.assets.enabled = true
13 |
14 | # Version of your assets, change if you want to expire all your assets
15 | config.assets.version = '1.0'
16 |
17 | # Settings in config/environments/* take precedence over those specified here.
18 | # Application configuration should go into files in config/initializers
19 | # -- all .rb files in that directory are automatically loaded.
20 |
21 | # Custom directories with classes and modules you want to be autoloadable.
22 | # config.autoload_paths += %W(#{config.root}/extras)
23 |
24 | # Only load the plugins named here, in the order given (default is alphabetical).
25 | # :all can be used as a placeholder for all plugins not explicitly named.
26 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
27 |
28 | # Activate observers that should always be running.
29 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
30 |
31 | # Configure generators values.
32 | config.generators do |g|
33 | g.stylesheets false
34 | g.template_engine :slim
35 | g.test_framework :rspec,
36 | :view_specs => false,
37 | :fixture_replacement => :factory_girl
38 | end
39 |
40 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
41 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
42 | # config.time_zone = 'Central Time (US & Canada)'
43 | config.time_zone = 'Brasilia'
44 |
45 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
46 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
47 | # config.i18n.default_locale = :de
48 |
49 | # JavaScript files you want as :defaults (application.js is always included).
50 | # config.action_view.javascript_expansions[:defaults] = %w(jquery rails)
51 |
52 | # Configure the default encoding used in templates for Ruby 1.9.
53 | config.encoding = "utf-8"
54 |
55 | # Configure sensitive parameters which will be filtered from the log file.
56 | config.filter_parameters += [:password]
57 | end
58 | end
59 |
60 |
--------------------------------------------------------------------------------
/config/boot.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 |
3 | # Set up gems listed in the Gemfile.
4 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
5 |
6 | require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
7 |
--------------------------------------------------------------------------------
/config/cucumber.yml:
--------------------------------------------------------------------------------
1 | <%
2 | rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : ""
3 | rerun_opts = rerun.to_s.strip.empty? ? "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}"
4 | std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} --strict --drb --guess --tags ~@wip"
5 | %>
6 | default: <%= std_opts %> features
7 | wip: --tags @wip:3 --wip features
8 | rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --guess --tags ~@wip
9 |
10 |
--------------------------------------------------------------------------------
/config/database.yml:
--------------------------------------------------------------------------------
1 | # MySQL. Versions 4.1 and 5.0 are recommended.
2 | #
3 | # Install the MySQL driver:
4 | # gem install mysql2
5 | #
6 | # And be sure to use new-style password hashing:
7 | # http://dev.mysql.com/doc/refman/5.0/en/old-client.html
8 | development:
9 | adapter: sqlite3
10 | encoding: utf8
11 | reconnect: false
12 | database: db/development.sqlite3
13 |
14 | # Warning: The database defined as "test" will be erased and
15 | # re-generated from your development database when you run "rake".
16 | # Do not set this db to the same as development or production.
17 | test: &test
18 | adapter: sqlite3
19 | encoding: utf8
20 | reconnect: false
21 | database: db/test.sqlite3
22 |
23 | production:
24 | adapter: mysql2
25 | encoding: utf8
26 | reconnect: false
27 | database: kanban_roots_production
28 | pool: 5
29 | username: root
30 | password: root
31 | socket: /var/run/mysqld/mysqld.sock
32 |
33 | cucumber:
34 | <<: *test
35 |
36 |
--------------------------------------------------------------------------------
/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the rails application
2 | require File.expand_path('../application', __FILE__)
3 |
4 | # Initialize the rails application
5 | KanbanRoots::Application.initialize!
6 |
7 |
--------------------------------------------------------------------------------
/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | KanbanRoots::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 webserver when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Log error messages when you accidentally call methods on nil.
10 | config.whiny_nils = true
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 | # Only use best-standards-support built into browsers
23 | config.action_dispatch.best_standards_support = :builtin
24 |
25 | # Configuration for devise
26 | config.action_mailer.default_url_options = { :host => 'localhost:3000' }
27 |
28 | # Do not compress assets
29 | config.assets.compress = false
30 |
31 | # Expands the lines which load the assets
32 | config.assets.debug = true
33 | end
34 |
35 | silence_warnings do
36 | require 'pry'
37 | IRB = Pry
38 | end
--------------------------------------------------------------------------------
/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | KanbanRoots::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb
3 |
4 | # The production environment is meant for finished, "live" apps.
5 | # Code is not reloaded between requests
6 | config.cache_classes = true
7 |
8 | # Full error reports are disabled and caching is turned on
9 | config.consider_all_requests_local = false
10 | config.action_controller.perform_caching = true
11 |
12 | # Specifies the header that your server uses for sending files
13 | config.action_dispatch.x_sendfile_header = "X-Sendfile"
14 |
15 | # For nginx:
16 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
17 |
18 | # If you have no front-end server that supports something like X-Sendfile,
19 | # just comment this out and Rails will serve the files
20 |
21 | # See everything in the log (default is :info)
22 | # config.log_level = :debug
23 |
24 | # Use a different logger for distributed setups
25 | # config.logger = SyslogLogger.new
26 |
27 | # Use a different cache store in production
28 | # config.cache_store = :mem_cache_store
29 |
30 | # Disable Rails's static asset server
31 | # In production, Apache or nginx will already do this
32 | config.serve_static_assets = false
33 |
34 | # Enable serving of images, stylesheets, and javascripts from an asset server
35 | # config.action_controller.asset_host = "http://assets.example.com"
36 |
37 | # Disable delivery errors, bad email addresses will be ignored
38 | # config.action_mailer.raise_delivery_errors = false
39 |
40 | # Enable threaded mode
41 | # config.threadsafe!
42 |
43 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
44 | # the I18n.default_locale when a translation can not be found)
45 | config.i18n.fallbacks = true
46 |
47 | # Send deprecation notices to registered listeners
48 | config.active_support.deprecation = :notify
49 |
50 | # Compress JavaScript and CSS
51 | config.assets.compress = true
52 |
53 | # Don't fallback to assets pipeline if a precompiled assets is missed
54 | config.assets.compile = false
55 |
56 | # Generate digests for assets URLs
57 | config.assets.digest = true
58 | end
59 |
60 |
--------------------------------------------------------------------------------
/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | KanbanRoots::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 | # Log error messages when you accidentally call methods on nil.
11 | config.whiny_nils = true
12 |
13 | # Show full error reports and disable caching
14 | config.consider_all_requests_local = true
15 | config.action_controller.perform_caching = false
16 |
17 | # Raise exceptions instead of rendering exception templates
18 | config.action_dispatch.show_exceptions = false
19 |
20 | # Disable request forgery protection in test environment
21 | config.action_controller.allow_forgery_protection = false
22 |
23 | # Tell Action Mailer not to deliver emails to the real world.
24 | # The :test delivery method accumulates sent emails in the
25 | # ActionMailer::Base.deliveries array.
26 | config.action_mailer.delivery_method = :test
27 |
28 | # Use SQL instead of Active Record's schema dumper when creating the test database.
29 | # This is necessary if your schema can't be completely dumped by the schema dumper,
30 | # like if you have constraints or database-specific column types
31 | # config.active_record.schema_format = :sql
32 |
33 | # Print deprecation notices to the stderr
34 | config.active_support.deprecation = :stderr
35 |
36 | # Configure static asset server for tests with Cache-Control for performance
37 | config.serve_static_assets = true
38 | config.static_cache_control = "public, max-age=3600"
39 |
40 | # Allow pass debug_assets=true as a query parameter to load pages with unpack
41 | config.assets.allow_debugging = true
42 | end
43 |
44 |
--------------------------------------------------------------------------------
/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/config/initializers/devise.rb:
--------------------------------------------------------------------------------
1 | # Use this hook to configure devise mailer, warden hooks and so forth. The first
2 | # four configuration values can also be set straight in your models.
3 | Devise.setup do |config|
4 | # ==> Mailer Configuration
5 | # Configure the e-mail address which will be shown in DeviseMailer.
6 | config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com"
7 |
8 | # Configure the class responsible to send e-mails.
9 | # config.mailer = "Devise::Mailer"
10 |
11 | # ==> ORM configuration
12 | # Load and configure the ORM. Supports :active_record (default) and
13 | # :mongoid (bson_ext recommended) by default. Other ORMs may be
14 | # available as additional gems.
15 | require 'devise/orm/active_record'
16 |
17 | # ==> Configuration for any authentication mechanism
18 | # Configure which keys are used when authenticating a user. The default is
19 | # just :email. You can configure it to use [:username, :subdomain], so for
20 | # authenticating a user, both parameters are required. Remember that those
21 | # parameters are used only when authenticating and not when retrieving from
22 | # session. If you need permissions, you should implement that in a before filter.
23 | # You can also supply a hash where the value is a boolean determining whether
24 | # or not authentication should be aborted when the value is not present.
25 | config.authentication_keys = [ :login ]
26 |
27 | # Configure parameters from the request object used for authentication. Each entry
28 | # given should be a request method and it will automatically be passed to the
29 | # find_for_authentication method and considered in your model lookup. For instance,
30 | # if you set :request_keys to [:subdomain], :subdomain will be used on authentication.
31 | # The same considerations mentioned for authentication_keys also apply to request_keys.
32 | # config.request_keys = []
33 |
34 | # Configure which authentication keys should be case-insensitive.
35 | # These keys will be downcased upon creating or modifying a user and when used
36 | # to authenticate or find a user. Default is :email.
37 | config.case_insensitive_keys = [ :email ]
38 |
39 | # Configure which authentication keys should have whitespace stripped.
40 | # These keys will have whitespace before and after removed upon creating or
41 | # modifying a user and when used to authenticate or find a user. Default is :email.
42 | config.strip_whitespace_keys = [ :email ]
43 |
44 | # Tell if authentication through request.params is enabled. True by default.
45 | # config.params_authenticatable = true
46 |
47 | # Tell if authentication through HTTP Basic Auth is enabled. False by default.
48 | # config.http_authenticatable = false
49 |
50 | # If http headers should be returned for AJAX requests. True by default.
51 | # config.http_authenticatable_on_xhr = true
52 |
53 | # The realm used in Http Basic Authentication. "Application" by default.
54 | # config.http_authentication_realm = "Application"
55 |
56 | # It will change confirmation, password recovery and other workflows
57 | # to behave the same regardless if the e-mail provided was right or wrong.
58 | # Does not affect registerable.
59 | # config.paranoid = true
60 |
61 | # ==> Configuration for :database_authenticatable
62 | # For bcrypt, this is the cost for hashing the password and defaults to 10. If
63 | # using other encryptors, it sets how many times you want the password re-encrypted.
64 | config.stretches = 10
65 |
66 | # Setup a pepper to generate the encrypted password.
67 | # config.pepper = "1646bc954e173b525a42ccf34ad5941f5447cededcc6856e0d82d47a8de87da954cb53a4847e82e4a1eefe174e8013830699bf0032afa67b3c90bd9432075fb5"
68 |
69 | # ==> Configuration for :confirmable
70 | # The time you want to give your user to confirm his account. During this time
71 | # he will be able to access your application without confirming. Default is 0.days
72 | # When confirm_within is zero, the user won't be able to sign in without confirming.
73 | # You can use this to let your user access some features of your application
74 | # without confirming the account, but blocking it after a certain period
75 | # (ie 2 days).
76 | # config.allow_unconfirmed_access_for = 2.days
77 |
78 | # Defines which key will be used when confirming an account
79 | # config.confirmation_keys = [ :email ]
80 |
81 | # ==> Configuration for :rememberable
82 | # The time the user will be remembered without asking for credentials again.
83 | # config.remember_for = 2.weeks
84 |
85 | # If true, extends the user's remember period when remembered via cookie.
86 | # config.extend_remember_period = false
87 |
88 | # If true, uses the password salt as remember token. This should be turned
89 | # to false if you are not using database authenticatable.
90 | config.use_salt_as_remember_token = true
91 |
92 | # Options to be passed to the created cookie. For instance, you can set
93 | # :secure => true in order to force SSL only cookies.
94 | # config.cookie_options = {}
95 |
96 | # ==> Configuration for :validatable
97 | # Range for password length. Default is 6..128.
98 | # config.password_length = 6..128
99 |
100 | # Regex to use to validate the email address
101 | config.email_regexp = /\A([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})\z/i
102 |
103 | # ==> Configuration for :timeoutable
104 | # The time you want to timeout the user session without activity. After this
105 | # time the user will be asked for credentials again. Default is 30 minutes.
106 | # config.timeout_in = 30.minutes
107 |
108 | # ==> Configuration for :lockable
109 | # Defines which strategy will be used to lock an account.
110 | # :failed_attempts = Locks an account after a number of failed attempts to sign in.
111 | # :none = No lock strategy. You should handle locking by yourself.
112 | # config.lock_strategy = :failed_attempts
113 |
114 | # Defines which key will be used when locking and unlocking an account
115 | # config.unlock_keys = [ :email ]
116 |
117 | # Defines which strategy will be used to unlock an account.
118 | # :email = Sends an unlock link to the user email
119 | # :time = Re-enables login after a certain amount of time (see :unlock_in below)
120 | # :both = Enables both strategies
121 | # :none = No unlock strategy. You should handle unlocking by yourself.
122 | # config.unlock_strategy = :both
123 |
124 | # Number of authentication tries before locking an account if lock_strategy
125 | # is failed attempts.
126 | # config.maximum_attempts = 20
127 |
128 | # Time interval to unlock the account if :time is enabled as unlock_strategy.
129 | # config.unlock_in = 1.hour
130 |
131 | # ==> Configuration for :recoverable
132 | #
133 | # Defines which key will be used when recovering the password for an account
134 | config.reset_password_keys = [ :email ]
135 |
136 | # Time interval you can reset your password with a reset password key.
137 | # Don't put a too small interval or your users won't have the time to
138 | # change their passwords.
139 | config.reset_password_within = 2.hours
140 |
141 | # ==> Configuration for :encryptable
142 | # Allow you to use another encryption algorithm besides bcrypt (default). You can use
143 | # :sha1, :sha512 or encryptors from others authentication tools as :clearance_sha1,
144 | # :authlogic_sha512 (then you should set stretches above to 20 for default behavior)
145 | # and :restful_authentication_sha1 (then you should set stretches to 10, and copy
146 | # REST_AUTH_SITE_KEY to pepper)
147 | # config.encryptor = :sha512
148 |
149 | # ==> Configuration for :token_authenticatable
150 | # Defines name of the authentication token params key
151 | # config.token_authentication_key = :auth_token
152 |
153 | # If uncommented, authentication through token does not store user in session and needs
154 | # to be supplied on each request. Useful if you are using the token as API token.
155 | # config.skip_session_storage << :auth_token
156 |
157 | # ==> Scopes configuration
158 | # Turn scoped views on. Before rendering "sessions/new", it will first check for
159 | # "users/sessions/new". It's turned off by default because it's slower if you
160 | # are using only default views.
161 | # config.scoped_views = false
162 |
163 | # Configure the default scope given to Warden. By default it's the first
164 | # devise role declared in your routes (usually :user).
165 | # config.default_scope = :user
166 |
167 | # Configure sign_out behavior.
168 | # Sign_out action can be scoped (i.e. /users/sign_out affects only :user scope).
169 | # The default is true, which means any logout action will sign out all active scopes.
170 | # config.sign_out_all_scopes = true
171 |
172 | # ==> Navigation configuration
173 | # Lists the formats that should be treated as navigational. Formats like
174 | # :html, should redirect to the sign in page when the user does not have
175 | # access, but formats like :xml or :json, should return 401.
176 | #
177 | # If you have any extra navigational formats, like :iphone or :mobile, you
178 | # should add them to the navigational formats lists.
179 | #
180 | # The :"*/*" and "*/*" formats below is required to match Internet
181 | # Explorer requests.
182 | # config.navigational_formats = [:"*/*", "*/*", :html]
183 |
184 | # The default HTTP method used to sign out a resource. Default is :delete.
185 | config.sign_out_via = :delete
186 |
187 | # ==> OmniAuth
188 | # Add a new OmniAuth provider. Check the wiki for more information on setting
189 | # up on your models and hooks.
190 | # config.omniauth :github, 'APP_ID', 'APP_SECRET', :scope => 'user,public_repo'
191 |
192 | # ==> Warden configuration
193 | # If you want to use other strategies, that are not supported by Devise, or
194 | # change the failure app, you can configure them inside the config.warden block.
195 | #
196 | # config.warden do |manager|
197 | # manager.failure_app = AnotherApp
198 | # manager.intercept_401 = false
199 | # manager.default_strategies(:scope => :user).unshift :some_external_strategy
200 | # end
201 | end
202 |
203 |
--------------------------------------------------------------------------------
/config/initializers/escape_rack.rb:
--------------------------------------------------------------------------------
1 | # from http://openhood.com/rack/ruby/2010/07/15/rack-test-warning/
2 |
3 | module Rack
4 | module Utils
5 | def escape(s)
6 | EscapeUtils.escape_url(s)
7 | end
8 | end
9 | end
10 |
11 |
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format
4 | # (all these examples are active by default):
5 | # ActiveSupport::Inflector.inflections do |inflect|
6 | # inflect.plural /^(ox)$/i, '\1en'
7 | # inflect.singular /^(ox)en/i, '\1'
8 | # inflect.irregular 'person', 'people'
9 | # inflect.uncountable %w( fish sheep )
10 | # end
11 |
--------------------------------------------------------------------------------
/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 | # Mime::Type.register_alias "text/html", :iphone
6 |
--------------------------------------------------------------------------------
/config/initializers/secret_token.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Your secret key for verifying the integrity of signed cookies.
4 | # If you change this key, all old signed cookies will become invalid!
5 | # Make sure the secret is at least 30 characters and all random,
6 | # no regular words or you'll be exposed to dictionary attacks.
7 | KanbanRoots::Application.config.secret_token = '70845b925167d41f7b45bb0a8d7abe9e7eafc7f5443d6d6cf9bb4411ff0581a4d9da970fb5a718cb3fd1e05515898a9658b1b1da37e5db122b4f4d6e0bdc4b9f'
8 |
9 |
--------------------------------------------------------------------------------
/config/initializers/session_store.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | KanbanRoots::Application.config.session_store :cookie_store, :key => '_kanban_roots_session'
4 |
5 | # Use the database for sessions instead of the cookie-based default,
6 | # which shouldn't be used to store highly confidential information
7 | # (create the session table with "rails generate session_migration")
8 | # KanbanRoots::Application.config.session_store :active_record_store
9 |
--------------------------------------------------------------------------------
/config/initializers/simple_form.rb:
--------------------------------------------------------------------------------
1 | # Use this setup block to configure all options available in SimpleForm.
2 | SimpleForm.setup do |config|
3 | # Components used by the form builder to generate a complete input. You can remove
4 | # any of them, change the order, or even add your own components to the stack.
5 | # config.components = [ :placeholder, :label_input, :hint, :error ]
6 |
7 | # Default tag used on hints.
8 | # config.hint_tag = :span
9 |
10 | # CSS class to add to all hint tags.
11 | config.hint_class = 'help-block'
12 |
13 | # CSS class used on errors.
14 | config.error_class = 'help-inline'
15 |
16 | # Default tag used on errors.
17 | # config.error_tag = :span
18 |
19 | # Method used to tidy up errors.
20 | # config.error_method = :first
21 |
22 | # Default tag used for error notification helper.
23 | # config.error_notification_tag = :p
24 |
25 | # CSS class to add for error notification helper.
26 | # config.error_notification_class = :error_notification
27 |
28 | # ID to add for error notification helper.
29 | # config.error_notification_id = nil
30 |
31 | # You can wrap all inputs in a pre-defined tag.
32 | # config.wrapper_tag = :div
33 |
34 | # CSS class to add to all wrapper tags.
35 | config.wrapper_class = :clearfix
36 |
37 | # CSS class to add to the wrapper if the field has errors.
38 | config.wrapper_error_class = :error
39 |
40 | # You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
41 | # config.collection_wrapper_tag = nil
42 |
43 | # You can wrap each item in a collection of radio/check boxes with a tag, defaulting to span.
44 | # config.item_wrapper_tag = :span
45 |
46 | # Series of attempts to detect a default label method for collection.
47 | # config.collection_label_methods = [ :to_label, :name, :title, :to_s ]
48 |
49 | # Series of attempts to detect a default value method for collection.
50 | # config.collection_value_methods = [ :id, :to_s ]
51 |
52 | # How the label text should be generated altogether with the required text.
53 | # config.label_text = lambda { |label, required| "#{required} #{label}" }
54 |
55 | # You can define the class to use on all labels. Default is nil.
56 | # config.label_class = nil
57 |
58 | # You can define the class to use on all forms. Default is simple_form.
59 | # config.form_class = :simple_form
60 |
61 | # Whether attributes are required by default (or not). Default is true.
62 | # config.required_by_default = true
63 |
64 | # Tell browsers whether to use default HTML5 validations (novalidate option).
65 | # Default is enabled.
66 | config.browser_validations = false
67 |
68 | # Determines whether HTML5 types (:email, :url, :search, :tel) and attributes
69 | # (e.g. required) are used or not. True by default.
70 | # Having this on in non-HTML5 compliant sites can cause odd behavior in
71 | # HTML5-aware browsers such as Chrome.
72 | # config.html5 = true
73 |
74 | # Custom mappings for input types. This should be a hash containing a regexp
75 | # to match as key, and the input type that will be used when the field name
76 | # matches the regexp as value.
77 | # config.input_mappings = { /count/ => :integer }
78 |
79 | # Collection of methods to detect if a file type was given.
80 | # config.file_methods = [ :mounted_as, :file?, :public_filename ]
81 |
82 | # Default priority for time_zone inputs.
83 | # config.time_zone_priority = nil
84 |
85 | # Default priority for country inputs.
86 | # config.country_priority = nil
87 |
88 | # Default size for text inputs.
89 | # config.default_input_size = 50
90 |
91 | # When false, do not use translations for labels, hints or placeholders.
92 | # config.translate = true
93 | end
94 |
95 |
--------------------------------------------------------------------------------
/config/locales/devise.en.yml:
--------------------------------------------------------------------------------
1 | # Additional translations at https://github.com/plataformatec/devise/wiki/I18n
2 |
3 | en:
4 | errors:
5 | messages:
6 | expired: "has expired, please request a new one"
7 | not_found: "not found"
8 | already_confirmed: "was already confirmed, please try signing in"
9 | not_locked: "was not locked"
10 | not_saved:
11 | one: "1 error prohibited this %{resource} from being saved:"
12 | other: "%{count} errors prohibited this %{resource} from being saved:"
13 |
14 | devise:
15 | failure:
16 | already_authenticated: 'You are already signed in.'
17 | unauthenticated: 'You need to sign in or sign up before continuing.'
18 | unconfirmed: 'You have to confirm your account before continuing.'
19 | locked: 'Your account is locked.'
20 | invalid: 'Invalid email or password.'
21 | invalid_token: 'Invalid authentication token.'
22 | timeout: 'Your session expired, please sign in again to continue.'
23 | inactive: 'Your account was not activated yet.'
24 | sessions:
25 | signed_in: 'Signed in successfully.'
26 | signed_out: 'Signed out successfully.'
27 | passwords:
28 | send_instructions: 'You will receive an email with instructions about how to reset your password in a few minutes.'
29 | updated: 'Your password was changed successfully. You are now signed in.'
30 | updated_not_active: 'Your password was changed successfully.'
31 | send_paranoid_instructions: "If your e-mail exists on our database, you will receive a password recovery link on your e-mail"
32 | confirmations:
33 | send_instructions: 'You will receive an email with instructions about how to confirm your account in a few minutes.'
34 | send_paranoid_instructions: 'If your e-mail exists on our database, you will receive an email with instructions about how to confirm your account in a few minutes.'
35 | confirmed: 'Your account was successfully confirmed. You are now signed in.'
36 | registrations:
37 | signed_up: 'Welcome! You have signed up successfully.'
38 | signed_up_but_unconfirmed: 'A message with a confirmation link has been sent to your email address. Please open the link to activate your account.'
39 | signed_up_but_inactive: 'You have signed up successfully. However, we could not sign you in because your account is not yet activated.'
40 | signed_up_but_locked: 'You have signed up successfully. However, we could not sign you in because your account is locked.'
41 | updated: 'You updated your account successfully.'
42 | update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and click on the confirm link to finalize confirming your new email address."
43 | destroyed: 'Bye! Your account was successfully cancelled. We hope to see you again soon.'
44 | unlocks:
45 | send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.'
46 | unlocked: 'Your account has been unlocked successfully. Please sign in to continue.'
47 | send_paranoid_instructions: 'If your account exists, you will receive an email with instructions about how to unlock it in a few minutes.'
48 | omniauth_callbacks:
49 | success: 'Successfully authorized from %{kind} account.'
50 | failure: 'Could not authorize you from %{kind} because "%{reason}".'
51 | mailer:
52 | confirmation_instructions:
53 | subject: 'Confirmation instructions'
54 | reset_password_instructions:
55 | subject: 'Reset password instructions'
56 | unlock_instructions:
57 | subject: 'Unlock Instructions'
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Sample localization file for English. Add more files in this directory for other locales.
2 | # See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3 |
4 | en:
5 | hello: "Hello world"
6 |
--------------------------------------------------------------------------------
/config/locales/simple_form.en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | simple_form:
3 | "yes": 'Yes'
4 | "no": 'No'
5 | required:
6 | text: 'required'
7 | mark: '*'
8 | # You can uncomment the line below if you need to overwrite the whole required html.
9 | # When using html, text and mark won't be used.
10 | # html: '*'
11 | error_notification:
12 | default_message: "Some errors were found, please take a look:"
13 | # Labels and hints examples
14 | # labels:
15 | # password: 'Password'
16 | # user:
17 | # new:
18 | # email: 'E-mail para efetuar o sign in.'
19 | # edit:
20 | # email: 'E-mail.'
21 | # hints:
22 | # username: 'User name to sign in.'
23 | # password: 'No special characters, please.'
24 |
25 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | KanbanRoots::Application.routes.draw do
2 |
3 | devise_for :contributors
4 |
5 | get 'contributors' => 'contributors#index'
6 |
7 | resources :projects, :path_names => { :edit => 'admin' }, :except => [:index, :show] do
8 | resources :tasks, :path_names => { :edit => 'edit' } do
9 | resources :comments, :except => [:show, :new]
10 | end
11 | resources :categories, :path_names => { :edit => 'edit' }, :except => :show
12 | end
13 |
14 | match 'projects/:project_id/board' => 'boards#show', :as => :project_board
15 | match 'projects/:project_id/clean_up_done' => 'boards#clean_up_done', :as => :clean_up_done
16 |
17 | match 'board/update_position' => 'boards#update_position'
18 | match 'board/update_points' => 'boards#update_points'
19 | match 'board/update_assignees' => 'boards#update_assignees'
20 |
21 | root :to => 'contributors#dashboard'
22 |
23 | end
24 |
25 |
--------------------------------------------------------------------------------
/db/migrate/20100921004233_create_projects.rb:
--------------------------------------------------------------------------------
1 | class CreateProjects < ActiveRecord::Migration
2 | def self.up
3 | create_table :projects do |t|
4 | t.string :name
5 | t.integer :owner_id
6 | t.string :description
7 |
8 | t.timestamps
9 | end
10 | end
11 |
12 | def self.down
13 | drop_table :projects
14 | end
15 | end
16 |
17 |
--------------------------------------------------------------------------------
/db/migrate/20100921012401_create_contributors.rb:
--------------------------------------------------------------------------------
1 | class CreateContributors < ActiveRecord::Migration
2 | def self.up
3 | create_table(:contributors) do |t|
4 | t.string :name
5 | t.string :username
6 |
7 | ## Devise: Database authenticatable
8 | t.string :email, :null => false, :default => ""
9 | t.string :encrypted_password, :null => false, :default => ""
10 |
11 | ## Devise: Recoverable
12 | t.string :reset_password_token
13 | t.datetime :reset_password_sent_at
14 |
15 | ## Devise: Rememberable
16 | t.datetime :remember_created_at
17 |
18 | t.timestamps
19 | end
20 |
21 | add_index :contributors, :email, :unique => true
22 | add_index :contributors, :reset_password_token, :unique => true
23 | end
24 |
25 | def self.down
26 | drop_table :contributors
27 | end
28 | end
29 |
30 |
--------------------------------------------------------------------------------
/db/migrate/20100925143455_contributors_tasks.rb:
--------------------------------------------------------------------------------
1 | class ContributorsTasks < ActiveRecord::Migration
2 | def self.up
3 | create_table :contributors_tasks, :id => false do |t|
4 | t.references :contributor, :task
5 | end
6 | end
7 |
8 | def self.down
9 | drop_table :contributors_tasks
10 | end
11 | end
12 |
13 |
--------------------------------------------------------------------------------
/db/migrate/20101031223531_create_tasks.rb:
--------------------------------------------------------------------------------
1 | class CreateTasks < ActiveRecord::Migration
2 | def self.up
3 | create_table :tasks do |t|
4 | t.string :title
5 | t.text :description
6 | t.integer :points
7 | t.string :position, :default => 'Backlog'
8 | t.references :category
9 | t.references :project
10 |
11 | t.timestamps
12 | end
13 | end
14 |
15 | def self.down
16 | drop_table :tasks
17 | end
18 | end
19 |
20 |
--------------------------------------------------------------------------------
/db/migrate/20110106023051_create_comments.rb:
--------------------------------------------------------------------------------
1 | class CreateComments < ActiveRecord::Migration
2 | def self.up
3 | create_table :comments do |t|
4 | t.text :content
5 | t.references :task
6 | t.references :contributor
7 |
8 | t.timestamps
9 | end
10 | end
11 |
12 | def self.down
13 | drop_table :comments
14 | end
15 | end
16 |
17 |
--------------------------------------------------------------------------------
/db/migrate/20110128222606_create_categories.rb:
--------------------------------------------------------------------------------
1 | class CreateCategories < ActiveRecord::Migration
2 | def self.up
3 | create_table :categories do |t|
4 | t.string :name
5 | t.string :color
6 | t.references :project
7 |
8 | t.timestamps
9 | end
10 | end
11 |
12 | def self.down
13 | drop_table :categories
14 | end
15 | end
16 |
17 |
--------------------------------------------------------------------------------
/db/migrate/20110421121043_contributors_projects.rb:
--------------------------------------------------------------------------------
1 | class ContributorsProjects < ActiveRecord::Migration
2 | def self.up
3 | create_table :contributors_projects, :id => false do |t|
4 | t.references :contributor, :project
5 | end
6 | end
7 |
8 | def self.down
9 | drop_table :contributors_projects
10 | end
11 | end
12 |
13 |
--------------------------------------------------------------------------------
/db/migrate/20110821185450_add_author_id_to_task.rb:
--------------------------------------------------------------------------------
1 | class AddAuthorIdToTask < ActiveRecord::Migration
2 | def self.up
3 | add_column :tasks, :author_id, :integer
4 | end
5 |
6 | def self.down
7 | remove_column :tasks, :author_id
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/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 => 'Daley', :city => cities.first)
8 |
9 | Project.delete_all
10 | Contributor.delete_all
11 |
12 | Contributor.create!(
13 | :name => 'John Doe',
14 | :username => 'johndoe',
15 | :email => 'johndoe@example.com',
16 | :password => '123456',
17 | :password_confirmation => '123456'
18 | )
19 |
20 | Project.create!(
21 | :name => 'kanban-roots',
22 | :description => 'A kanban board that keeps the simplicity as well as the roots of the concept.',
23 | :owner_id => Contributor.all.first.id
24 | )
25 |
26 |
--------------------------------------------------------------------------------
/direction.md:
--------------------------------------------------------------------------------
1 | # Kanban-roots direction
2 |
3 | ## __Goal__
4 |
5 | __Keep as simple as possible__
6 |
7 | ## Features
8 |
9 | * The backlog division can optionally appear or not on the board, and should
10 | have a separated view too.
11 |
12 | * Use drag-and-drop to move the tasks between the positions.
13 |
14 | * When a tasks is created, all contributors of the project are informed, besides
15 | who create.
16 |
17 | * When the position of a tasks is modified, all contributors of the project are
18 | informed, besides who move.
19 |
20 | * The contributor choose at his options if want to receive emails or not
21 |
22 | * A simple burn-down chart, Pair chart, Interrupt count chart.
23 | [Reference](http://blog.caelum.com.br/pensando-em-metricas-para-times-ageis)
24 |
25 | * Interact with Github
26 |
--------------------------------------------------------------------------------
/doc/README_FOR_APP:
--------------------------------------------------------------------------------
1 | Use this README file to introduce your application and point to useful places in the API for learning more.
2 | Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries.
3 |
--------------------------------------------------------------------------------
/features/board.feature:
--------------------------------------------------------------------------------
1 | Feature: Use the board
2 | As a user
3 | I want use the board
4 | In order see, move and manipulate the taks of my project
5 |
6 | Scenario: Clean up Done tasks
7 | Given I am an authenticated contributor
8 | And I have a project
9 | And the following tasks:
10 | | title | position |
11 | | task 1 | Doing |
12 | | task 2 | Done |
13 | | task 3 | Done |
14 | | task 4 | Done |
15 | When I am on the projects board page
16 | And I follow "Clean up Done"
17 | Then I should see "Done division was cleaned up."
18 | And the Done division should be cleaned
19 |
20 | Scenario: See the category legend on the board, ordered by name
21 | Given I am an authenticated contributor
22 | And I have a project
23 | And the following categories:
24 | | name | color |
25 | | Feature | ffffff |
26 | | Refactoring | fff000 |
27 | | Bug | 000fff |
28 | When I am on the projects board page
29 | Then I should see "Legend:NoneBugFeatureRefactoring"
30 |
31 | @javascript
32 | Scenario: Drag and drop a task to another board position
33 | Given I am an authenticated contributor
34 | And I have a project
35 | And the following tasks:
36 | | title | position |
37 | | task 1 | Doing |
38 | | task 2 | Done |
39 | And I am on the projects board page
40 | When I drag "task 1" task to "Done" position
41 | Then I should see "task 1" task at "Done" position
42 |
43 |
44 | Scenario: Positions shows the points at their titles
45 | Given I am an authenticated contributor
46 | And I have a project
47 | And the following tasks:
48 | | title | position | points |
49 | | task 1 | To Do | 5 |
50 | | task 2 | Doing | 3 |
51 | | task 3 | Done | 4 |
52 | And I am on the projects board page
53 | Then I should see "To Do (5)"
54 | And I should see "Doing (3)"
55 | And I should see "Done (4)"
56 |
57 |
58 | @javascript
59 | Scenario: Changing tasks from a position to another updates the position points
60 | Given I am an authenticated contributor
61 | And I have a project
62 | And the following tasks:
63 | | title | position | points |
64 | | task 1 | Backlog | 5 |
65 | | task 2 | Backlog | 3 |
66 | And I am on the projects board page
67 | When I drag "task 1" task to "To Do" position
68 | Then I should see "To Do (5)"
69 | When I drag "task 2" task to "To Do" position
70 | Then I should see "To Do (8)"
71 | When I drag "task 2" task to "Doing" position
72 | Then I should see "To Do (5)"
73 | And I should see "Doing (3)"
74 | When I drag "task 2" task to "Done" position
75 | Then I should see "Doing (0)"
76 | And I should see "Done (3)"
77 |
78 |
--------------------------------------------------------------------------------
/features/comments.feature:
--------------------------------------------------------------------------------
1 | Feature: Manipulate comments in tasks
2 | As a user
3 | I want manipulate comments in tasks
4 | In order to help other assignees in their job
5 |
6 | Scenario: Create a comment in a task
7 | Given I am a contributor of "sgtran" project
8 | And I am authenticated
9 | And I have a task of "sgtran" project
10 | When I am on the task page
11 | And I fill in "comment_content" with "Some content here"
12 | And I press "Comment"
13 | Then I should be on the task page
14 | And I should see "Some content here"
15 | And I should see "Comment was successfully created."
16 | And the comment should belongs to the task
17 | And I am the coment's author
18 |
19 | Scenario: Edit a comment
20 | Given I am a contributor of "sgtran" project
21 | And I am authenticated
22 | And I have a task of "sgtran" project
23 | And I write a comment for this task
24 | When I am on the task page
25 | And I follow "Edit" within "div.comment-wrapper"
26 | And I fill in "Add comment" with "I'm editing this content"
27 | And I press "Comment"
28 | Then I should be on the task page
29 | And I should see "I'm editing this content"
30 | And I should see "Comment was successfully updated."
31 |
32 | Scenario: Edit only my comment
33 | Given I am a contributor of "sgtran" project
34 | And I am authenticated
35 | And I have a task of "sgtran" project
36 | And there is a comment for this task
37 | When I go to the edit comment page
38 | Then I should be on the task page
39 |
40 | Scenario: See the edit link only in my comments
41 | Given I am a contributor of "sgtran" project
42 | And I am authenticated
43 | And I have a task of "sgtran" project
44 | And there is a comment for this task
45 | When I am on the task page
46 | Then I should not see "Edit" within "div.comment-wrapper"
47 |
48 | Scenario: Comments should be rendered with Markdown syntax on the tasks page
49 | Given I am a contributor of "sgtran" project
50 | And I am authenticated
51 | And I have a task of "sgtran" project
52 | When I am on the task page
53 | And I fill in "comment_content" with "# Some content [link](http://exemplo.com)"
54 | And I press "Comment"
55 | Then I should see "Some content" in a "h1" tag
56 | And I should see "link" in an "a" tag
57 |
58 | @javascript
59 | Scenario: Destroy a comment
60 | Given I am a contributor of "sgtran" project
61 | And I am authenticated
62 | And I have a task of "sgtran" project
63 | And I write a comment for this task
64 | When I am on the task page
65 | And I follow "Destroy" within "my comment box" and press ok at the alert
66 | Then I should be on the task page
67 | And The comment should no longer exist
68 | And I should see "Comment was successfully destroyed."
69 |
70 |
--------------------------------------------------------------------------------
/features/step_definitions/board_steps.rb:
--------------------------------------------------------------------------------
1 | When /^I drag "([^"]*)" task to "([^"]*)" position$/ do |task_title, position|
2 | task = Task.find_by_title(task_title)
3 | postit = find_by_id(task.id)
4 | target_position = find_by_id(position.gsub(/ /, '').downcase)
5 | postit.drag_to(target_position)
6 | end
7 |
8 | Then /^I should see a board like this:$/ do |table|
9 | table.diff!(tableish('table tr', 'td,th'))
10 | end
11 |
12 | Then /^the Done division should be cleaned$/ do
13 | @project.tasks_by_position(Board::POSITIONS['done']).should be_empty
14 | end
15 |
16 | Then /^I should see "([^"]*)" task at "([^"]*)" position$/ do |task_title, position|
17 | task = Task.find_by_title(task_title)
18 | page.should have_xpath "//ul[@id='#{position.downcase}']/li[@id='#{task.id}']"
19 | end
20 |
21 |
--------------------------------------------------------------------------------
/features/step_definitions/categories_steps.rb:
--------------------------------------------------------------------------------
1 | Given /^I have a category with name "([^"]*)" and color "([^"]*)"$/ do |name, color|
2 | @category = Factory.create :category, :project => @project,
3 | :name => name,
4 | :color => color
5 | end
6 |
7 | Given /^the following categories:$/ do |categories|
8 | hash = categories.hashes
9 | hash.each do |dict|
10 | dict[:project] = @project
11 | end
12 | Category.create!(hash)
13 | end
14 |
15 |
--------------------------------------------------------------------------------
/features/step_definitions/comments_steps.rb:
--------------------------------------------------------------------------------
1 | Given /^there is a comment for this task$/ do
2 | fake_contributor = Factory.create :contributor,
3 | :name => "name",
4 | :email => "email@mail.com"
5 | @comment = Factory.create :comment, :task => @task, :contributor => fake_contributor
6 | end
7 |
8 | Given /^I write a comment for this task$/ do
9 | @comment = Factory.create :comment, :contributor => @contributor
10 | @task.update_attributes(:comments => [@comment])
11 | end
12 |
13 | Then /^the comment should belongs to the task$/ do
14 | @comment = Comment.all.last
15 | @comment.task.should == @task
16 | end
17 |
18 | Then /^I am the coment's author$/ do
19 | @comment.contributor.should == @contributor
20 | end
21 |
22 | Then /^The comment should no longer exist$/ do
23 | Comment.where(:id => @comment.id).should be_empty
24 | end
25 |
26 |
--------------------------------------------------------------------------------
/features/step_definitions/contributor_steps.rb:
--------------------------------------------------------------------------------
1 | Given /^I have a contributor(?: named "([^"]*)")?$/ do |name|
2 | name ||= 'Any name'
3 | email = name.split.first.downcase + '@test.com'
4 | @contributor = Factory.create :contributor, :name => name, :email => email
5 | end
6 |
7 | Given /^I am(?:| an) authenticated(?:| contributor)$/ do
8 | @contributor ||= Factory.create :contributor
9 |
10 | step %{I am on the sign in page}
11 | step %{I fill in "contributor_login" with "#{@contributor.email}"}
12 | step %{I fill in "contributor_password" with "#{@contributor.password}"}
13 | step %{I press "Sign in"}
14 | end
15 |
16 | Given /^I am a contributor of "([^"]*)" project$/ do |name|
17 | @project = Factory.create :project, :name => name
18 | @contributor = Factory.create :contributor, :contributions => [@project]
19 | @project.update_attribute(:contributors, [@contributor])
20 | end
21 |
22 | Given /^I am contributor with password "([^\"]*)" and email "([^\"]*)"$/ do |password, email|
23 | @contributor = Factory.create :contributor,
24 | :email => email,
25 | :password => password,
26 | :password_confirmation => password
27 | end
28 |
29 | Given /^I am contributor with password "([^"]*)" and username "([^"]*)"$/ do |password, username|
30 | @contributor = Factory.create :contributor,
31 | :username => username,
32 | :password => password,
33 | :password_confirmation => password
34 | end
35 |
36 |
37 | Given /^"([^"]*)" is a contributor of the project$/ do |name|
38 | contributor = Factory.create :contributor, :name => name, :contributions => [@project]
39 | @project.contributors << contributor
40 | @project.save
41 | end
--------------------------------------------------------------------------------
/features/step_definitions/my_web_steps.rb:
--------------------------------------------------------------------------------
1 | When /^I follow "([^\"]*)" and press ok at the pop-up$/ do |link|
2 | page.evaluate_script("window.confirm = function() { return true; }")
3 | click_link(link)
4 | end
5 |
6 | When /^I follow "([^"]*)" within "([^"]*)" and press ok at the alert$/ do |link, selector|
7 | page.evaluate_script("window.confirm = function() { return true; }")
8 | with_scope(selector) do
9 | click_link(link)
10 | end
11 | end
12 |
13 | When /^I should see "([^"]*)" in a(?:|n) "([^"]*)" tag$/ do |text, tag|
14 | if page.respond_to? :should
15 | page.should have_xpath("//#{tag}", :text => text)
16 | else
17 | assert page.has_xpath?("//#{tag}", :text => text)
18 | end
19 | end
20 |
21 |
--------------------------------------------------------------------------------
/features/step_definitions/project_steps.rb:
--------------------------------------------------------------------------------
1 | Given /^I have a project$/ do
2 | @project = Factory.create :project
3 | end
4 |
5 | Given /^the following projects:$/ do |projects|
6 | projects.hashes.collect { |p| p[:owner_id] = @contributor.id }
7 | Project.create!(projects.hashes)
8 | end
9 |
10 | When /^I delete the (\d+)(?:st|nd|rd|th) project$/ do |pos|
11 | pos = pos.to_i - 1
12 | Project.all[pos].destroy
13 | end
14 |
15 | Then /^"([^"]*)" project should have "([^"]*)" task$/ do |project_name, tasks_number|
16 | project = Project.where(:name => project_name).first
17 | project.tasks.count.should == tasks_number.to_i
18 | end
19 |
20 | Then /^"([^"]*)" project should have "([^"]*)" categor(?:y|ies)$/ do |project_name, categories_number|
21 | project = Project.where(:name => project_name).first
22 | project.categories.count.should == categories_number.to_i
23 | end
24 |
25 | Given /^I have a project named "([^"]*)"$/ do |name|
26 | Factory.create :project, :name => name
27 | end
28 |
29 | Then /^I should be the project's owner$/ do
30 | @project = Project.all.first
31 | @project.owner.should == @contributor
32 | @project.owner_id.should == @contributor.id
33 | @contributor.projects.should include(@project)
34 | end
35 |
36 | Then /^I should not have any project$/ do
37 | @contributor.projects.should be_empty
38 | end
39 |
40 |
--------------------------------------------------------------------------------
/features/step_definitions/task_steps.rb:
--------------------------------------------------------------------------------
1 | Given /^I have a task$/ do
2 | @task = Factory.create :task
3 | end
4 |
5 | Given /^the following tasks:$/ do |tasks|
6 | hash = tasks.hashes
7 | hash.each do |dict|
8 | dict[:project] = @project
9 | Factory.create :task, dict
10 | end
11 | end
12 |
13 | Given /^I have a task of "([^"]*)" project$/ do |project_name|
14 | @project = Factory.create :project, :name => 'project_name'
15 | @task = Factory.create :task, :project => @project, :author => @contributor
16 | end
17 |
18 | Then /^"([^"]*)" should be a assignee of the task$/ do |name|
19 | assignee = (Contributor.where :name => name).first
20 | @task.contributors.should include(assignee)
21 | end
22 |
23 | Then /^The task should no longer exist$/ do
24 | Task.where(:id => @task.id).should be_empty
25 | end
26 |
27 | Then /^I should be the task author$/ do
28 | Task.all.last.author.should == @contributor
29 | end
30 |
31 |
--------------------------------------------------------------------------------
/features/step_definitions/web_steps.rb:
--------------------------------------------------------------------------------
1 | # TL;DR: YOU SHOULD DELETE THIS FILE
2 | #
3 | # This file was generated by Cucumber-Rails and is only here to get you a head start
4 | # These step definitions are thin wrappers around the Capybara/Webrat API that lets you
5 | # visit pages, interact with widgets and make assertions about page content.
6 | #
7 | # If you use these step definitions as basis for your features you will quickly end up
8 | # with features that are:
9 | #
10 | # * Hard to maintain
11 | # * Verbose to read
12 | #
13 | # A much better approach is to write your own higher level step definitions, following
14 | # the advice in the following blog posts:
15 | #
16 | # * http://benmabey.com/2008/05/19/imperative-vs-declarative-scenarios-in-user-stories.html
17 | # * http://dannorth.net/2011/01/31/whose-domain-is-it-anyway/
18 | # * http://elabs.se/blog/15-you-re-cuking-it-wrong
19 | #
20 |
21 |
22 | require 'uri'
23 | require 'cgi'
24 | require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths"))
25 | require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "selectors"))
26 |
27 | module WithinHelpers
28 | def with_scope(locator)
29 | locator ? within(*selector_for(locator)) { yield } : yield
30 | end
31 | end
32 | World(WithinHelpers)
33 |
34 | # Single-line step scoper
35 | When /^(.*) within (.*[^:])$/ do |_step, parent|
36 | with_scope(parent) { step _step }
37 | end
38 |
39 | # Multi-line step scoper
40 | When /^(.*) within (.*[^:]):$/ do |_step, parent, table_or_string|
41 | with_scope(parent) { step "#{_step}:", table_or_string }
42 | end
43 |
44 | Given /^(?:|I )am on (.+)$/ do |page_name|
45 | visit path_to(page_name)
46 | end
47 |
48 | When /^(?:|I )go to (.+)$/ do |page_name|
49 | visit path_to(page_name)
50 | end
51 |
52 | When /^(?:|I )press "([^"]*)"$/ do |button|
53 | click_button(button)
54 | end
55 |
56 | When /^(?:|I )follow "([^"]*)"$/ do |link|
57 | click_link(link)
58 | end
59 |
60 | When /^(?:|I )fill in "([^"]*)" with "([^"]*)"$/ do |field, value|
61 | fill_in(field, :with => value)
62 | end
63 |
64 | When /^(?:|I )fill in "([^"]*)" for "([^"]*)"$/ do |value, field|
65 | fill_in(field, :with => value)
66 | end
67 |
68 | # Use this to fill in an entire form with data from a table. Example:
69 | #
70 | # When I fill in the following:
71 | # | Account Number | 5002 |
72 | # | Expiry date | 2009-11-01 |
73 | # | Note | Nice guy |
74 | # | Wants Email? | |
75 | #
76 | # TODO: Add support for checkbox, select og option
77 | # based on naming conventions.
78 | #
79 | When /^(?:|I )fill in the following:$/ do |fields|
80 | fields.rows_hash.each do |name, value|
81 | When %{I fill in "#{name}" with "#{value}"}
82 | end
83 | end
84 |
85 | When /^(?:|I )select "([^"]*)" from "([^"]*)"$/ do |value, field|
86 | select(value, :from => field)
87 | end
88 |
89 | When /^(?:|I )check "([^"]*)"$/ do |field|
90 | check(field)
91 | end
92 |
93 | When /^(?:|I )uncheck "([^"]*)"$/ do |field|
94 | uncheck(field)
95 | end
96 |
97 | When /^(?:|I )choose "([^"]*)"$/ do |field|
98 | choose(field)
99 | end
100 |
101 | When /^(?:|I )attach the file "([^"]*)" to "([^"]*)"$/ do |path, field|
102 | attach_file(field, File.expand_path(path))
103 | end
104 |
105 | Then /^(?:|I )should see "([^"]*)"$/ do |text|
106 | if page.respond_to? :should
107 | page.should have_content(text)
108 | else
109 | assert page.has_content?(text)
110 | end
111 | end
112 |
113 | Then /^(?:|I )should see \/([^\/]*)\/$/ do |regexp|
114 | regexp = Regexp.new(regexp)
115 |
116 | if page.respond_to? :should
117 | page.should have_xpath('//*', :text => regexp)
118 | else
119 | assert page.has_xpath?('//*', :text => regexp)
120 | end
121 | end
122 |
123 | Then /^(?:|I )should not see "([^"]*)"$/ do |text|
124 | if page.respond_to? :should
125 | page.should have_no_content(text)
126 | else
127 | assert page.has_no_content?(text)
128 | end
129 | end
130 |
131 | Then /^(?:|I )should not see \/([^\/]*)\/$/ do |regexp|
132 | regexp = Regexp.new(regexp)
133 |
134 | if page.respond_to? :should
135 | page.should have_no_xpath('//*', :text => regexp)
136 | else
137 | assert page.has_no_xpath?('//*', :text => regexp)
138 | end
139 | end
140 |
141 | Then /^the "([^"]*)" field(?: within (.*))? should contain "([^"]*)"$/ do |field, parent, value|
142 | with_scope(parent) do
143 | field = find_field(field)
144 | field_value = (field.tag_name == 'textarea') ? field.text : field.value
145 | if field_value.respond_to? :should
146 | field_value.should =~ /#{value}/
147 | else
148 | assert_match(/#{value}/, field_value)
149 | end
150 | end
151 | end
152 |
153 | Then /^the "([^"]*)" field(?: within (.*))? should not contain "([^"]*)"$/ do |field, parent, value|
154 | with_scope(parent) do
155 | field = find_field(field)
156 | field_value = (field.tag_name == 'textarea') ? field.text : field.value
157 | if field_value.respond_to? :should_not
158 | field_value.should_not =~ /#{value}/
159 | else
160 | assert_no_match(/#{value}/, field_value)
161 | end
162 | end
163 | end
164 |
165 | Then /^the "([^"]*)" checkbox(?: within (.*))? should be checked$/ do |label, parent|
166 | with_scope(parent) do
167 | field_checked = find_field(label)['checked']
168 | if field_checked.respond_to? :should
169 | field_checked.should be_true
170 | else
171 | assert field_checked
172 | end
173 | end
174 | end
175 |
176 | Then /^the "([^"]*)" checkbox(?: within (.*))? should not be checked$/ do |label, parent|
177 | with_scope(parent) do
178 | field_checked = find_field(label)['checked']
179 | if field_checked.respond_to? :should
180 | field_checked.should be_false
181 | else
182 | assert !field_checked
183 | end
184 | end
185 | end
186 |
187 | Then /^(?:|I )should be on (.+)$/ do |page_name|
188 | current_path = URI.parse(current_url).path
189 | if current_path.respond_to? :should
190 | current_path.should == path_to(page_name)
191 | else
192 | assert_equal path_to(page_name), current_path
193 | end
194 | end
195 |
196 | Then /^(?:|I )should have the following query string:$/ do |expected_pairs|
197 | query = URI.parse(current_url).query
198 | actual_params = query ? CGI.parse(query) : {}
199 | expected_params = {}
200 | expected_pairs.rows_hash.each_pair{|k,v| expected_params[k] = v.split(',')}
201 |
202 | if actual_params.respond_to? :should
203 | actual_params.should == expected_params
204 | else
205 | assert_equal expected_params, actual_params
206 | end
207 | end
208 |
209 | Then /^show me the page$/ do
210 | save_and_open_page
211 | end
212 |
--------------------------------------------------------------------------------
/features/support/env.rb:
--------------------------------------------------------------------------------
1 | require 'spork'
2 |
3 | Spork.prefork do
4 | require 'cucumber/rails'
5 |
6 | Capybara.default_selector = :css
7 |
8 | ActionController::Base.allow_rescue = false
9 |
10 | Before('@no-txn,@selenium,@culerity,@celerity,@javascript') do
11 | DatabaseCleaner.strategy = :truncation, {:except => %w[widgets]}
12 | end
13 |
14 | Before('~@no-txn', '~@selenium', '~@culerity', '~@celerity', '~@javascript') do
15 | DatabaseCleaner.strategy = :transaction
16 | end
17 | end
18 |
19 | Spork.each_run do
20 | end
21 |
22 |
--------------------------------------------------------------------------------
/features/support/paths.rb:
--------------------------------------------------------------------------------
1 | module NavigationHelpers
2 | # Maps a name to a path. Used by the
3 | #
4 | # When /^I go to (.+)$/ do |page_name|
5 | #
6 | # step definition in web_steps.rb
7 | #
8 | def path_to(page_name)
9 | case page_name
10 |
11 | when /the dashboard/
12 | '/'
13 |
14 | when /the project edit page/
15 | edit_project_path(@project)
16 |
17 | when /the projects board page/
18 | project_board_path(@project)
19 |
20 | when /my details edit page/
21 | edit_contributor_registration_path(@contributor)
22 |
23 | when /the new contributor page/
24 | new_contributor_registration_path
25 |
26 | when /the task edit page/
27 | edit_project_task_path(@project, @task)
28 |
29 | when /^the (.*) tasks page$/i
30 | project_tasks_path(@project)
31 |
32 | when /the task page/
33 | project_task_path(@project, @task)
34 |
35 | when /the category edit page/
36 | edit_project_category_path(@project, @category)
37 |
38 | when /^the (.*) categories page$/i
39 | project_categories_path(@project)
40 |
41 | when /the sign in page/
42 | new_contributor_session_path
43 |
44 | when /the edit comment page/
45 | edit_project_task_comment_path(@project, @task, @comment)
46 |
47 | # Add more mappings here.
48 | # Here is an example that pulls values out of the Regexp:
49 | #
50 | # when /^(.*)'s profile page$/i
51 | # user_profile_path(User.find_by_login($1))
52 |
53 | else
54 | begin
55 | page_name =~ /^the (.*) page$/
56 | path_components = $1.split(/\s+/)
57 | self.send(path_components.push('path').join('_').to_sym)
58 | rescue NoMethodError, ArgumentError
59 | raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
60 | "Now, go and add a mapping in #{__FILE__}"
61 | end
62 | end
63 | end
64 | end
65 |
66 | World(NavigationHelpers)
67 |
68 |
--------------------------------------------------------------------------------
/features/support/selectors.rb:
--------------------------------------------------------------------------------
1 | module HtmlSelectorsHelpers
2 | # Maps a name to a selector. Used primarily by the
3 | #
4 | # When /^(.+) within (.+)$/ do |step, scope|
5 | #
6 | # step definitions in web_steps.rb
7 | #
8 | def selector_for(locator)
9 | case locator
10 |
11 | when "the page"
12 | "html > body"
13 |
14 | when /my comment box/
15 | 'div.comment-wrapper'
16 |
17 | # Add more mappings here.
18 | # Here is an example that pulls values out of the Regexp:
19 | #
20 | # when /^the (notice|error|info) flash$/
21 | # ".flash.#{$1}"
22 |
23 | # You can also return an array to use a different selector
24 | # type, like:
25 | #
26 | # when /the header/
27 | # [:xpath, "//header"]
28 |
29 | # This allows you to provide a quoted selector as the scope
30 | # for "within" steps as was previously the default for the
31 | # web steps:
32 | when /^"(.+)"$/
33 | $1
34 |
35 | else
36 | raise "Can't find mapping from \"#{locator}\" to a selector.\n" +
37 | "Now, go and add a mapping in #{__FILE__}"
38 | end
39 | end
40 | end
41 |
42 | World(HtmlSelectorsHelpers)
43 |
44 |
--------------------------------------------------------------------------------
/features/tasks.feature:
--------------------------------------------------------------------------------
1 | Feature: Manipulate tasks
2 | As a user
3 | I want manipulate tasks
4 | In order to organize them
5 |
6 | Scenario: Register task successfully
7 | Given I am a contributor of "sgtran" project
8 | And I am authenticated
9 | And I have a category with name "Feature" and color "ffa5a5"
10 | And I am on the "sgtran" tasks page
11 | When I follow "New Task"
12 | And I fill in "Title" with "Create issues"
13 | And I fill in "Description" with "Create issues for kanban-roots project"
14 | And I select "5" from "Points"
15 | And I select "Feature" from "Category"
16 | And I select "" from "Position"
17 | And I press "Save"
18 | Then I should be on the projects board page
19 | And I should see "Task was successfully created."
20 | And I should see "Create issue"
21 | And "sgtran" project should have "1" task
22 | And I should be the task author
23 |
24 | Scenario: Try to register tasks with errors
25 | Given I am a contributor of "sgtran" project
26 | And I am authenticated
27 | And I am on the "sgtran" tasks page
28 | When I follow "New Task"
29 | When I fill in "Title" with ""
30 | And I press "Save"
31 | Then I should see "can't be blank"
32 |
33 | Scenario: Tasks description should be rendered with Markdown syntax
34 | Given I am a contributor of "sgtran" project
35 | And I am authenticated
36 | And I have a task of "sgtran" project
37 | When I am on the task edit page
38 | And I fill in "Description" with "## Some content here with _emphasis_"
39 | And I press "Save"
40 | And I go to the task page
41 | And I should see "Some content here" in a "h2" tag
42 | And I should see "emphasis" in an "em" tag
43 |
44 | Scenario: Edit a task
45 | Given I am a contributor of "sgtran" project
46 | And I am authenticated
47 | And I have a task of "sgtran" project
48 | When I am on the task edit page
49 | And I fill in "Title" with "Close issue"
50 | And I press "Save"
51 | Then I should be on the projects board page
52 | Then I should see "Close issue"
53 |
54 | @javascript
55 | Scenario: Destroy task
56 | Given I am a contributor of "sgtran" project
57 | And I am authenticated
58 | And I have a task of "sgtran" project
59 | When I am on the "sgtran" tasks page
60 | And I follow "Destroy" and press ok at the pop-up
61 | Then I should be on the projects board page
62 | And The task should no longer exist
63 |
64 | Scenario: Add assignees for a task
65 | Given I am a contributor of "sgtran" project
66 | And I am authenticated
67 | And I have a task of "sgtran" project
68 | And "Hugo Maia" is a contributor of the project
69 | And "Rodrigo Manhães" is a contributor of the project
70 | When I am on the task edit page
71 | And I select "Hugo Maia" from "Assignees"
72 | And I select "Rodrigo Manhães" from "Assignees"
73 | And I press "Save"
74 | Then I should be on the projects board page
75 | And I should see "Task was successfully updated."
76 | And "Hugo Maia" should be a assignee of the task
77 | And "Rodrigo Manhães" should be a assignee of the task
78 |
79 |
--------------------------------------------------------------------------------
/heroku-deploy.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | echo "gem 'pg'" >> Gemfile
3 | bundle exec rake assets:precompile
4 | bundle
5 | git add .
6 | git commit -m "Precompile assets; add postgre adapter"
7 | git push heroku master --force
8 | git reset HEAD^1
9 | rm -rf public/assets
10 | git checkout .
11 |
12 |
--------------------------------------------------------------------------------
/lib/albino_render.rb:
--------------------------------------------------------------------------------
1 | # create a custom renderer that allows highlighting of code blocks
2 | class HTMLwithAlbino < Redcarpet::Render::HTML
3 | def block_code(code, lang)
4 | lang = sanitaze_lang(lang)
5 |
6 | if can_pygmentize?
7 | Albino.colorize(code, lang)
8 | else
9 | # This is a hack for pygments work on Heroku
10 | require 'net/http'
11 | Net::HTTP.post_form(URI.parse('http://pygments.appspot.com/'),
12 | {'code'=>code, 'lang'=>lang}).body
13 | end
14 | end
15 |
16 | private
17 |
18 | def can_pygmentize?
19 | system 'pygmentize -V'
20 | end
21 |
22 | # Sanitize the language highlighting to not raise an error if used github code
23 | # block style (~~~.lang). This is based on Albino valid options.
24 | # http://rubydoc.info/gems/albino/1.3.3/Albino#convert_options-instance_method
25 | def sanitaze_lang(lang)
26 | lang.scan(/[a-z0-9\-\_\+\=\#\,\s]+/i).join
27 | end
28 | end
--------------------------------------------------------------------------------
/lib/tasks/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hugomaiavieira/kanban-roots/8115084ef5778868cf911fbac154958e4af5cc63/lib/tasks/.gitkeep
--------------------------------------------------------------------------------
/lib/templates/slim/scaffold/_form.html.slim:
--------------------------------------------------------------------------------
1 | = simple_form_for(@<%= singular_table_name %>) do |f|
2 | = f.error_notification
3 |
4 | .inputs
5 | | <%- attributes.each do |attribute| -%>
6 | = f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %>
7 | | <%- end -%>
8 |
9 | .actions
10 | = f.button :submit
11 |
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
The page you were looking for doesn't exist.
23 |
You may have mistyped the address or the page may have moved.