easy_attributes 0.1.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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Allen Fair
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,156 @@
1
+ = easy_attributes
2
+
3
+ Easy Attributes is a Ruby DSL to give more control to attributes. Tested with ruby 1.87 & 1.9.2
4
+
5
+ == Usage
6
+
7
+ Easy Attributes works in any Ruby class, it does not require Active Record or other ORM.
8
+
9
+ First, install this from rubygems.org
10
+ gem install easy_attributes
11
+
12
+ Next, you need to configure your app to use it, by either:
13
+ require 'rubygems' # Rails application, no framework
14
+ require 'easy_attributes' #
15
+
16
+ config.gem 'easy_attributes' # Rails 2.x, in your ./config/environment.rb
17
+
18
+ gem 'easy_attributes' # Rails 3.x, in your ./Gemfile
19
+
20
+ Configuration:
21
+ EasyAttributes::Config.orm = :active_model # will generate some validations, etc.
22
+ EasyAttributes::Config.load(filename) # Loads a file of column/value/names/descriptions
23
+ EasyAttributes::define(attribute_name, {:name=>value, ...})
24
+
25
+ Declaration:
26
+
27
+ class Record < ObjectRelationalMapperOfChoice
28
+ include EasyAttributes
29
+
30
+ attr_values :field, :name=>'value', :name=>'value', ... :validate=>:active_record, :required=>true, :like=>'other' ...
31
+ attr_sequence :field, :name, :name, ... ,:start=>1, :step=>1
32
+ attr_money :amount, :units=>"dollars", :unit=>'$', :negative=>'%.2f CR'
33
+ attr_bytes :bandwidth
34
+ end
35
+
36
+ == attr_values
37
+
38
+ will define:
39
+ field() # => value ### if orm == :attr, attr_accessor will be installed for the attribute
40
+ field=(value) # => value
41
+ field_sym() # => :name
42
+ field_sym=(symbol) # => value
43
+ field_values() # => {:name=>value, ...}
44
+ field_is?(:name) # true if :name matches current value
45
+ field_is?(:name, :name, ...) # true if value matches a given symbol
46
+ field_is?(:greater_than, :name) # true if the value is greater than the value of :name
47
+ The first argument can be one of:
48
+ :greater_than, :gt, :greater_than_or_equal, :ge, :less_than, :lt, :less_than_or_equal, :le
49
+ field_is?(:between, :name1, :name2) # true if the value is between the corresponding values
50
+
51
+ == attr_sequence
52
+
53
+ sets up a attr_values with the sequential numbering, by default starting at 1 incrementing by 1
54
+
55
+ == attr_bytes
56
+
57
+ Creates helper fields to display and accept byte quantities as KB, MB, GB, TB, etc. Since a kilobyte (KB) is
58
+ now defined as 1000 bytes, and a kibibyte (KiB) is the 1024 quantity, you wan to configure which you want to use
59
+
60
+ EasyAttributes::Config.kb_size = 1000 # Uses KB units as 1KB = 1000 bytes
61
+ EasyAttributes::Config.kb_size = 1024 # Uses KB units as 1KB = 1024 bytes
62
+ EasyAttributes::Config.kb_size = :new # Uses KiB units as 1KiB = 1024 bytes (DEFAULT)
63
+ EasyAttributes::Config.kb_size = :both # Combines usage of KB (1000) and KiB (1024) units (Watch out!)
64
+
65
+ attr_bytes creates the following methods for a :bandwidth attribute
66
+
67
+ bandwidth_bytes = "1 KB" # => 1024
68
+ bandwidth_bytes # => "1 KB" using whatever unit is best
69
+ bandwidth_bytes(:MiB) # => "12345.67 MB" using the specified unit
70
+ bandwidth_bytes(:MiB, :precision=>0) # => "123 MB"
71
+
72
+ == attr_money
73
+
74
+ is the inclusion of my easy_money gem
75
+
76
+ Mixin the EasyAttributes module into the (model) class you need, and declare the
77
+ attributes (columns) you wish you have the attr_money helpers
78
+
79
+ class Ledger
80
+ include EasyAttributes
81
+ attr_accessor :amount, :euro # Integer value of cents
82
+ attr_money :amount # Creates amount_money() and amount_money=() methods
83
+ attr_money :amount, :units=>"dollars", :unit=>'$', :negative=>'%.2f CR'
84
+ # Creates amount_dollars() and amount_dollars=() methods
85
+ end
86
+
87
+ ledger = Ledger.new
88
+ ledger.amount = 100 # 100 cents = $1.00
89
+ ledger.amount_money #=> "1.00"
90
+ ledger.amount_money = "-123.45"
91
+ ledger.amount #=> -12345 (cents)
92
+ ledger.amount_money(:negative=>'%.2f CR', :zero=>'Free') # Uses these formats
93
+ ledger.amount_dollars #=> "$123.45 CR"
94
+
95
+ # Track the bets of the Gamesters of Triskelion on their drill thrall competitions.
96
+ class ProviderWagers < ActiveRecord::Base
97
+ include EasyAttributes
98
+ attr_money :quatloos, :units=>'quatloos', :precision=>3,
99
+ :zero=>'even', :nil=>'no bet', :negative=>'%.3f Loss', :unit=>'Q',
100
+ :negative_regex=>/^(-?)(.+\d)\s*Loss/i
101
+ # creates amount_quatloos(), amount_quatloos=()
102
+ end
103
+
104
+ # in your views
105
+ <%= f.text_field :amount_quatloos %> # -12000 => "Q12.000 Loss"
106
+
107
+ Options for attr_money calls:
108
+ * :money_method - Use this as the alternative name to the money-access methods
109
+ * :units - Use this as an alternative suffix name to the money methods ('dollars' gives 'xx_dollars')
110
+ * :precision - The number of digits implied after the decimal, default is 2
111
+ * :separator - The character to use after the integer part, default is '.'
112
+ * :delimiter - The character to use between every 3 digits of the integer part, default none
113
+ * :positive - The sprintf format to use for positive numbers, default is based on precision
114
+ * :negative - The sprintf format to use for negative numbers, default is same as :positive
115
+ * :zero - The sprintf format to use for zero, default is same as :positive
116
+ * :nil - The sprintf format to use for nil values, default none
117
+ * :unit - Prepend this to the front of the money value, say '$', default none
118
+ * :blank - Return this value when the money string is empty or has no digits on assignment
119
+ * :negative_regex - A Regular Expression used to determine if a number is negative (and without a - sign), defaults to having a "CR" after the number
120
+
121
+ === Formatters
122
+ You can also call or build your own custom conversions. Ensure that
123
+ you can convert between the integer and money forms if you need to.
124
+
125
+ The "money" type is a string, suitable for human editing, and will convert back into
126
+ integer type. If you override the formatting options, test that your money result
127
+ string will convert properly back to the original integer value.
128
+
129
+ include EasyAttributes
130
+ ...
131
+ puts EasyAttributes.money_to_integer( money_string, :option=>value, ... )
132
+ puts EasyAttributes.integer_to_money( cents_integer, :option=>value, ... )
133
+ puts EasyAttributes.integer_to_float( cents_integer, :option=>value, ... )
134
+ puts EasyAttributes.float_to_integer( money_float, :option=>value, ... )
135
+
136
+ EasyAttributes.integer_to_float( nil, blank:0 ) #=> 0.0 [Ruby 1.9.1 Syntax]
137
+ EasyAttributes.integer_to_float( 12345, :precision=>3 ) #=> 12.345
138
+ EasyAttributes.float_to_integer(12.345111, :precision=>3 ) #=> 12345
139
+
140
+ The options to these methods are the same as the #attr_money declarations
141
+
142
+
143
+ == Note on Patches/Pull Requests
144
+
145
+ * Fork the project.
146
+ * Make your feature addition or bug fix.
147
+ * Add tests for it. This is important so I don't break it in a
148
+ future version unintentionally.
149
+ * Commit, do not mess with rakefile, version, or history.
150
+ (if you want to have your own version, that is fine but
151
+ bump version in a commit by itself I can ignore when I pull)
152
+ * Send me a pull request. Bonus points for topic branches.
153
+
154
+ == Copyright
155
+
156
+ Copyright (c) 2010 Allen Fair. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,52 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "easy_attributes"
8
+ gem.summary = %Q{Easy Attributes for Ruby}
9
+ gem.description = %Q{Easy Attributes is a Ruby DSL to give more control to attributes.}
10
+ gem.email = "allen.fair@gmail.com"
11
+ gem.homepage = "http://github.com/afair/easy_attributes"
12
+ gem.authors = ["Allen Fair"]
13
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
+ end
15
+ Jeweler::GemcutterTasks.new
16
+ rescue LoadError
17
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
18
+ end
19
+
20
+ require 'rake/testtask'
21
+ Rake::TestTask.new(:test) do |test|
22
+ test.libs << 'lib' << 'test'
23
+ test.pattern = 'test/**/test_*.rb'
24
+ test.verbose = true
25
+ end
26
+
27
+ begin
28
+ require 'rcov/rcovtask'
29
+ Rcov::RcovTask.new do |test|
30
+ test.libs << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+ rescue LoadError
35
+ task :rcov do
36
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
37
+ end
38
+ end
39
+
40
+ task :test => :check_dependencies
41
+
42
+ task :default => :test
43
+
44
+ require 'rake/rdoctask'
45
+ Rake::RDocTask.new do |rdoc|
46
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
47
+
48
+ rdoc.rdoc_dir = 'rdoc'
49
+ rdoc.title = "easy_attributes #{version}"
50
+ rdoc.rdoc_files.include('README*')
51
+ rdoc.rdoc_files.include('lib/**/*.rb')
52
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,51 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{easy_attributes}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Allen Fair"]
12
+ s.date = %q{2010-04-28}
13
+ s.description = %q{Easy Attributes is a Ruby DSL to give more control to attributes.}
14
+ s.email = %q{allen.fair@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "easy_attributes.gemspec",
27
+ "lib/easy_attributes.rb",
28
+ "test/helper.rb",
29
+ "test/test_easy_attributes.rb"
30
+ ]
31
+ s.homepage = %q{http://github.com/afair/easy_attributes}
32
+ s.rdoc_options = ["--charset=UTF-8"]
33
+ s.require_paths = ["lib"]
34
+ s.rubygems_version = %q{1.3.6}
35
+ s.summary = %q{Easy Attributes for Ruby}
36
+ s.test_files = [
37
+ "test/helper.rb",
38
+ "test/test_easy_attributes.rb"
39
+ ]
40
+
41
+ if s.respond_to? :specification_version then
42
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
43
+ s.specification_version = 3
44
+
45
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
46
+ else
47
+ end
48
+ else
49
+ end
50
+ end
51
+
@@ -0,0 +1,364 @@
1
+ # Mixes in attr_* definitions into ruby classes for using symbolic names as values, money, and bytes
2
+ module EasyAttributes
3
+
4
+ def self.included(base) #:nodoc:
5
+ base.extend( ClassMethods )
6
+ #EasyAttributes::Config
7
+ #puts "easy_attributes included into #{base}"
8
+ end
9
+
10
+ # Configuration class for EasyAttributes, set at load time.
11
+ class Config
12
+ @@orm = :attr # :attr, :active_model
13
+ @@attributes = {}
14
+ @@values = {}
15
+ @@kb_size=:iec
16
+
17
+ # Values: :old, :jedec, or 1024 uses KB=1024
18
+ # :new, :iec uses KiB=1024, no KB
19
+ # 1000, :decimal uses only KB (1000) (other values mix KB and KiB units)
20
+ def self.kb_size=(b)
21
+ @@kb_size = b
22
+ end
23
+
24
+ # Returns the kb_size setting
25
+ def self.kb_size
26
+ @@kb_size
27
+ end
28
+
29
+ # Set the ORM or attribute manager, currently to :attr (attr_accessor) or :active_model
30
+ def self.orm=(o)
31
+ @@orm = o
32
+ end
33
+
34
+ # Returns the ORM setting
35
+ def self.orm
36
+ @@orm
37
+ end
38
+
39
+ # Defines a symbol name to a hash of :name=>value
40
+ def self.define(name, hash)
41
+ @@attributes[name] = hash
42
+ end
43
+
44
+ # Returns the symbol table
45
+ def self.attributes
46
+ @@attributes
47
+ end
48
+
49
+ # Loads a tab-delimted filename into the symbol table in format:
50
+ # attribute value role symbolic_name short_description long_description (useful for "<option>" data)
51
+ # If a block is given, it should parse a file line and return an array of
52
+ # [attribute, value, role*, name, short*, long_description*], *denotes not required, but placeholder required
53
+ def self.load(filename)
54
+ File.open(filename).each do |r|
55
+ next unless r =~ /^\w/
56
+ (col, val, priv, symbol, word, desc) = block_given? ? yield(r) : r.split(/\t/)
57
+ next if desc.nil? || desc.empty? || symbol.empty? || word.empty? || symbol.nil?
58
+ col = col.to_sym
59
+ @@values[col] = {:sym=>{}, :val=>{}, :rec=>{}} unless @@values.has_key?(col)
60
+ @@values[col][:sym][symbol.to_sym] = val.to_i
61
+ @@values[col][:val][val] = symbol.to_sym
62
+ @@values[col][:rec][symbol.to_sym] = {:word=>word, :description=>desc, :value=>val.to_i}
63
+ @@attributes[col.to_s] ||= {}
64
+ @@attributes[col.to_s][symbol.to_sym] = val.to_i
65
+ end
66
+ end
67
+ end
68
+
69
+ module ClassMethods
70
+ # Defines an attribute as a sequence of names. :name1=>1, :name2=>2, etc.
71
+ # attr_sequence :attr, :name, ... :start=>n, :step=>:n
72
+ def attr_sequence(attribute, *names)
73
+ opt = EasyAttributes.pop_options(names)
74
+ values = {}
75
+ names.inject(opt[:start]||1) { |seq, n| values[n]=seq; seq+=(opt[:step]||1)}
76
+ attr_values( attribute, values, opt)
77
+ end
78
+
79
+ # Defines an attribute as a hash like {:key=>value,...} where key names are used interchangably with values
80
+ def attr_values(attribute, *args)
81
+ opt = args.size > 1 ? args.pop : {}
82
+ hash = args.first
83
+
84
+ # Use :like=>colname to copy from another definition (ClassName#attribute) or from the loaded table columns
85
+ if opt[:like]
86
+ hash = EasyAttributes::Config.attributes[opt[:like]]
87
+ end
88
+
89
+ name = "#{self.name}##{attribute}"
90
+ EasyAttributes.add_definition( name, hash)
91
+ code = ''
92
+ if EasyAttributes::Config.orm == :active_model
93
+ validates_inclusion_of attribute, :in=>hash.values
94
+ else
95
+ attr_accessor attribute
96
+ end
97
+ code += %Q(
98
+ def #{attribute}_sym=(v)
99
+ self.#{attribute} = EasyAttributes.value_for_sym("#{name}", v)
100
+ end
101
+ def #{attribute}_sym
102
+ EasyAttributes.sym_for_value("#{name}", #{attribute})
103
+ end
104
+ def self.#{attribute}_values
105
+ EasyAttributes::Config.attributes["#{name}"]
106
+ end
107
+ def #{attribute}_is?(*args)
108
+ EasyAttributes.value_is?("#{name}", attribute, *args)
109
+ end
110
+ )
111
+ #puts code
112
+ class_eval code
113
+ end
114
+
115
+ # attr_bytes allows manipultion and display as kb, mb, gb, tb, pb
116
+ # Adds method: attribute_bytes=() and attribute_bytes(:unit, :option=>value )
117
+ def attr_bytes(attribute, *args)
118
+ name = "#{self.name}##{attribute}"
119
+ opt = EasyAttributes.pop_options(args)
120
+ #getter, setter = EasyAttributes.getter_setter(attribute)
121
+ attr_accessor attribute if EasyAttributes::Config.orm == :attr
122
+ code = %Q(
123
+ def #{attribute}_bytes(*args)
124
+ args.size == 0 ? v : EasyAttributes::format_bytes(self.#{attribute}, *args)
125
+ end
126
+ def #{attribute}_bytes=(v)
127
+ self.#{attribute} = EasyAttributes.parse_bytes(v)
128
+ end
129
+ )
130
+ #puts code
131
+ class_eval code
132
+ end
133
+
134
+ # Creates an money instance method for the given method, named "#{attribute}_money" which returns
135
+ # a formatted money string, and a #{attribute}_money= method used to set an edited money string.
136
+ # The original method stores the value as integer (cents, or other precision/currency setting). Options:
137
+ # * :money_method - Use this as the alternative name to the money-access methods
138
+ # * :units - Use this as an alternative suffix name to the money methods ('dollars' gives 'xx_dollars')
139
+ # * :precision - The number of digits implied after the decimal, default is 2
140
+ # * :separator - The character to use after the integer part, default is '.'
141
+ # * :delimiter - The character to use between every 3 digits of the integer part, default none
142
+ # * :positive - The sprintf format to use for positive numbers, default is based on precision
143
+ # * :negative - The sprintf format to use for negative numbers, default is same as :positive
144
+ # * :zero - The sprintf format to use for zero, default is same as :positive
145
+ # * :nil - The sprintf format to use for nil values, default none
146
+ # * :unit - Prepend this to the front of the money value, say '$', default none
147
+ # * :blank - Return this value when the money string is empty or has no digits on assignment
148
+ # * :negative_regex - A Regular Expression used to determine if a number is negative (and without a - sign)
149
+ #
150
+ def attr_money(attribute, *args)
151
+ opt = args.last.is_a?(Hash) ? args.pop : {}
152
+ money_method = opt.delete(:money_method) || "#{attribute}_#{opt.delete(:units)||'money'}"
153
+
154
+ class_eval %Q(
155
+ def #{money_method}(*args)
156
+ opt = args.last.is_a?(Hash) ? args.pop : {}
157
+ EasyAttributes.integer_to_money( #{attribute}, #{opt.inspect}.merge(opt))
158
+ end
159
+
160
+ def #{money_method}=(v, *args)
161
+ opt = args.last.is_a?(Hash) ? args.pop : {}
162
+ self.#{attribute} = EasyAttributes.money_to_integer( v, #{opt.inspect}.merge(opt))
163
+ end
164
+ )
165
+ end
166
+
167
+ end
168
+
169
+ # Returns a [getter_code, setter_code] depending on the orm configuration
170
+ def self.getter_setter(attribute)
171
+ if EasyAttributes::Config.orm == :active_model
172
+ ["self.attributes[:#{attribute}]", "self.write_attribute(:#{attribute}, v)"]
173
+ else
174
+ ["@#{attribute}", "@#{attribute} = v"]
175
+ end
176
+ end
177
+
178
+ def self.pop_options(args, defaults={})
179
+ args.last.is_a?(Hash) ? defaults.merge(args.pop) : defaults
180
+ end
181
+
182
+ def self.add_definition(attribute, hash, opt={})
183
+ EasyAttributes::Config.define attribute, hash
184
+ end
185
+
186
+ ##############################################################################
187
+ # attr_values helpers
188
+ ##############################################################################
189
+
190
+ # Returns the defined value for the given symbol and attribute
191
+ def self.value_for_sym(attribute, sym)
192
+ EasyAttributes::Config.attributes[attribute][sym]
193
+ end
194
+
195
+ # Returns the defined symbol for the given value on the attribute
196
+ def self.sym_for_value(attribute, value)
197
+ EasyAttributes::Config.attributes[attribute].each {|k,v| return k if v==value}
198
+ end
199
+
200
+ def self.value_is?(attribute, value, *args)
201
+ case args.first
202
+ when :between then
203
+ value >= EasyAttributes::Config.attributes[attribute][args[1]] && value <= EasyAttributes::Config.attributes[attribute][args[2]]
204
+ when :gt, :greater_than then
205
+ value > EasyAttributes::Config.attributes[attribute][args[1]]
206
+ when :ge, :greater_than_or_equal_to then
207
+ value >= EasyAttributes::Config.attributes[attribute][args[1]]
208
+ when :lt, :less_than then
209
+ value < EasyAttributes::Config.attributes[attribute][args[1]]
210
+ when :le, :less_than_or_equal_to then
211
+ value <= EasyAttributes::Config.attributes[attribute][args[1]]
212
+ #when :not, :not_in
213
+ # ! args.include? EasyAttributes::Config.attributes[attribute].keys
214
+ else
215
+ args.include? EasyAttributes.sym_for_value(v)
216
+ end
217
+ end
218
+
219
+ ##############################################################################
220
+ # attr_byte helpers
221
+ ##############################################################################
222
+
223
+ # Official Definitions for kilobyte and kibibyte quantity prefixes
224
+ KB =1000; MB =KB **2; GB= KB **3; TB =KB **4; PB =KB **5; EB =KB **6; ZB =KB **7; YB =KB **8
225
+ KiB=1024; MiB=KiB**2; GiB=KiB**3; TiB=KiB**4; PiB=KiB**5; EiB=KiB**6; ZiB=KiB**7; YiB=KiB**8
226
+ DECIMAL_PREFIXES = {:B=>1, :KB=>KB, :MB=>MB, :GB=>GB, :TB=>TB, :PB=>PB, :EB=>EB, :ZB=>ZB, :TB=>YB}
227
+ BINARY_PREFIXES = {:B=>1, :KiB=>KiB, :MiB=>MiB, :GiB=>GiB, :TiB=>TiB, :PiB=>PiB, :EiB=>EiB, :ZiB=>ZiB, :TiB=>YiB}
228
+
229
+ # Returns a hash of prefix names to decimal quantities for the given setting
230
+ def self.byte_prefixes(kb_size=0)
231
+ case kb_size
232
+ when 1000, :decimal, :si then DECIMAL_PREFIXES
233
+ when :old, :jedec, 1024 then {:KB=>KiB, :MB=>MiB, :GB=>GiB, :TB=>TiB, :PB=>PiB, :EB=>EiB, :ZB=>ZiB, :TB=>YiB}
234
+ when :new, :iec then BINARY_PREFIXES
235
+ else DECIMAL_PREFIXES.merge(BINARY_PREFIXES) # Both? What's the least surprise?
236
+ end
237
+ end
238
+
239
+ # Takes a number of bytes and an optional :unit argument and :precision option, returns a formatted string of units
240
+ def self.format_bytes(v, *args)
241
+ opt = EasyAttributes.pop_options(args, :precision=>3)
242
+ prefixes = EasyAttributes.byte_prefixes(opt[:kb_size]||EasyAttributes::Config.kb_size||0)
243
+ if args.size > 0 && args.first.is_a?(Symbol)
244
+ (unit, precision) = args
245
+ v = "%.#{precision||opt[:precision]}f" % (1.0 * v / (prefixes[unit]||1))
246
+ return "#{v} #{unit}"
247
+ else
248
+ precision = args.shift || opt[:precision]
249
+ prefixes.sort{|a,b| a[1]<=>b[1]}.reverse.each do |pv|
250
+ next if pv[1] > v
251
+ v = "%f.10f" % (1.0 * v / pv[1])
252
+ v = v[0,precision+1] if v =~ /^(\d)+\.(\d+)/ && v.size > (precision+1)
253
+ v.gsub!(/\.0*$/, '')
254
+ return "#{v} #{pv[0]}"
255
+ end
256
+ end
257
+ v.to_s
258
+ end
259
+
260
+ # Takes a string of "number units" or Array of [number, :units] and returns the number of bytes represented.
261
+ def self.parse_bytes(v, *args)
262
+ opt = EasyAttributes.pop_options(args, :precision=>3)
263
+ # Handle v= [100, :KB]
264
+ if v.is_a?(Array)
265
+ bytes = v.shift
266
+ v = "#{bytes} #{v.shift}"
267
+ else
268
+ bytes = v.to_f
269
+ end
270
+
271
+ if v.downcase =~ /^\s*(?:[\d\.]+)\s*([kmgtpezy]i?b)/i
272
+ units = ($1.size==2 ? $1.upcase : $1[0,1].upcase+$1[1,1]+$1[2,1].upcase).to_sym
273
+ prefixes = EasyAttributes.byte_prefixes(opt[:kb_size]||EasyAttributes::Config.kb_size||0)
274
+ bytes *= prefixes[units] if prefixes.has_key?(units)
275
+ #puts "v=#{v} b=#{bytes} u=#{units} #{units.class} bp=#{prefixes[units]} kb=#{opt[:kb_size]} P=#{prefixes.inspect}"
276
+ end
277
+ (bytes*100).to_i/100
278
+ end
279
+
280
+ ##############################################################################
281
+ # attr_money helpers
282
+ ##############################################################################
283
+
284
+ # Returns the money string of the given integer value. Uses relevant options from #easy_money
285
+ def self.integer_to_money(value, *args)
286
+ opt = args.last.is_a?(Hash) ? args.pop : {}
287
+ opt[:positive] ||= "%.#{opt[:precision]||2}f"
288
+ pattern =
289
+ if value.nil?
290
+ value = 0
291
+ opt[:nil] || opt[:positive]
292
+ else
293
+ case value <=> 0
294
+ when 1 then opt[:positive]
295
+ when 0 then opt[:zero] || opt[:positive]
296
+ else
297
+ value = -value if opt[:negative] && opt[:negative] != opt[:positive]
298
+ opt[:negative] || opt[:positive]
299
+ end
300
+ end
301
+ value = self.format_money( value, pattern, opt)
302
+ value = opt[:unit]+value if opt[:unit]
303
+ value.gsub!(/\./,opt[:separator]) if opt[:separator]
304
+ if opt[:delimiter] && (m = value.match(/^(\D*)(\d+)(.*)/))
305
+ # Adapted From Rails' ActionView::Helpers::NumberHelper
306
+ n = m[2].gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{opt[:delimiter]}")
307
+ value=m[1]+n+m[3]
308
+ end
309
+ value
310
+ end
311
+
312
+ def self.integer_to_float(value, *args)
313
+ opt = args.last.is_a?(Hash) ? args.pop : {}
314
+ return (opt[:blank]||nil) if value.nil?
315
+ value = 1.0 * value / (10**(opt[:precision]||2))
316
+ end
317
+
318
+ # Returns the integer of the given money string. Uses relevant options from #easy_money
319
+ def self.money_to_integer(value, *args)
320
+ opt = args.last.is_a?(Hash) ? args.pop : {}
321
+ value = value.gsub(opt[:delimiter],'') if opt[:delimiter]
322
+ value = value.gsub(opt[:separator],'.') if opt[:separator]
323
+ value = value.gsub(/^[^\d\.\-\,]+/,'')
324
+ return (opt[:blank]||nil) unless value =~ /\d/
325
+ m = value.to_s.match(opt[:negative_regex]||/^(-?)(.+\d)\s*cr/i)
326
+ value = value.match(/^-/) ? m[2] : "-#{m[2]}" if m && m[2]
327
+
328
+ # Money string ("123.45") to proper integer withough passing through the float transformation
329
+ match = value.match(/(-?\d*)\.?(\d*)/)
330
+ return 0 unless match
331
+ value = match[1].to_i * (10 ** (opt[:precision]||2))
332
+ cents = match[2]
333
+ cents = cents + '0' while cents.length < (opt[:precision]||2)
334
+ cents = cents.to_s[0,opt[:precision]||2]
335
+ value += cents.to_i * (value<0 ? -1 : 1)
336
+ value
337
+ end
338
+
339
+ # Returns the integer (cents) value from a Float
340
+ def self.float_to_integer(value, *args)
341
+ opt = args.last.is_a?(Hash) ? args.pop : {}
342
+ return (opt[:blank]||nil) if value.nil?
343
+ value = (value.to_f*(10**((opt[:precision]||2)+1))).to_i/10 # helps rounding 4.56 -> 455 ouch!
344
+ end
345
+
346
+ # Replacing the sprintf function to deal with money as float. "... %[flags]m ..."
347
+ def self.format_money(value, pattern="%.2m", *args)
348
+ opt = args.last.is_a?(Hash) ? args.pop : {}
349
+ sign = value < 0 ? -1 : 1
350
+ dollars, cents = value.abs.divmod( 10 ** (opt[:precision]||2))
351
+ dollars *= sign
352
+ parts = pattern.match(/^(.*)%([-\. \d+0]*)[fm](.*)/)
353
+ return pattern unless parts
354
+ intdec = parts[2].match(/(.*)\.(\d*)/)
355
+ dprec, cprec = intdec ? [intdec[1], intdec[2]] : ['', '']
356
+ dollars = sprintf("%#{dprec}d", dollars)
357
+ cents = '0' + cents.to_s while cents.to_s.length < (opt[:precision]||2)
358
+ cents = cents.to_s[0,cprec.to_i]
359
+ cents = cents + '0' while cents.length < cprec.to_i
360
+ value = parts[1] + "#{(dollars.to_i==0 && sign==-1) ? '-' : '' }#{dollars}#{cents>' '? '.':''}#{cents}" + parts[3]
361
+ value
362
+ end
363
+
364
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ require 'easy_attributes'
7
+
8
+ class Test::Unit::TestCase
9
+ end
10
+ require 'rubygems'
11
+ require 'test/unit'
12
+
13
+
14
+ class Sample
15
+ include EasyAttributes
16
+ attr_accessor :price, :balance
17
+ attr_money :price
18
+ attr_money :balance
19
+ end
@@ -0,0 +1,147 @@
1
+ require 'helper'
2
+ EasyAttributes::Config.orm = :attr
3
+
4
+ class TestEasyAttributes < Test::Unit::TestCase
5
+ include EasyAttributes
6
+ attr_sequence :tas, :n1, :n2
7
+ attr_values :tav, :k1=>1, :k2=>2, :k3=>3
8
+ attr_values :status, {}, :like=>'TestEasyAttributes#tav'
9
+ attr_bytes :bw
10
+ attr_money :amount
11
+
12
+ def test_attr_sequence
13
+ self.tas_sym = :n1
14
+ assert_equal self.tas, 1
15
+ assert_equal self.tas_sym, :n1
16
+ end
17
+
18
+ def test_attr_values
19
+ self.tav_sym = :k1
20
+ assert_equal tav, 1
21
+ self.tav_sym = :k2
22
+ assert_equal tav, 2
23
+ assert_equal tav_sym, :k2
24
+ end
25
+
26
+ def test_like
27
+ self.status_sym = :k1
28
+ assert_equal self.status, 1
29
+ end
30
+
31
+ # Removed for now, not shipping my data file!
32
+ def test_load
33
+ #EasyAttributes::Config.load "values"
34
+ #assert_equal EasyAttributes::Config.attributes['status'][:ok], 8
35
+ end
36
+
37
+ def test_attr_bytes
38
+ self.bw = 1024
39
+ assert_equal bw, 1024
40
+ assert_equal bw_bytes(:KiB, :precision=>0), "1 KiB"
41
+ self.bw = [1, :kb]
42
+ end
43
+
44
+ def test_format_bytes
45
+ EasyAttributes::Config.kb_size = :both
46
+ assert_equal EasyAttributes.format_bytes( 900 ), "900 B"
47
+ assert_equal EasyAttributes.format_bytes( 1000 ), "1 KB"
48
+ assert_equal EasyAttributes.format_bytes( 12345 ), "12 KiB"
49
+ assert_equal EasyAttributes.format_bytes( 123456789 ), "117 MiB"
50
+ assert_equal EasyAttributes.format_bytes( 9999999999 ), "9.31 GiB"
51
+ assert_equal EasyAttributes.format_bytes( 123456789, :KiB ), "120563.271 KiB"
52
+ assert_equal EasyAttributes.format_bytes( 123456789, :KiB, 1 ), "120563.3 KiB"
53
+ assert_equal EasyAttributes.format_bytes( 123456789, :KiB, 0 ), "120563 KiB"
54
+ end
55
+
56
+ def test_parse_bytes
57
+ EasyAttributes::Config.kb_size=:both
58
+ assert_equal EasyAttributes.parse_bytes( "1.5 KiB" ), 1536
59
+ assert_equal EasyAttributes.parse_bytes( "1 gb" ), EasyAttributes::GB
60
+ assert_equal EasyAttributes.parse_bytes( "1kb", :kb_size=>1000 ), 1000
61
+ assert_equal EasyAttributes.parse_bytes( "1kb", :kb_size=>1024 ), 1024
62
+ end
63
+
64
+ def test_include
65
+ sample = Sample.new
66
+ flunk "no price_money method" unless sample.respond_to?(:price_money)
67
+ flunk "no price_money= method" unless sample.respond_to?(:price_money=)
68
+ end
69
+
70
+ def test_method_money
71
+ s = Sample.new
72
+ [ 10000, 123456, 0, -1 -9876 ].each do |p|
73
+ s.price = p
74
+ m = s.price_money
75
+ s.price_money = m
76
+ flunk "Assignment error: p=#{p} m=#{m} price=#{s.price}" unless s.price = p
77
+ end
78
+ end
79
+
80
+ def test_method_money=
81
+ s = Sample.new
82
+ { "0.00"=>0, "12.34"=>1234, "-1.2345"=>-123, "12"=>1200, "4.56CR"=>-456 }.each do |m,p|
83
+ s.price_money = m
84
+ flunk "Assignment error: p=#{p} m=#{m} price=#{s.price}" unless s.price = p
85
+ end
86
+ end
87
+
88
+ def test_integer_to_money
89
+ assert EasyAttributes.integer_to_money(123) == '1.23'
90
+ assert EasyAttributes.integer_to_money(-12333) == '-123.33'
91
+ assert EasyAttributes.integer_to_money(0) == '0.00'
92
+ assert EasyAttributes.integer_to_money(nil, :nil=>'?') == '?'
93
+ assert EasyAttributes.integer_to_money(-1, :negative=>'%.2f CR') == '0.01 CR'
94
+ assert EasyAttributes.integer_to_money(0, :zero=>'free') == 'free'
95
+ assert EasyAttributes.integer_to_money(100, :unit=>'$') == '$1.00'
96
+ assert EasyAttributes.integer_to_money(100, :separator=>',') == '1,00'
97
+ assert EasyAttributes.integer_to_money(12345678900, :separator=>',', :delimiter=>'.') == '123.456.789,00'
98
+ assert EasyAttributes.integer_to_money(333, :precision=>3) == '0.333'
99
+ assert EasyAttributes.integer_to_money(111, :precision=>1) == '11.1'
100
+ assert EasyAttributes.integer_to_money(111, :precision=>0) == '111'
101
+ end
102
+
103
+ def test_money_to_integer
104
+ assert EasyAttributes.money_to_integer('1.23' ) == 123
105
+ assert EasyAttributes.money_to_integer('0.00' ) == 0
106
+ assert EasyAttributes.money_to_integer('-1.23' ) == -123
107
+ assert EasyAttributes.money_to_integer('1.23 CR' ) == -123
108
+ assert EasyAttributes.money_to_integer('$-2.34 CR' ) == 234
109
+ assert EasyAttributes.money_to_integer(' 1.234' ) == 123
110
+ assert EasyAttributes.money_to_integer('$1' ) == 100
111
+ assert EasyAttributes.money_to_integer('1' ) == 100
112
+ assert EasyAttributes.money_to_integer('' ) == nil
113
+ assert EasyAttributes.money_to_integer('1,00', :separator=>',',:delimiter=>'.') == 100
114
+ assert EasyAttributes.money_to_integer('$123.456.789,00 CR', :separator=>',',:delimiter=>'.') == -12345678900
115
+ assert EasyAttributes.money_to_integer('4.44', :precision=>4 ) == 44400
116
+ assert EasyAttributes.money_to_integer('4.44', :precision=>0 ) == 4
117
+ end
118
+
119
+ def test_float_to_integer
120
+ assert EasyAttributes.float_to_integer(1.00 ) == 100
121
+ assert EasyAttributes.float_to_integer(1.001 ) == 100
122
+ assert EasyAttributes.float_to_integer(-1.23 ) == -123
123
+ assert EasyAttributes.float_to_integer(9.0 ) == 900
124
+ assert EasyAttributes.float_to_integer(nil ) == nil
125
+ assert EasyAttributes.float_to_integer(0.00 ) == 0
126
+ end
127
+
128
+ def test_integer_to_float
129
+ assert EasyAttributes.integer_to_float(1 ) == 0.01
130
+ assert EasyAttributes.integer_to_float(0 ) == 0.0
131
+ assert EasyAttributes.integer_to_float(-100 ) == -1.00
132
+ assert EasyAttributes.integer_to_float(nil ) == nil
133
+ assert EasyAttributes.integer_to_float(9999888, :precision=>3 ) == 9999.888
134
+ end
135
+
136
+ def test_format_money
137
+ assert EasyAttributes.format_money(12345) == '123.45'
138
+ assert EasyAttributes.format_money(12345, "%07.2m") == '0000123.45'
139
+ assert EasyAttributes.format_money(12345, "%07.3m") == '0000123.450'
140
+ assert EasyAttributes.format_money(12345, "%m") == '123'
141
+ assert EasyAttributes.format_money(12345, "free") == 'free'
142
+ assert EasyAttributes.format_money(-12345) == '-123.45'
143
+ assert EasyAttributes.format_money(-12345, "%07.1m") == '-000123.4'
144
+ assert EasyAttributes.format_money(-1) == '-0.01'
145
+ end
146
+
147
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: easy_attributes
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Allen Fair
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-28 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Easy Attributes is a Ruby DSL to give more control to attributes.
22
+ email: allen.fair@gmail.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - LICENSE
29
+ - README.rdoc
30
+ files:
31
+ - .document
32
+ - .gitignore
33
+ - LICENSE
34
+ - README.rdoc
35
+ - Rakefile
36
+ - VERSION
37
+ - easy_attributes.gemspec
38
+ - lib/easy_attributes.rb
39
+ - test/helper.rb
40
+ - test/test_easy_attributes.rb
41
+ has_rdoc: true
42
+ homepage: http://github.com/afair/easy_attributes
43
+ licenses: []
44
+
45
+ post_install_message:
46
+ rdoc_options:
47
+ - --charset=UTF-8
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ segments:
55
+ - 0
56
+ version: "0"
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ requirements: []
65
+
66
+ rubyforge_project:
67
+ rubygems_version: 1.3.6
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: Easy Attributes for Ruby
71
+ test_files:
72
+ - test/helper.rb
73
+ - test/test_easy_attributes.rb