acts_as_account 1.1.5 → 1.1.6

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,20 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "rspec", "~> 2.3.0"
10
+ gem "bundler", "~> 1.0.0"
11
+ gem "jeweler", "~> 1.5.2"
12
+ gem "cucumber", "~> 0.9.4"
13
+ gem 'mysql', '>= 2.7.0'
14
+ end
15
+
16
+ group :default do
17
+ gem "activerecord", "~> 2.3.5"
18
+ gem "actionpack", "~> 2.3.5"
19
+ gem "database_cleaner"
20
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,52 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ actionpack (2.3.11)
5
+ activesupport (= 2.3.11)
6
+ rack (~> 1.1.0)
7
+ activerecord (2.3.11)
8
+ activesupport (= 2.3.11)
9
+ activesupport (2.3.11)
10
+ builder (2.1.2)
11
+ cucumber (0.9.4)
12
+ builder (~> 2.1.2)
13
+ diff-lcs (~> 1.1.2)
14
+ gherkin (~> 2.2.9)
15
+ json (~> 1.4.6)
16
+ term-ansicolor (~> 1.0.5)
17
+ database_cleaner (0.6.6)
18
+ diff-lcs (1.1.2)
19
+ gherkin (2.2.9)
20
+ json (~> 1.4.6)
21
+ term-ansicolor (~> 1.0.5)
22
+ git (1.2.5)
23
+ jeweler (1.5.2)
24
+ bundler (~> 1.0.0)
25
+ git (>= 1.2.5)
26
+ rake
27
+ json (1.4.6)
28
+ mysql (2.8.1)
29
+ rack (1.1.2)
30
+ rake (0.8.7)
31
+ rspec (2.3.0)
32
+ rspec-core (~> 2.3.0)
33
+ rspec-expectations (~> 2.3.0)
34
+ rspec-mocks (~> 2.3.0)
35
+ rspec-core (2.3.1)
36
+ rspec-expectations (2.3.0)
37
+ diff-lcs (~> 1.1.2)
38
+ rspec-mocks (2.3.0)
39
+ term-ansicolor (1.0.5)
40
+
41
+ PLATFORMS
42
+ ruby
43
+
44
+ DEPENDENCIES
45
+ actionpack (~> 2.3.5)
46
+ activerecord (~> 2.3.5)
47
+ bundler (~> 1.0.0)
48
+ cucumber (~> 0.9.4)
49
+ database_cleaner
50
+ jeweler (~> 1.5.2)
51
+ mysql (>= 2.7.0)
52
+ rspec (~> 2.3.0)
data/Rakefile CHANGED
@@ -1,4 +1,13 @@
1
1
  require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ Bundler.require(:default, :development)
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts "Run `bundle install` to install missing gems"
9
+ exit e.status_code
10
+ end
2
11
  require 'rake'
3
12
 
4
13
  begin
@@ -30,5 +39,5 @@ namespace :features do
30
39
  conn.execute('CREATE DATABASE acts_as_account')
31
40
  conn.execute('USE acts_as_account')
32
41
  load(File.dirname(__FILE__) + '/db/schema.rb')
33
- end
42
+ end
34
43
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.5
1
+ 1.1.6
@@ -4,8 +4,13 @@ Feature: Creating an Account
4
4
 
5
5
  Scenario: Every Holder should have a default Account
6
6
  Then User A has an Account named default
7
-
8
- Scenario: Creating a second Accounts with the same name for a Holder returns original account
7
+
8
+ Scenario: Creating a second Accounts with the same name for a Holder returns original account
9
9
  Given User A has an Account named default
10
10
  And I create an Account named default for User A
11
- Then I get the original account
11
+ Then I get the original account
12
+
13
+ Scenario: Race condition while creating account
14
+ Given I have the same user in memory
15
+ And I disable the account existence check on those
16
+ When I call 'account' on both it should be possible
@@ -19,13 +19,13 @@ end
19
19
 
