command_model 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|