command_model 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/Guardfile +9 -0
- data/LICENSE +22 -0
- data/README.md +99 -0
- data/Rakefile +2 -0
- data/command_model.gemspec +23 -0
- data/examples/bank/.gitignore +15 -0
- data/examples/bank/Gemfile +38 -0
- data/examples/bank/README.rdoc +261 -0
- data/examples/bank/Rakefile +7 -0
- data/examples/bank/app/assets/images/rails.png +0 -0
- data/examples/bank/app/assets/javascripts/accounts.js.coffee +3 -0
- data/examples/bank/app/assets/javascripts/application.js +15 -0
- data/examples/bank/app/assets/stylesheets/accounts.css.scss +3 -0
- data/examples/bank/app/assets/stylesheets/application.css +13 -0
- data/examples/bank/app/controllers/accounts_controller.rb +62 -0
- data/examples/bank/app/controllers/application_controller.rb +3 -0
- data/examples/bank/app/helpers/accounts_helper.rb +2 -0
- data/examples/bank/app/helpers/application_helper.rb +2 -0
- data/examples/bank/app/mailers/.gitkeep +0 -0
- data/examples/bank/app/models/.gitkeep +0 -0
- data/examples/bank/app/models/account.rb +65 -0
- data/examples/bank/app/views/accounts/deposit_form.html.erb +23 -0
- data/examples/bank/app/views/accounts/index.html.erb +22 -0
- data/examples/bank/app/views/accounts/transfer_form.html.erb +32 -0
- data/examples/bank/app/views/accounts/withdraw_form.html.erb +23 -0
- data/examples/bank/app/views/layouts/application.html.erb +15 -0
- data/examples/bank/config.ru +4 -0
- data/examples/bank/config/application.rb +65 -0
- data/examples/bank/config/boot.rb +6 -0
- data/examples/bank/config/environment.rb +5 -0
- data/examples/bank/config/environments/development.rb +31 -0
- data/examples/bank/config/environments/production.rb +64 -0
- data/examples/bank/config/environments/test.rb +35 -0
- data/examples/bank/config/initializers/accounts.rb +6 -0
- data/examples/bank/config/initializers/backtrace_silencers.rb +7 -0
- data/examples/bank/config/initializers/inflections.rb +15 -0
- data/examples/bank/config/initializers/mime_types.rb +5 -0
- data/examples/bank/config/initializers/secret_token.rb +7 -0
- data/examples/bank/config/initializers/session_store.rb +8 -0
- data/examples/bank/config/initializers/wrap_parameters.rb +10 -0
- data/examples/bank/config/locales/en.yml +5 -0
- data/examples/bank/config/routes.rb +12 -0
- data/examples/bank/db/seeds.rb +7 -0
- data/examples/bank/lib/assets/.gitkeep +0 -0
- data/examples/bank/lib/tasks/.gitkeep +0 -0
- data/examples/bank/log/.gitkeep +0 -0
- data/examples/bank/public/404.html +26 -0
- data/examples/bank/public/422.html +26 -0
- data/examples/bank/public/500.html +25 -0
- data/examples/bank/public/favicon.ico +0 -0
- data/examples/bank/public/robots.txt +5 -0
- data/examples/bank/script/rails +6 -0
- data/examples/bank/test/fixtures/.gitkeep +0 -0
- data/examples/bank/test/functional/.gitkeep +0 -0
- data/examples/bank/test/functional/accounts_controller_test.rb +19 -0
- data/examples/bank/test/integration/.gitkeep +0 -0
- data/examples/bank/test/performance/browsing_test.rb +12 -0
- data/examples/bank/test/test_helper.rb +7 -0
- data/examples/bank/test/unit/.gitkeep +0 -0
- data/examples/bank/test/unit/helpers/accounts_helper_test.rb +4 -0
- data/examples/bank/vendor/assets/javascripts/.gitkeep +0 -0
- data/examples/bank/vendor/assets/stylesheets/.gitkeep +0 -0
- data/examples/bank/vendor/plugins/.gitkeep +0 -0
- data/lib/command_model.rb +7 -0
- data/lib/command_model/model.rb +173 -0
- data/lib/command_model/version.rb +3 -0
- data/spec/model_spec.rb +222 -0
- data/spec/spec_helper.rb +2 -0
- metadata +164 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
Bank::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
|
+
# Configure static asset server for tests with Cache-Control for performance
|
11
|
+
config.serve_static_assets = true
|
12
|
+
config.static_cache_control = "public, max-age=3600"
|
13
|
+
|
14
|
+
# Log error messages when you accidentally call methods on nil
|
15
|
+
config.whiny_nils = true
|
16
|
+
|
17
|
+
# Show full error reports and disable caching
|
18
|
+
config.consider_all_requests_local = true
|
19
|
+
config.action_controller.perform_caching = false
|
20
|
+
|
21
|
+
# Raise exceptions instead of rendering exception templates
|
22
|
+
config.action_dispatch.show_exceptions = false
|
23
|
+
|
24
|
+
# Disable request forgery protection in test environment
|
25
|
+
config.action_controller.allow_forgery_protection = false
|
26
|
+
|
27
|
+
# Tell Action Mailer not to deliver emails to the real world.
|
28
|
+
# The :test delivery method accumulates sent emails in the
|
29
|
+
# ActionMailer::Base.deliveries array.
|
30
|
+
config.action_mailer.delivery_method = :test
|
31
|
+
|
32
|
+
|
33
|
+
# Print deprecation notices to the stderr
|
34
|
+
config.active_support.deprecation = :stderr
|
35
|
+
end
|
@@ -0,0 +1,7 @@
|
|
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!
|
@@ -0,0 +1,15 @@
|
|
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
|
+
#
|
12
|
+
# These inflection rules are supported but not enabled by default:
|
13
|
+
# ActiveSupport::Inflector.inflections do |inflect|
|
14
|
+
# inflect.acronym 'RESTful'
|
15
|
+
# end
|
@@ -0,0 +1,7 @@
|
|
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
|
+
Bank::Application.config.secret_token = '85ecca5acd1ebe24d4dde79f727ecdf024e614fe2178c8fafc86037e43028479cd34ac819c045e43af2822e3dcd613658ab6f3580c3593e74e7d933a442797a3'
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# Be sure to restart your server when you modify this file.
|
2
|
+
|
3
|
+
Bank::Application.config.session_store :cookie_store, key: '_bank_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
|
+
# Bank::Application.config.session_store :active_record_store
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# Be sure to restart your server when you modify this file.
|
2
|
+
#
|
3
|
+
# This file contains settings for ActionController::ParamsWrapper which
|
4
|
+
# is enabled by default.
|
5
|
+
|
6
|
+
# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
|
7
|
+
ActiveSupport.on_load(:action_controller) do
|
8
|
+
wrap_parameters format: [:json]
|
9
|
+
end
|
10
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Bank::Application.routes.draw do
|
2
|
+
get "accounts/:id/withdraw" => "accounts#withdraw_form", :as => :account_withdraw_form
|
3
|
+
post "accounts/:id/withdraw" => "accounts#withdraw", :as => :account_withdraw
|
4
|
+
|
5
|
+
get "accounts/:id/deposit" => "accounts#deposit_form", :as => :account_deposit_form
|
6
|
+
post "accounts/:id/deposit" => "accounts#deposit", :as => :account_deposit
|
7
|
+
|
8
|
+
get "accounts/transfer" => "accounts#transfer_form", :as => :account_transfer_form
|
9
|
+
post "accounts/transfer" => "accounts#transfer", :as => :account_transfer
|
10
|
+
|
11
|
+
root :to => "accounts#index"
|
12
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# This file should contain all the record creation needed to seed the database with its default values.
|
2
|
+
# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
|
3
|
+
#
|
4
|
+
# Examples:
|
5
|
+
#
|
6
|
+
# cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
|
7
|
+
# Mayor.create(name: 'Emanuel', city: cities.first)
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>The page you were looking for doesn't exist (404)</title>
|
5
|
+
<style type="text/css">
|
6
|
+
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
|
7
|
+
div.dialog {
|
8
|
+
width: 25em;
|
9
|
+
padding: 0 4em;
|
10
|
+
margin: 4em auto 0 auto;
|
11
|
+
border: 1px solid #ccc;
|
12
|
+
border-right-color: #999;
|
13
|
+
border-bottom-color: #999;
|
14
|
+
}
|
15
|
+
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
|
16
|
+
</style>
|
17
|
+
</head>
|
18
|
+
|
19
|
+
<body>
|
20
|
+
<!-- This file lives in public/404.html -->
|
21
|
+
<div class="dialog">
|
22
|
+
<h1>The page you were looking for doesn't exist.</h1>
|
23
|
+
<p>You may have mistyped the address or the page may have moved.</p>
|
24
|
+
</div>
|
25
|
+
</body>
|
26
|
+
</html>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>The change you wanted was rejected (422)</title>
|
5
|
+
<style type="text/css">
|
6
|
+
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
|
7
|
+
div.dialog {
|
8
|
+
width: 25em;
|
9
|
+
padding: 0 4em;
|
10
|
+
margin: 4em auto 0 auto;
|
11
|
+
border: 1px solid #ccc;
|
12
|
+
border-right-color: #999;
|
13
|
+
border-bottom-color: #999;
|
14
|
+
}
|
15
|
+
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
|
16
|
+
</style>
|
17
|
+
</head>
|
18
|
+
|
19
|
+
<body>
|
20
|
+
<!-- This file lives in public/422.html -->
|
21
|
+
<div class="dialog">
|
22
|
+
<h1>The change you wanted was rejected.</h1>
|
23
|
+
<p>Maybe you tried to change something you didn't have access to.</p>
|
24
|
+
</div>
|
25
|
+
</body>
|
26
|
+
</html>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>We're sorry, but something went wrong (500)</title>
|
5
|
+
<style type="text/css">
|
6
|
+
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
|
7
|
+
div.dialog {
|
8
|
+
width: 25em;
|
9
|
+
padding: 0 4em;
|
10
|
+
margin: 4em auto 0 auto;
|
11
|
+
border: 1px solid #ccc;
|
12
|
+
border-right-color: #999;
|
13
|
+
border-bottom-color: #999;
|
14
|
+
}
|
15
|
+
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
|
16
|
+
</style>
|
17
|
+
</head>
|
18
|
+
|
19
|
+
<body>
|
20
|
+
<!-- This file lives in public/500.html -->
|
21
|
+
<div class="dialog">
|
22
|
+
<h1>We're sorry, but something went wrong.</h1>
|
23
|
+
</div>
|
24
|
+
</body>
|
25
|
+
</html>
|
File without changes
|
@@ -0,0 +1,6 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
|
3
|
+
|
4
|
+
APP_PATH = File.expand_path('../../config/application', __FILE__)
|
5
|
+
require File.expand_path('../../config/boot', __FILE__)
|
6
|
+
require 'rails/commands'
|
File without changes
|
File without changes
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class AccountsControllerTest < ActionController::TestCase
|
4
|
+
test "should get index" do
|
5
|
+
get :index
|
6
|
+
assert_response :success
|
7
|
+
end
|
8
|
+
|
9
|
+
test "should get show" do
|
10
|
+
get :show
|
11
|
+
assert_response :success
|
12
|
+
end
|
13
|
+
|
14
|
+
test "should get transfer" do
|
15
|
+
get :transfer
|
16
|
+
assert_response :success
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
File without changes
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'rails/performance_test_help'
|
3
|
+
|
4
|
+
class BrowsingTest < ActionDispatch::PerformanceTest
|
5
|
+
# Refer to the documentation for all available options
|
6
|
+
# self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory]
|
7
|
+
# :output => 'tmp/performance', :formats => [:flat] }
|
8
|
+
|
9
|
+
def test_homepage
|
10
|
+
get '/'
|
11
|
+
end
|
12
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,173 @@
|
|
1
|
+
module CommandModel
|
2
|
+
class Model
|
3
|
+
include ActiveModel::Validations
|
4
|
+
include ActiveModel::Conversion
|
5
|
+
extend ActiveModel::Naming
|
6
|
+
|
7
|
+
# Parameter requires one or more attributes as its first parameter(s).
|
8
|
+
# It accepts an options hash as its last parameter.
|
9
|
+
#
|
10
|
+
# ==== Options
|
11
|
+
#
|
12
|
+
# * typecast - The type of object to typecast to. Typecasts are built-in
|
13
|
+
# for integer, float, and date. Additional typecasts can be defined
|
14
|
+
# by defining a method typecast_#{name} for a typecast of #{name}.
|
15
|
+
# * validations - All other options are considered validations and are
|
16
|
+
# passed to ActiveModel::Validates.validates
|
17
|
+
#
|
18
|
+
# ==== Examples
|
19
|
+
#
|
20
|
+
# parameter :gender
|
21
|
+
# parameter :name, :presence => true
|
22
|
+
# parameter :birthdate, :typecast => :date
|
23
|
+
# parameter :height, :weight,
|
24
|
+
# :typecast => :integer,
|
25
|
+
# :presence => true,
|
26
|
+
# :numericality => { :greater_than_or_equal_to => 0 }
|
27
|
+
def self.parameter(*args)
|
28
|
+
options = args.last.kind_of?(Hash) ? args.pop.clone : {}
|
29
|
+
typecast = options.delete(:typecast)
|
30
|
+
|
31
|
+
args.each do |name|
|
32
|
+
attr_reader name
|
33
|
+
|
34
|
+
if typecast
|
35
|
+
attr_typecasting_writer name, typecast
|
36
|
+
else
|
37
|
+
attr_writer name
|
38
|
+
end
|
39
|
+
validates name, options.clone if options.present? # clone options because validates mutates the hash :(
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.attr_typecasting_writer(name, target_type) #:nodoc
|
44
|
+
eval <<-END_EVAL
|
45
|
+
def #{name}=(value)
|
46
|
+
typecast_value = typecast_#{target_type}(value)
|
47
|
+
if typecast_value
|
48
|
+
@typecast_errors.delete("#{name}")
|
49
|
+
@#{name} = typecast_value
|
50
|
+
else
|
51
|
+
@typecast_errors["#{name}"] = "#{target_type}"
|
52
|
+
@#{name} = value
|
53
|
+
end
|
54
|
+
|
55
|
+
@#{name}
|
56
|
+
end
|
57
|
+
END_EVAL
|
58
|
+
end
|
59
|
+
|
60
|
+
# Executes a block of code if the command model is valid.
|
61
|
+
#
|
62
|
+
# Accepts either a command model or a hash of attributes with which to
|
63
|
+
# create a new command model.
|
64
|
+
#
|
65
|
+
# ==== Examples
|
66
|
+
#
|
67
|
+
# RenameUserCommand.execute(:login => "john") do |command|
|
68
|
+
# if allowed_to_rename_user?
|
69
|
+
# self.login = command.login
|
70
|
+
# else
|
71
|
+
# command.errors.add :base, "not allowed to rename"
|
72
|
+
# end
|
73
|
+
# end
|
74
|
+
def self.execute(attributes_or_command)
|
75
|
+
command = if attributes_or_command.kind_of? self
|
76
|
+
attributes_or_command
|
77
|
+
else
|
78
|
+
new(attributes_or_command)
|
79
|
+
end
|
80
|
+
|
81
|
+
yield command if command.valid?
|
82
|
+
command.execution_attempted!
|
83
|
+
command
|
84
|
+
end
|
85
|
+
|
86
|
+
# Quickly create a successful command object. This is used when the
|
87
|
+
# command takes no parameters to want to take advantage of the success?
|
88
|
+
# and errors properties of a command object.
|
89
|
+
def self.success
|
90
|
+
new.tap do |instance|
|
91
|
+
instance.execution_attempted!
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Quickly create a failed command object. Requires one parameter with
|
96
|
+
# the description of what went wrong. This is used when the
|
97
|
+
# command takes no parameters to want to take advantage of the success?
|
98
|
+
# and errors properties of a command object.
|
99
|
+
def self.failure(error)
|
100
|
+
new.tap do |instance|
|
101
|
+
instance.execution_attempted!
|
102
|
+
instance.errors.add(:base, error)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Accepts an attributes hash
|
107
|
+
def initialize(attributes={})
|
108
|
+
@typecast_errors = {}
|
109
|
+
|
110
|
+
attributes.each do |k,v|
|
111
|
+
send "#{k}=", v
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Record that an attempt was made to execute this command whether or not
|
116
|
+
# it was successful.
|
117
|
+
def execution_attempted! #:nodoc:
|
118
|
+
@execution_attempted = true
|
119
|
+
end
|
120
|
+
|
121
|
+
# True if execution has been attempted on this command
|
122
|
+
def execution_attempted?
|
123
|
+
@execution_attempted
|
124
|
+
end
|
125
|
+
|
126
|
+
# Command has been executed without errors
|
127
|
+
def success?
|
128
|
+
execution_attempted? && errors.empty?
|
129
|
+
end
|
130
|
+
|
131
|
+
#:nodoc:
|
132
|
+
def persisted?
|
133
|
+
false
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
def typecast_integer(value)
|
138
|
+
Integer(value) rescue nil
|
139
|
+
end
|
140
|
+
|
141
|
+
def typecast_float(value)
|
142
|
+
Float(value) rescue nil
|
143
|
+
end
|
144
|
+
|
145
|
+
def typecast_date(value)
|
146
|
+
return value if value.kind_of? Date
|
147
|
+
value = value.to_s
|
148
|
+
if value =~ /\A(\d\d\d\d)-(\d\d)-(\d\d)\z/
|
149
|
+
Date.civil($1.to_i, $2.to_i, $3.to_i) rescue nil
|
150
|
+
else
|
151
|
+
Date.strptime(value, "%m/%d/%Y") rescue nil
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def include_typecasting_errors
|
156
|
+
@typecast_errors.each do |attribute, target_type|
|
157
|
+
unless errors[attribute].present?
|
158
|
+
errors.add attribute, "is not a #{target_type}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# overriding this to make typecasting errors run at the end so they will
|
164
|
+
# not run if there is already an error on the column. Otherwise, when
|
165
|
+
# typecasting to an integer and using validates_numericality_of two
|
166
|
+
# errors will be generated.
|
167
|
+
def run_validations!
|
168
|
+
super
|
169
|
+
include_typecasting_errors
|
170
|
+
errors.empty?
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|