20
20
  Then /^the account has (\d+) journals?$/ do |num_journals|
21
21
  num_journals = num_journals.to_i
22
-
22
+
23
23
  if num_journals == 1
24
24
  @journal = @account.journals.first
25
25
  else
26
26
  @journals = @account.journals
27
27
  end
28
-
28
+
29
29
  assert_equal num_journals, @account.journals.count
30
30
  end
31
31
 
@@ -56,7 +56,7 @@ When /^I transfer (\d+) € from global (\w+) account to global (\w+) account$/
56
56
  to_account = Account.for(to)
57
57
  Journal.current.transfer(amount.to_i, from_account, to_account, @reference, @valuta)
58
58
  end
59
-
59
+
60
60
  Then /^the balance\-sheet should be:$/ do |table|
61
61
  table.hashes.each do |row|
62
62
  assert_equal row['Balance'].to_i, User.find_by_name(row['User']).account.balance
@@ -103,7 +103,7 @@ end
103
103
  Then /^all postings reference (\w+) with (\w+) (\w+)$/ do |reference_class, name, value|
104
104
  reference = reference_class.constantize.find(:first, :conditions => "#{name} = #{value}")
105
105
  Posting.all.each do |posting|
106
- assert_equal reference, posting.reference
106
+ assert_equal reference, posting.reference
107
107
  end
108
108
  end
109
109
 
@@ -121,7 +121,7 @@ end
121
121
 
122
122
  Then /^(\w+) with (\w+) (\w+) references all postings$/ do |reference_class, name, value|
123
123
  reference = reference_class.constantize.find(:first, :conditions => "#{name} = #{value}")
124
- assert_equal Posting.all, reference.postings
124
+ assert_equal Posting.all, reference.postings
125
125
  end
126
126
 
127
127
  Then /^the order of the postings is correct$/ do
@@ -130,4 +130,19 @@ Then /^the order of the postings is correct$/ do
130
130
  assert from.amount < 0
131
131
  assert to.amount > 0
132
132
  end
133
+ end
134
+
135
+ Given /^I have the same user in memory$/ do
136
+ @user1 = User.first
137
+ @user2 = User.find(@user1.id)
138
+ end
139
+
140
+ Given /^I disable the account existence check on those$/ do
141
+ [@user1, @user2].each do |user|
142
+ user.instance_eval "def default_account ; end"
143
+ end
144
+ end
145
+
146
+ When /^I call 'account' on both it should be possible$/ do
147
+ [@user1, @user2].each { |user| user.account }
133
148
  end
@@ -1,16 +1,9 @@
1
- require 'rubygems'
2
- gem 'mysql'
3
- gem 'activerecord'
4
- gem 'actionpack'
5
- require 'active_record'
6
-
7
1
  require 'test/unit/assertions'
8
2
 
9
3
  World(Test::Unit::Assertions)
10
4
 
11
- ActiveRecord::Base.establish_connection(YAML.load_file(File.dirname(__FILE__) + '/../../db/database.yml')['acts_as_account'])
12
-
13
5
  require File.dirname(__FILE__) + '/../../lib/acts_as_account'
6
+ ActiveRecord::Base.establish_connection(YAML.load_file(File.dirname(__FILE__) + '/../../db/database.yml')['acts_as_account'])
14
7
 
15
8
  #ActiveRecord::Base.logger = Logger.new(STDOUT)
16
9
 
@@ -1,46 +1,46 @@
1
1
  module ActsAsAccount
2
2
  class Account < ActiveRecord::Base
3
3
  set_table_name :acts_as_account_accounts
4
-
4
+
5
5
  belongs_to :holder, :polymorphic => true
6
6
  has_many :postings
7
7
  has_many :journals, :through => :postings
8
8
 
9
- # TODO: discuss with norman:
9
+ # TODO: discuss with norman:
10
10
  # validates_presence_of will force an ActiveRecord::find on the object
11
11
  # but we have to create accounts for deleted holder!
12
12
  #
13
13
  # validates_presence_of :holder
14
-
14
+
15
15
  class << self
16
-
16
+
17
17
  def recalculate_all_balances
18
18
  ActsAsAccount::Account.update_all(:balance => 0, :postings_count => 0, :last_valuta => nil)
19
19
  sql = <<-EOT
20
- SELECT
21
- account_id as id,
20
+ SELECT
21
+ account_id as id,
22
22
  count(*) as calculated_postings_count,
23
23
  sum(amount) as calculated_balance,
24
24
  max(valuta) as calculated_valuta
25
25
  FROM
26
- acts_as_account_postings
27
- GROUP BY
28
- account_id
29
- HAVING
26
+ acts_as_account_postings
27
+ GROUP BY
28
+ account_id
29
+ HAVING
30
30
  calculated_postings_count > 0
31
31
  EOT
32
32
 
33
33
  ActsAsAccount::Account.find_by_sql(sql).each do |account|
34
34
  account.lock!
35
35
  account.update_attributes(
36
- :balance => account.calculated_balance,
36
+ :balance => account.calculated_balance,
37
37
  :postings_count => account.calculated_postings_count,
38
38
  :last_valuta => account.calculated_valuta)
39
39
 
40
40
  puts "account:#{account.id}, balance:#{account.balance}, postings_count:#{account.postings_count}, last_valuta:#{account.last_valuta}"
41
41
  end
42
42
  end
43
-
43
+
44
44
  def for(name)
45
45
  GlobalAccount.find_or_create_by_name(name.to_s).account
46
46
  end
@@ -50,26 +50,26 @@ module ActsAsAccount
50
50
  super
51
51
  end
52
52
  end
53
-
53
+
54
54
  def create(attributes = nil)
55
55
  find_on_error(attributes) do
56
56
  super
57
57
  end
58
58
  end
59
-
59
+
60
60
  def delete_account(number)
61
61
  transaction do
62
62
  account = find(number)
63
63
  raise ActiveRecord::ActiveRecordError, "Cannot be deleted" unless account.deleteable?
64
-
64
+
65
65
  account.holder.destroy if [ManuallyCreatedAccount, GlobalAccount].include?(account.holder.class)
66
66
  account.destroy
67
67
  end
68
68
  end
69
-
69
+
70
70
  def find_on_error(attributes)
71
71
  yield
72
-
72
+
73
73
  # Trying to create a duplicate key on a unique index raises StatementInvalid
74
74
  rescue ActiveRecord::StatementInvalid => e
75
75
  record = if attributes[:holder]
@@ -80,7 +80,7 @@ module ActsAsAccount
80
80
  attributes[:holder_type], attributes[:holder_id], attributes[:name]
81
81
  ])
82
82
  end
83
- record || raise
83
+ record || raise("Cannot find or create account with attributes #{attributes.inspect}")
84
84
  end
85
85
  end
86
86
 
@@ -4,20 +4,20 @@ module ActsAsAccount
4
4
  base.extend ClassMethods
5
5
  base.class_eval do
6
6
  def account(name = :default)
7
- __send__("#{name}_account") || __send__("create_#{name}_account", :name => name.to_s)
7
+ __send__("#{name}_account") || __send__("create_#{name}_account", :name => name.to_s, :holder_id => self.id, :holder_type => self.class.to_s)
8
8
  end
9
9
  end
10
10
  end
11
-
11
+
12
12
  module ClassMethods
13
-
13
+
14
14
  def has_account(name = :default)
15
15
  has_one "#{name}_account", :conditions => "name = '#{name}'", :class_name => "ActsAsAccount::Account", :as => :holder
16
16
  unless instance_methods.include?('accounts')
17
17
  has_many :accounts, :class_name => "ActsAsAccount::Account", :as => :holder
18
18
  end
19
19
  end
20
-
20
+
21
21
  def is_reference
22
22
  has_many :postings, :class_name => "ActsAsAccount::Posting", :as => :reference
23
23
  class_eval <<-EOS
@@ -20,7 +20,7 @@ module ActsAsAccount
20
20
  end
21
21
 
22
22
  def transfers
23
- returning([]) do |transfers|
23
+ [].tap do |transfers|
24
24
  postings.in_groups_of(2) { |postings| transfers << Transfer.new(*postings) }
25
25
  end
26
26
  end
@@ -43,7 +43,8 @@ module ActsAsAccount
43
43
  end
44
44
  end
45
45
 
46
- private
46
+ private
47
+
47
48
  def add_posting(amount, account, other_account, reference, valuta)
48
49
  posting = postings.build(
49
50
  :amount => amount,
@@ -52,15 +53,9 @@ module ActsAsAccount
52
53
  :reference => reference,
53
54
  :valuta => valuta)
54
55
 
55
- account.balance += posting.amount
56
+ account.reload.balance += posting.amount
56
57
  account.postings_count += 1
57
58
 
58
- # The last valuta will be the most recent date btw this posting valuta (payment.created_at)
59
- # and the last valuta.
60
- # TODO 2010-05-20 jm: Ask Holger how the last valuta could be more recent than the payment ???
61
-
62
-
63
- # TODO 2010-05-20 jm: Ask Holger with saving *without* validations ???
64
59
  posting.save_without_validation
65
60
  account.save_without_validation
66
61
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acts_as_account
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
5
- prerelease: false
4
+ hash: 31
5
+ prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 1
9
- - 5
10
- version: 1.1.5
9
+ - 6
10
+ version: 1.1.6
11
11
  platform: ruby
12
12
  authors:
13
13
  - Thies C. Arntzen, Norman Timmler, Matthias Frick, Phillip Oertel
@@ -15,13 +15,42 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-11-15 00:00:00 +01:00
19
- default_executable:
18
+ date: 2012-03-12 00:00:00 Z
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
21
+ version_requirements: &id001 !ruby/object:Gem::Requirement
22
+ none: false
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ hash: 9
27
+ segments:
28
+ - 2
29
+ - 3
30
+ - 5
31
+ version: 2.3.5
22
32
  name: activerecord
23
33
  prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
34
+ type: :runtime
35
+ requirement: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ version_requirements: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ~>
41
+ - !ruby/object:Gem::Version
42
+ hash: 9
43
+ segments:
44
+ - 2
45
+ - 3
46
+ - 5
47
+ version: 2.3.5
48
+ name: actionpack
49
+ prerelease: false
50
+ type: :runtime
51
+ requirement: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ version_requirements: &id003 !ruby/object:Gem::Requirement
25
54
  none: false
26
55
  requirements:
27
56
  - - ">="
@@ -30,12 +59,92 @@ dependencies:
30
59
  segments:
31
60
  - 0
32
61
  version: "0"
62
+ name: database_cleaner
63
+ prerelease: false
33
64
  type: :runtime
34
- version_requirements: *id001
65
+ requirement: *id003
35
66
  - !ruby/object:Gem::Dependency
36
- name: actionpack
67
+ version_requirements: &id004 !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ~>
71
+ - !ruby/object:Gem::Version
72
+ hash: 3
73
+ segments:
74
+ - 2
75
+ - 3
76
+ - 0
77
+ version: 2.3.0
78
+ name: rspec
79
+ prerelease: false
80
+ type: :development
81
+ requirement: *id004
82
+ - !ruby/object:Gem::Dependency
83
+ version_requirements: &id005 !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ~>
87
+ - !ruby/object:Gem::Version
88
+ hash: 23
89
+ segments:
90
+ - 1
91
+ - 0
92
+ - 0
93
+ version: 1.0.0
94
+ name: bundler
37
95
  prerelease: false
38
- requirement: &id002 !ruby/object:Gem::Requirement
96
+ type: :development
97
+ requirement: *id005
98
+ - !ruby/object:Gem::Dependency
99
+ version_requirements: &id006 !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ~>
103
+ - !ruby/object:Gem::Version
104
+ hash: 7
105
+ segments:
106
+ - 1
107
+ - 5
108
+ - 2
109
+ version: 1.5.2
110
+ name: jeweler
111
+ prerelease: false
112
+ type: :development
113
+ requirement: *id006
114
+ - !ruby/object:Gem::Dependency
115
+ version_requirements: &id007 !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ~>
119
+ - !ruby/object:Gem::Version
120
+ hash: 51
121
+ segments:
122
+ - 0
123
+ - 9
124
+ - 4
125
+ version: 0.9.4
126
+ name: cucumber
127
+ prerelease: false
128
+ type: :development
129
+ requirement: *id007
130
+ - !ruby/object:Gem::Dependency
131
+ version_requirements: &id008 !ruby/object:Gem::Requirement
132
+ none: false
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ hash: 19
137
+ segments:
138
+ - 2
139
+ - 7
140
+ - 0
141
+ version: 2.7.0
142
+ name: mysql
143
+ prerelease: false
144
+ type: :development
145
+ requirement: *id008
146
+ - !ruby/object:Gem::Dependency
147
+ version_requirements: &id009 !ruby/object:Gem::Requirement
39
148
  none: false
40
149
  requirements:
41
150
  - - ">="
@@ -44,12 +153,26 @@ dependencies:
44
153
  segments:
45
154
  - 0
46
155
  version: "0"
156
+ name: activerecord
157
+ prerelease: false
47
158
  type: :runtime
48
- version_requirements: *id002
159
+ requirement: *id009
49
160
  - !ruby/object:Gem::Dependency
50
- name: database_cleaner
161
+ version_requirements: &id010 !ruby/object:Gem::Requirement
162
+ none: false
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ hash: 3
167
+ segments:
168
+ - 0
169
+ version: "0"
170
+ name: actionpack
51
171
  prerelease: false
52
- requirement: &id003 !ruby/object:Gem::Requirement
172
+ type: :runtime
173
+ requirement: *id010
174
+ - !ruby/object:Gem::Dependency
175
+ version_requirements: &id011 !ruby/object:Gem::Requirement
53
176
  none: false
54
177
  requirements:
55
178
  - - ">="
@@ -58,8 +181,10 @@ dependencies:
58
181
  segments:
59
182
  - 0
60
183
  version: "0"
184
+ name: database_cleaner
185
+ prerelease: false
61
186
  type: :runtime
62
- version_requirements: *id003
187
+ requirement: *id011
63
188
  description: acts_as_account implements double entry accounting for Rails models. Your models get accounts and you can do consistent transactions between them. Since the documentation is sparse, see the transfer.feature for usage examples.
64
189
  email: thieso@gmail.com
65
190
  executables: []
@@ -71,6 +196,8 @@ extra_rdoc_files:
71
196
  - README.rdoc
72
197
  files:
73
198
  - .specification
199
+ - Gemfile
200
+ - Gemfile.lock
74
201
  - LICENSE
75
202
  - README.rdoc
76
203
  - Rakefile
@@ -94,13 +221,12 @@ files:
94
221
  - lib/acts_as_account/manually_created_account.rb
95
222
  - lib/acts_as_account/posting.rb
96
223
  - lib/acts_as_account/transfer.rb
97
- has_rdoc: true
98
224
  homepage: http://github.com/betterplace/acts_as_account
99
225
  licenses: []
100
226
 
101
227
  post_install_message:
102
- rdoc_options:
103
- - --charset=UTF-8
228
+ rdoc_options: []
229
+
104
230
  require_paths:
105
231
  - lib
106
232
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -124,7 +250,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
124
250
  requirements: []
125
251
 
126
252
  rubyforge_project:
127
- rubygems_version: 1.3.7
253
+ rubygems_version: 1.8.17
128
254
  signing_key:
129
255
  specification_version: 3
130
256
  summary: acts_as_account implements double entry accounting for Rails models