easy_attributes 0.1.3 → 0.2.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.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/README.md +36 -0
- data/Rakefile +6 -48
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/easy_attributes.gemspec +27 -46
- data/easy_attributes/.gitignore +9 -0
- data/easy_attributes/.travis.yml +4 -0
- data/lib/easy_attributes.rb +1002 -340
- data/lib/easy_attributes/version.rb +3 -0
- metadata +83 -60
- data/LICENSE +0 -20
- data/README.rdoc +0 -156
- data/VERSION +0 -1
- data/test/helper.rb +0 -19
- data/test/test_easy_attributes.rb +0 -151
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 31d92f9d96aec2da5924fffa386bef56751300bf
|
4
|
+
data.tar.gz: 7640d424ea0cd14b3ce0bf5a03e23a4f92a39603
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 67eeae0dbc1159032d636511a332ba950867c44af7bf8ad6f38473e1140ad08ee0f150c3bd156b1b070eced708241867c31980e0a515dcf4ffce5e592d66288b
|
7
|
+
data.tar.gz: 3bdc3eda298dfab2fc15d249f75f007c2565990b7f4dcfd6604d636cdb77415179ee2666d316dc8ed055b1038d1c8fac3608ca9e666717c3ec1e18eedc0a8d02
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# EasyAttributes
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/easy_attributes`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'easy_attributes'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install easy_attributes
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
TODO: Write usage instructions here
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
|
+
|
31
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/easy_attributes.
|
36
|
+
|
data/Rakefile
CHANGED
@@ -1,52 +1,10 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rake/testtask"
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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"
|
4
|
+
Rake::TestTask.new(:test) do |t|
|
5
|
+
t.libs << "test"
|
6
|
+
t.libs << "lib"
|
7
|
+
t.test_files = FileList['test/**/*_test.rb']
|
18
8
|
end
|
19
9
|
|
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
10
|
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/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "easy_attributes"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/easy_attributes.gemspec
CHANGED
@@ -1,51 +1,32 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'easy_attributes/version'
|
5
5
|
|
6
|
-
Gem::Specification.new do |
|
7
|
-
|
8
|
-
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "easy_attributes"
|
8
|
+
spec.version = EasyAttributes::VERSION
|
9
|
+
spec.authors = ["Allen Fair"]
|
10
|
+
spec.email = ["allen.fair@gmail.com"]
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
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.7}
|
35
|
-
s.summary = %q{Easy Attributes for Ruby}
|
36
|
-
s.test_files = [
|
37
|
-
"test/helper.rb",
|
38
|
-
"test/test_easy_attributes.rb"
|
39
|
-
]
|
12
|
+
spec.summary = %q{Easy Attributes for Ruby: Enum, Bytes, Money, float-as-integer views and forms}
|
13
|
+
spec.description = %q{Easy Attributes is a Ruby DSL to give more control to attributes. It provides a unique attribute enum setup, and conversions to bytes and float-as-integer (money, frequencies, Ratings, etc.) / fixed-decimal precision as integer (See the easy_money gem).}
|
14
|
+
spec.homepage = "https://github.com/afair/easy_attributes"
|
40
15
|
|
41
|
-
|
42
|
-
|
43
|
-
|
16
|
+
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
17
|
+
# delete this section to allow pushing this gem to any host.
|
18
|
+
#if spec.respond_to?(:metadata)
|
19
|
+
# spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
|
20
|
+
#else
|
21
|
+
# raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
22
|
+
#end
|
44
23
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
end
|
50
|
-
end
|
24
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
25
|
+
spec.bindir = "exe"
|
26
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
|
+
spec.require_paths = ["lib"]
|
51
28
|
|
29
|
+
spec.add_development_dependency "bundler", "~> 1.11"
|
30
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
31
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
32
|
+
end
|
data/lib/easy_attributes.rb
CHANGED
@@ -1,309 +1,579 @@
|
|
1
|
-
|
1
|
+
require "easy_attributes/version"
|
2
|
+
|
3
|
+
###############################################################################
|
4
|
+
# EasyAttributes Module - Provides attribute handling enahancements.
|
5
|
+
#
|
6
|
+
# Features include:
|
7
|
+
#
|
8
|
+
# * Attribute Enum support, giving symbolic names for numeric or other values
|
9
|
+
# * Provides optional ActiveModel ORM enhancements for validations, etc.
|
10
|
+
# * Byte datatype helpers
|
11
|
+
# * Fixed-decimal datatype (e.g. Dollars) helpers
|
12
|
+
#
|
13
|
+
# To Use:
|
14
|
+
#
|
15
|
+
# * Require the easy_attributes gem if you need.
|
16
|
+
# * Mix in the EasyAttributes module into your class.
|
17
|
+
# * Load any external enum definitions at application start-up
|
18
|
+
# * Define attribute enhancers in your class
|
19
|
+
#
|
20
|
+
# Gemfile (add entry, then `bundle install`):
|
21
|
+
# gem 'easy_attributes'
|
22
|
+
# Or manually
|
23
|
+
# require 'easy_attributes'
|
24
|
+
# Then
|
25
|
+
# class MyClass
|
26
|
+
# include EasyAttributes
|
27
|
+
# attr_enum :my_attribute, :zero, :one, :two
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
###############################################################################
|
2
31
|
module EasyAttributes
|
3
|
-
|
32
|
+
|
33
|
+
# Called by Ruby after including to add our ClassMethods to the parent class
|
4
34
|
def self.included(base) #:nodoc:
|
5
|
-
base.extend(
|
35
|
+
base.extend(ClassMethods)
|
6
36
|
end
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
37
|
+
|
38
|
+
#############################################################################
|
39
|
+
# EasyAttributes::Definition - Class of a attribute values definition
|
40
|
+
#
|
41
|
+
# attribute - symbolic name of the attribute, field, or column.
|
42
|
+
# symbols - Hash of {symbolic_value:value, ...}
|
43
|
+
# values - Hash of {value => :symbolic_value, ...}
|
44
|
+
# options - Hash of {option_name:value} for attribute settings
|
45
|
+
# attr_options - Hash of {value_name: {option_name:value, ...}, e.g.
|
46
|
+
# :name - Alternate text for the value name
|
47
|
+
# :title - Alternate text for value definition
|
48
|
+
# :role - Identifier for a role allowed to set/see
|
49
|
+
#############################################################################
|
50
|
+
class Definition
|
51
|
+
include Enumerable
|
52
|
+
attr_accessor :attribute, :values, :symbols, :options, :attr_options
|
53
|
+
|
54
|
+
# Public: Returns an existing or new definition for the atribute name
|
55
|
+
# Call this method to define a global or shared setting
|
56
|
+
#
|
57
|
+
# attribute - The name of the attribute
|
58
|
+
# definition - The optional definition list passed to initialize
|
59
|
+
# A has of symbol_name => values
|
60
|
+
# Make sure the type of value matches your use,
|
61
|
+
# either a string "42" or integer "42".to_i
|
62
|
+
# attr_options - A optional Hash of attribute names to a hash of additional info
|
63
|
+
# {status:{help:"...", title:"Status"}}
|
64
|
+
#
|
65
|
+
# Examples
|
66
|
+
#
|
67
|
+
# defn = Definition.find_or_create(:status).add_symbol(:retired, 3)
|
68
|
+
# defn = Definition.find_or_create(:status, active:1, inactive:2)
|
69
|
+
# defn = Definition.find_or_create(:storage, {}, {kb_size:1000})
|
70
|
+
# defn.values #=> {value=>:symbol,...}
|
71
|
+
# defn.symbols #=> {:symbol=>:value,...}
|
72
|
+
#
|
73
|
+
#
|
74
|
+
# Returns an existing or new instance of Definition
|
75
|
+
#
|
76
|
+
def self.find_or_create(attribute, *definition)
|
77
|
+
attribute = attribute.to_sym
|
78
|
+
@attributes ||= {}
|
79
|
+
unless @attributes.has_key?(attribute)
|
80
|
+
@attributes[attribute] = Definition.new(attribute, *definition)
|
81
|
+
end
|
82
|
+
@attributes[attribute]
|
25
83
|
end
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
@@orm = o
|
84
|
+
|
85
|
+
def to_s
|
86
|
+
"<#EasyAttributes::Definition #{@attribute} #{@symbols.inspect}>"
|
30
87
|
end
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
@@orm
|
35
|
-
end
|
36
|
-
|
37
|
-
# Defines a symbol name to a hash of :name=>value
|
38
|
-
def self.define(name, hash)
|
39
|
-
@@attributes[name] = hash
|
40
|
-
end
|
41
|
-
|
42
|
-
# Returns the symbol table
|
43
|
-
def self.attributes
|
44
|
-
@@attributes
|
45
|
-
end
|
46
|
-
|
47
|
-
# Returns the value table:
|
48
|
-
# values[:name][:rec][:setting] = {:word, :description, :value}
|
49
|
-
# values[:name][:sym][:setting] = value
|
50
|
-
# values[:name][:value]["value"] = :symbol
|
51
|
-
def self.values
|
52
|
-
@@values
|
53
|
-
end
|
54
|
-
|
55
|
-
# Loads a tab-delimted filename into the symbol table in format:
|
56
|
-
# attribute value role symbolic_name short_description long_description (useful for "<option>" data)
|
57
|
-
# If a block is given, it should parse a file line and return an array of
|
58
|
-
# [attribute, value, role*, name, short*, long_description*], *denotes not required, but placeholder required
|
59
|
-
def self.load(filename)
|
60
|
-
File.open(filename).each do |r|
|
61
|
-
next unless r =~ /^\w/
|
62
|
-
(col, val, priv, symbol, word, desc) = block_given? ? yield(r) : r.split(/\t/)
|
63
|
-
next if desc.nil? || desc.empty? || symbol.empty? || word.empty? || symbol.nil?
|
64
|
-
col = col.to_sym
|
65
|
-
@@values[col] = {:sym=>{}, :val=>{}, :rec=>{}} unless @@values.has_key?(col)
|
66
|
-
@@values[col][:sym][symbol.to_sym] = val.to_i
|
67
|
-
@@values[col][:val][val] = symbol.to_sym
|
68
|
-
@@values[col][:rec][symbol.to_sym] = {:word=>word, :description=>desc.chomp, :value=>val.to_i}
|
69
|
-
@@attributes[col.to_s] ||= {}
|
70
|
-
@@attributes[col.to_s][symbol.to_sym] = val.to_i
|
71
|
-
end
|
88
|
+
|
89
|
+
def self.definitions
|
90
|
+
@attributes
|
72
91
|
end
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
#
|
86
|
-
def
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
92
|
+
|
93
|
+
def self.shared(attribute, *definition)
|
94
|
+
find_or_create(attribute, *definition)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Public: Creates a new Definition for the attribute and definition list
|
98
|
+
# Call this method to create a non-shared definition, else call find_or_create
|
99
|
+
#
|
100
|
+
# Examples
|
101
|
+
#
|
102
|
+
# Definition.new(:status, active:1, inactive:2)
|
103
|
+
#
|
104
|
+
# Returns the new instance
|
105
|
+
def initialize(attribute, *definition)
|
106
|
+
self.attribute = attribute.to_sym
|
107
|
+
self.values = {}
|
108
|
+
self.symbols = {}
|
109
|
+
self.options = {}
|
110
|
+
self.attr_options = {}
|
111
|
+
self.define(*definition)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Public: Create an attribute definition
|
115
|
+
#
|
116
|
+
# symbols - Hash of {symbol:value,...} for the attribute, or
|
117
|
+
# - Array of enum definitions for the attribute, or
|
118
|
+
# - Hash of {value:value, title:text, name:text, option_name:etc}
|
119
|
+
# options - Hash of {name:value,...} to track for the attribute. Optional.
|
120
|
+
# attr_options: {attribute: {....}, ...}
|
121
|
+
#
|
122
|
+
# Examples
|
123
|
+
#
|
124
|
+
# definition.define(active:1, inactive:2)
|
125
|
+
# definition.define(:active, :inactive)
|
126
|
+
#
|
127
|
+
def define(*args)
|
128
|
+
return if args.first.nil?
|
129
|
+
return define_enum(*args) if args.first.is_a?(Array)
|
130
|
+
|
131
|
+
symbols = {}
|
132
|
+
options = {}
|
133
|
+
args.first.each do |k,v|
|
134
|
+
if v.is_a?(Hash)
|
135
|
+
symbols[k.to_sym] = v.delete(:value) {k.to_s}
|
136
|
+
options[k.to_sym] = v
|
137
|
+
else
|
138
|
+
symbols[k.to_sym] = v
|
139
|
+
end
|
104
140
|
end
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
141
|
+
|
142
|
+
self.symbols.merge!(symbols)
|
143
|
+
self.values = Hash[* self.symbols.collect {|k,v| [v, k]}.flatten]
|
144
|
+
self.attr_options.merge!(options)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Public: Defines an Symbol/Value for the attribute
|
148
|
+
#
|
149
|
+
# attrib - name of the attribute or database column
|
150
|
+
# symbol - internal symbolic name for the value
|
151
|
+
# - If nil, it will use the next value from the set of current values
|
152
|
+
# value - Value to store in class or database
|
153
|
+
# options - Hash of {name:value,...} to track for the symbol. Optional.
|
154
|
+
#
|
155
|
+
# Examples
|
156
|
+
#
|
157
|
+
# definition.add_symbol(:active, 1)
|
158
|
+
# EasyAttributes::Config.define_value :status, :active, 1, name:"Active"
|
159
|
+
#
|
160
|
+
def add_symbol(symbol, value=nil, attr_options={})
|
161
|
+
symbol = symbol.to_sym
|
162
|
+
if value.nil?
|
163
|
+
if a.size > 0
|
164
|
+
value = self.values.keys.max.next
|
165
|
+
else
|
166
|
+
value = 0
|
117
167
|
end
|
118
|
-
)
|
119
|
-
if EasyAttributes::Config.orm == :active_model
|
120
|
-
code += %Q(
|
121
|
-
def #{attribute}=(v)
|
122
|
-
self[:#{attribute}] = v.is_a?(Symbol) ? EasyAttributes.value_for_sym("#{name}", v) : v;
|
123
|
-
end
|
124
|
-
)
|
125
168
|
end
|
126
|
-
|
127
|
-
|
169
|
+
|
170
|
+
value = self.values.keys.max || 0 if value.nil?
|
171
|
+
self.symbols[symbol] = value
|
172
|
+
self.values[value] = symbol
|
173
|
+
self.attr_options[symbol] = attr_options
|
128
174
|
end
|
129
|
-
|
130
|
-
#
|
131
|
-
#
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
175
|
+
|
176
|
+
# Public: Defines an attribute as an enumerated set of symbol/values
|
177
|
+
#
|
178
|
+
# args - list of symbols, reset values, with an optional options Hash
|
179
|
+
# a non-symbolic arg resets the counter to that value
|
180
|
+
# Non-integer values can be given if the #next() method is provided
|
181
|
+
# nil can be passed to skip the positional value
|
182
|
+
#
|
183
|
+
# Examples
|
184
|
+
#
|
185
|
+
# definition.define_enum(:active, :inactive)
|
186
|
+
# definition.define_enum(:active, :inactive, start:1, step:10)
|
187
|
+
# definition.define_enum(:active, 11, :retired, nil, :inactive)
|
188
|
+
#
|
189
|
+
def define_enum(args, opt={})
|
190
|
+
opt = {step:1}.merge(opt)
|
191
|
+
opt[:start] ||= self.values.keys.max ? self.values.keys.max + opt[:step] : Config.enum_start
|
192
|
+
hash = {}
|
193
|
+
i = opt[:start]
|
194
|
+
args.flatten.each do |arg|
|
195
|
+
if arg.is_a?(Symbol) || arg.nil?
|
196
|
+
hash[arg] = i unless arg.nil?
|
197
|
+
opt[:step].times {i = i.next}
|
198
|
+
else
|
199
|
+
i = arg
|
143
200
|
end
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
#
|
150
|
-
#
|
151
|
-
#
|
152
|
-
#
|
153
|
-
#
|
154
|
-
#
|
155
|
-
#
|
156
|
-
#
|
157
|
-
#
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
# * :unit - Prepend this to the front of the money value, say '$', default none
|
162
|
-
# * :blank - Return this value when the money string is empty or has no digits on assignment
|
163
|
-
# * :negative_regex - A Regular Expression used to determine if a number is negative (and without a - sign)
|
164
|
-
#
|
165
|
-
def attr_money(attribute, *args)
|
166
|
-
opt = args.last.is_a?(Hash) ? args.pop : {}
|
167
|
-
money_method = opt.delete(:money_method) || "#{attribute}_#{opt.delete(:units)||'money'}"
|
201
|
+
end
|
202
|
+
define(hash)
|
203
|
+
end
|
204
|
+
|
205
|
+
# Public: Returns the defined value for the given symbol, or returns
|
206
|
+
# the supplied default, nil, or a value yeilded by a block
|
207
|
+
#
|
208
|
+
# symbol - symbolic name of value, eg. :active
|
209
|
+
#
|
210
|
+
# Examples
|
211
|
+
#
|
212
|
+
# definition.value_of(:active) # => 1
|
213
|
+
#
|
214
|
+
# Returns the defined value of symbol for the attribute
|
215
|
+
def value_of(sym, default=nil)
|
216
|
+
self.symbols.fetch(sym.to_sym) { block_given? ? yield(sym) : default }
|
217
|
+
end
|
168
218
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
219
|
+
# Public: Returns the defined symbol for the given value, or returns
|
220
|
+
# the supplied default, nil, or a value yeilded by a block
|
221
|
+
#
|
222
|
+
# value - raw value of attribute, eg. 1
|
223
|
+
#
|
224
|
+
# Examples
|
225
|
+
#
|
226
|
+
# definition.symbol_of(1) # => :active
|
227
|
+
#
|
228
|
+
# Returns the defined symbol (eg.:active) for the given value on the attribute
|
229
|
+
def symbol_of(value, default=nil)
|
230
|
+
self.values.fetch(value) { block_given? ? yield(value) : default }
|
231
|
+
end
|
232
|
+
|
233
|
+
# Public: Returns true if the current value of the attribute is (or is in the list of
|
234
|
+
# values) referenced by their symbolic names
|
235
|
+
#
|
236
|
+
# value - The value to match
|
237
|
+
# symbols - array of symbolic value names, eg. :active, :inactive
|
238
|
+
#
|
239
|
+
# Examples
|
240
|
+
#
|
241
|
+
# definition.value_in(self.status, :active) # => false (maybe)
|
242
|
+
# definition.value_in(self.:status, :active, :inactive) # => true (maybe)
|
243
|
+
# self.value_in(:status, :between, :active, :inactive) # => true (maybe)
|
244
|
+
#
|
245
|
+
# Returns true if the value matches
|
246
|
+
def value_in(value, *args)
|
247
|
+
args.each do |arg|
|
248
|
+
return true if value == value_of(arg)
|
173
249
|
end
|
250
|
+
false
|
251
|
+
end
|
252
|
+
|
253
|
+
# Public: Implements the comparison operator (cmp, <=>) for a value against a symbolic name
|
254
|
+
#
|
255
|
+
# Examples
|
256
|
+
#
|
257
|
+
# definition.cmp(1,:active) # => -1. 0, or 1 according to the <=> op defined on the value class
|
258
|
+
#
|
259
|
+
# Returns -1 if value < symbol, 0 if value == symbol, or 1 of value < symbol
|
260
|
+
def cmp(value, symbol)
|
261
|
+
other = value_of(symbol)
|
262
|
+
value <=> other
|
263
|
+
end
|
174
264
|
|
175
|
-
|
176
|
-
|
177
|
-
|
265
|
+
# Public: Compares the value to be between the representation of of the two symbolic names
|
266
|
+
# Returns true if value withing the designated range, false otherwise
|
267
|
+
def between(value, op, symbol1, symbol2=nil)
|
268
|
+
v1 = value_of(symbol1)
|
269
|
+
v2 = value_of(symbol2)
|
270
|
+
value >= v1 && value <= v2
|
271
|
+
end
|
272
|
+
|
273
|
+
# Public: Returns the next value in the definition from the given value
|
274
|
+
def next(value, default=nil)
|
275
|
+
self.values.keys.sort.each {|i| return i if i > value }
|
276
|
+
default
|
277
|
+
end
|
278
|
+
alias :succ :next
|
279
|
+
|
280
|
+
# Public: Returns the next value in the definition from the given value
|
281
|
+
def previous(value, default=nil)
|
282
|
+
self.values.keys.sort.reverse.each {|i| return i if i < value }
|
283
|
+
default
|
284
|
+
end
|
285
|
+
|
286
|
+
# Public: Builds a list of [option_name, value] pairs useful for HTML select options
|
287
|
+
# Where option_name is the first found of:
|
288
|
+
# - attr_options[:option_name]
|
289
|
+
# - attr_options[:title]
|
290
|
+
# - attr_options[:name]
|
291
|
+
# - capitalized attribute name
|
292
|
+
#
|
293
|
+
# attribute - symbolic name of attribute
|
294
|
+
#
|
295
|
+
# Returns an array of [option_name, value] pairs.
|
296
|
+
def select_option_values(*args)
|
297
|
+
self.symbols.collect {|s,v| [symbol_option_name(s,*args), v]}
|
298
|
+
end
|
299
|
+
|
300
|
+
# Private: Builds a list of [option_name, symbol] pairs useful for HTML select options
|
301
|
+
# Where option_name is as defined in selection_option_values
|
302
|
+
#
|
303
|
+
# attribute - symbolic name of attribute
|
304
|
+
#
|
305
|
+
# Returns an array of [option_name, symbol] pairs.
|
306
|
+
def select_option_symbols(*args)
|
307
|
+
self.symbols.collect {|s,v| [symbol_option_name(s,*args), s]}
|
308
|
+
end
|
309
|
+
|
310
|
+
def symbol_option_name(s, *args)
|
311
|
+
if @attr_options.has_key?(s)
|
312
|
+
args.each {|arg| return @attr_options[s][arg] if @attr_options[s].has_key?(arg) }
|
313
|
+
[:option_name, :title, :name] .each do |f|
|
314
|
+
return @attr_options[s][f] if @attr_options[s].has_key?(f)
|
315
|
+
end
|
178
316
|
end
|
179
|
-
)
|
317
|
+
s.to_s.capitalize.gsub(/_/, ' ')
|
180
318
|
end
|
181
|
-
|
182
|
-
end
|
183
319
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
["self.attributes[:#{attribute}]", "self.write_attribute(:#{attribute}, v)"]
|
188
|
-
else
|
189
|
-
["@#{attribute}", "@#{attribute} = v"]
|
320
|
+
# Defines each() for Enumerable
|
321
|
+
def each
|
322
|
+
@definition.symbols.each {|s,v| yield(s,v)}
|
190
323
|
end
|
191
|
-
end
|
192
324
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
def self.add_definition(attribute, hash, opt={})
|
198
|
-
EasyAttributes::Config.define attribute, hash
|
199
|
-
end
|
200
|
-
|
201
|
-
##############################################################################
|
202
|
-
# attr_values helpers
|
203
|
-
##############################################################################
|
204
|
-
|
205
|
-
# Returns the defined value for the given symbol and attribute
|
206
|
-
def self.value_for_sym(attribute, sym)
|
207
|
-
EasyAttributes::Config.attributes[attribute][sym] or
|
208
|
-
raise "EasyAttributes #{attribute} value :#{sym} not declared"
|
209
|
-
end
|
210
|
-
|
211
|
-
# Returns the defined symbol for the given value on the attribute
|
212
|
-
def self.sym_for_value(attribute, value)
|
213
|
-
return nil if value.nil?
|
214
|
-
EasyAttributes::Config.attributes[attribute].each {|k,v| return k if v==value}
|
215
|
-
raise "EasyAttribute #{attribute} symbol not found for #{value}"
|
216
|
-
end
|
217
|
-
|
218
|
-
def self.value_is?(attribute, value, *args)
|
219
|
-
case args.first
|
220
|
-
when :between then
|
221
|
-
value >= EasyAttributes::Config.attributes[attribute][args[1]] && value <= EasyAttributes::Config.attributes[attribute][args[2]]
|
222
|
-
when :gt, :greater_than then
|
223
|
-
value > EasyAttributes::Config.attributes[attribute][args[1]]
|
224
|
-
when :ge, :greater_than_or_equal_to then
|
225
|
-
value >= EasyAttributes::Config.attributes[attribute][args[1]]
|
226
|
-
when :lt, :less_than then
|
227
|
-
value < EasyAttributes::Config.attributes[attribute][args[1]]
|
228
|
-
when :le, :less_than_or_equal_to then
|
229
|
-
value <= EasyAttributes::Config.attributes[attribute][args[1]]
|
230
|
-
#when :not, :not_in
|
231
|
-
# ! args.include? EasyAttributes::Config.attributes[attribute].keys
|
232
|
-
else
|
233
|
-
args.include? EasyAttributes.sym_for_value(attribute, value)
|
325
|
+
# Defines <=>() for Enumberable comparisons
|
326
|
+
def <=>(other)
|
327
|
+
@definition.cmp(@value,other)
|
234
328
|
end
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
##############################################################################
|
240
|
-
|
241
|
-
# Official Definitions for kilobyte and kibibyte quantity prefixes
|
242
|
-
KB =1000; MB =KB **2; GB= KB **3; TB =KB **4; PB =KB **5; EB =KB **6; ZB =KB **7; YB =KB **8
|
243
|
-
KiB=1024; MiB=KiB**2; GiB=KiB**3; TiB=KiB**4; PiB=KiB**5; EiB=KiB**6; ZiB=KiB**7; YiB=KiB**8
|
244
|
-
DECIMAL_PREFIXES = {:B=>1, :KB=>KB, :MB=>MB, :GB=>GB, :TB=>TB, :PB=>PB, :EB=>EB, :ZB=>ZB, :TB=>YB}
|
245
|
-
BINARY_PREFIXES = {:B=>1, :KiB=>KiB, :MiB=>MiB, :GiB=>GiB, :TiB=>TiB, :PiB=>PiB, :EiB=>EiB, :ZiB=>ZiB, :TiB=>YiB}
|
246
|
-
|
247
|
-
# Returns a hash of prefix names to decimal quantities for the given setting
|
248
|
-
def self.byte_prefixes(kb_size=0)
|
249
|
-
case kb_size
|
250
|
-
when 1000, :decimal, :si then DECIMAL_PREFIXES
|
251
|
-
when :old, :jedec, 1024 then {:KB=>KiB, :MB=>MiB, :GB=>GiB, :TB=>TiB, :PB=>PiB, :EB=>EiB, :ZB=>ZiB, :TB=>YiB}
|
252
|
-
when :new, :iec then BINARY_PREFIXES
|
253
|
-
else DECIMAL_PREFIXES.merge(BINARY_PREFIXES) # Both? What's the least surprise?
|
329
|
+
|
330
|
+
# For the experimental Value class. Takes a value or symbol
|
331
|
+
def value(v)
|
332
|
+
Value.new(self, v)
|
254
333
|
end
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
334
|
+
|
335
|
+
def inspect
|
336
|
+
@value
|
337
|
+
end
|
338
|
+
|
339
|
+
###########################################################################
|
340
|
+
# Official Definitions for kilobyte and kibibyte quantity units
|
341
|
+
###########################################################################
|
342
|
+
KB =1000; MB=KB ** 2; GB=KB ** 3; TB=KB ** 4; PB=KB ** 5; EB=KB ** 6; ZB=KB ** 7; YB=KB ** 8
|
343
|
+
KiB=1024; MiB=KiB**2; GiB=KiB**3; TiB=KiB**4; PiB=KiB**5; EiB=KiB**6; ZiB=KiB**7; YiB=KiB**8
|
344
|
+
BINARY_UNITS = {:B=>1,:KiB=>KiB,:MiB=>MiB,:GiB=>GiB,:TiB=>TiB,:PiB=>PiB,:EiB=>EiB,:ZiB=>ZiB,:YiB=>YiB}
|
345
|
+
DECIMAL_UNITS = {:B=>1,:KB=>KB, :MB=>MB, :GB=>GB, :TB=>TB, :PB=>PB, :EB=>EB, :ZB=>ZB, :YB=>YB}
|
346
|
+
JEDEC_UNITS = {:B=>1,:KB=>KiB, :MB=>MiB, :GB=>GiB, :TB=>TiB, :PB=>PiB, :EB=>EiB, :ZB=>ZiB, :YB=>YiB}
|
347
|
+
|
348
|
+
# Public: Maps the kb_size into a hash of desired unit_symbol=>bytes.
|
349
|
+
#
|
350
|
+
# kb_size - For decimal units: 1000, :decimal, :si, :kb
|
351
|
+
# For binary units : 1024, :jedec, :old, :kib
|
352
|
+
# Otherwise a hash of combined values is returned
|
353
|
+
#
|
354
|
+
# Returns a hash of prefix names to decimal quantities for the given setting
|
355
|
+
def self.byte_units(kb_size=0)
|
356
|
+
case kb_size
|
357
|
+
when 1000, :decimal, :si, :kb, :KB then DECIMAL_UNITS
|
358
|
+
when :old, :jedec, 1024, :kib, :KiB then JEDEC_UNITS
|
359
|
+
when :new, :iec then BINARY_UNITS
|
360
|
+
else DECIMAL_UNITS.merge(BINARY_UNITS) # Both? What's the least surprise?
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
# Private: Formats an integer as a byte-representation
|
365
|
+
#
|
366
|
+
# v - Integer value of bytes
|
367
|
+
# unit - Optional Unit to use for representation, regardless of magnitude
|
368
|
+
# opt - Optional hash of overrides for :kb_size, :precision, etc.
|
369
|
+
#
|
370
|
+
# Example:
|
371
|
+
# format_bytes(1000, :kb) # => "1 KB"
|
372
|
+
#
|
373
|
+
# Returns a string like "n.nn XB" representing the approximate bytes
|
374
|
+
#
|
375
|
+
def format_bytes(v, *args)
|
376
|
+
return v if v.nil?
|
377
|
+
opt = args.last.is_a?(Hash) ? args.pop : {}
|
378
|
+
opt = attr_options.merge(opt)
|
379
|
+
unit = args.shift
|
380
|
+
units = Definition.byte_units(opt[:kb_size]||Config.kb_size||1000)
|
381
|
+
precision = opt[:precision] || attr_options[:precision] || 0
|
382
|
+
|
383
|
+
if unit
|
384
|
+
units = Definition.byte_units() unless units.has_key?(unit)
|
385
|
+
v = "%.#{precision}f" % (1.0 * v / (units[unit]||1))
|
386
|
+
return "#{v} #{unit}"
|
387
|
+
end
|
388
|
+
|
389
|
+
units.sort{|a,b| a[1]<=>b[1]}.reverse.each do |pv|
|
268
390
|
next if pv[1] > v
|
269
|
-
v = "
|
270
|
-
v = v[0,precision+1] if v =~ /^(\d)+\.(\d+)/ && v.size > (precision+1)
|
391
|
+
v = "%.#{precision}f" % (1.0 * v / pv[1])
|
271
392
|
v.gsub!(/\.0*$/, '')
|
272
393
|
return "#{v} #{pv[0]}"
|
273
394
|
end
|
395
|
+
v.to_s
|
396
|
+
end
|
397
|
+
|
398
|
+
# Private: Parses a "1.23 kb" style string and converts into an integer
|
399
|
+
# v - String to parse of the format "1.23 kb" or so
|
400
|
+
# Optionally, this can be an array of [1.23, :kb]
|
401
|
+
# options - Hash of options to override defaults
|
402
|
+
# kb_size:1000
|
403
|
+
#
|
404
|
+
# Returns an integer of the parsed value.
|
405
|
+
def parse_bytes(v, *args)
|
406
|
+
opt = args.last.is_a?(Hash) ? args.pop : {}
|
407
|
+
opt = attr_options.merge(opt)
|
408
|
+
# Handle v= [100, :KB]
|
409
|
+
if v.is_a?(Array)
|
410
|
+
bytes = v.shift
|
411
|
+
v = "#{bytes} #{v.shift}"
|
412
|
+
else
|
413
|
+
bytes = v.to_f
|
414
|
+
end
|
415
|
+
|
416
|
+
if v.downcase =~ /^\s*(?:[\d\.]+)\s*([kmgtpezy]i?b)/i
|
417
|
+
unit = ($1.size==2 ? $1.upcase : $1[0,1].upcase+$1[1,1]+$1[2,1].upcase).to_sym
|
418
|
+
units = Definition.byte_units(opt[:kb_size]||Config.kb_size||1000)
|
419
|
+
units = Definition.byte_units(:both) unless units.has_key?(unit)
|
420
|
+
bytes *= units[unit] if units.has_key?(unit)
|
421
|
+
end
|
422
|
+
(bytes*100 + 0.00001).to_i/100
|
274
423
|
end
|
275
|
-
v.to_s
|
276
424
|
end
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
425
|
+
|
426
|
+
#############################################################################
|
427
|
+
# EasyAttributes::Value - Experiment! Value Class for attribute values
|
428
|
+
#############################################################################
|
429
|
+
class Value
|
430
|
+
include Comparable
|
431
|
+
|
432
|
+
# Usage: Value.new(definition, symbol_or_value)
|
433
|
+
def initialize(definition, value)
|
434
|
+
@definition = definition
|
435
|
+
@attribute = @definition.attribute
|
436
|
+
if value.is_a?(Symbol)
|
437
|
+
@value = @definition.value_of(value)
|
438
|
+
else
|
439
|
+
@value = value
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
# Returns the symbolic name of this value
|
444
|
+
def to_sym
|
445
|
+
@definition.symbol_of(@value)
|
446
|
+
end
|
447
|
+
|
448
|
+
# Compare with a defined symbol or another value. Required for Comparable
|
449
|
+
def <=>(other)
|
450
|
+
if other.is_a?(Symbol)
|
451
|
+
@value <=> @definition.value_of(other)
|
452
|
+
else
|
453
|
+
@value <=> other
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
# Returns the next value in the definition as a Value object
|
458
|
+
def next
|
459
|
+
Value.new(@definition.next(@value))
|
460
|
+
end
|
461
|
+
alias :succ :next
|
462
|
+
|
463
|
+
# Forward all other methods to the actual @value class
|
464
|
+
def method_missing(method, *args)
|
465
|
+
@value.send(method, *args)
|
466
|
+
end
|
296
467
|
end
|
297
|
-
|
298
|
-
|
299
|
-
#
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
468
|
+
|
469
|
+
#############################################################################
|
470
|
+
# EasyAttributes::Config - Namespace to define and hold configuration data.
|
471
|
+
#############################################################################
|
472
|
+
class Config
|
473
|
+
@orm = :attr # :attr, :active_model
|
474
|
+
@kb_size = :iec # :iec, :old, :new
|
475
|
+
|
476
|
+
# Public: Set the default size for a kilobyte/kibibyte
|
477
|
+
# Refer to: http://en.wikipedia.org/wiki/Binary_prefix
|
478
|
+
#
|
479
|
+
# setting - How to represent kilobyte
|
480
|
+
# :new, :iec uses KiB=1024, no KB
|
481
|
+
# :old, :jedec, or 1024 uses KB=1024
|
482
|
+
# 1000, :decimal uses only KB (1000) (other values mix KB and KiB units)
|
483
|
+
# Note: "IEC" is the International Electrotechnical Commission
|
484
|
+
# "JEDEC" is the Joint Electron Devices Engineering Council
|
485
|
+
#
|
486
|
+
# Examples
|
487
|
+
#
|
488
|
+
# EasyAttributes::Config.kb_size = :iec
|
489
|
+
#
|
490
|
+
# Returns new setting
|
491
|
+
#
|
492
|
+
def self.kb_size=(setting)
|
493
|
+
@kb_size = setting
|
494
|
+
end
|
495
|
+
|
496
|
+
# Public: Returns the current kb_size setting for use in computing Bytes
|
497
|
+
#
|
498
|
+
# Returns the current kb_size setting
|
499
|
+
#
|
500
|
+
def self.kb_size
|
501
|
+
case @kb_size
|
502
|
+
when :new, :iec then
|
503
|
+
1024
|
504
|
+
when :old, :jedec, :decimal
|
505
|
+
1000
|
506
|
+
else
|
507
|
+
@kb_size
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
511
|
+
# Public: Sets the ORM (Object Relational Mapper) to a supported policy
|
512
|
+
#
|
513
|
+
# new_orm - value
|
514
|
+
# :attr = attr_accessor style operations
|
515
|
+
# :acitve_model = Rails ActiveModel operations
|
516
|
+
#
|
517
|
+
# Returns the new ORM setting
|
518
|
+
#
|
519
|
+
def self.orm=(new_orm)
|
520
|
+
@orm = new_orm
|
521
|
+
end
|
522
|
+
|
523
|
+
# Public: Returns the current ORM setting
|
524
|
+
#
|
525
|
+
# Returns the current ORM setting
|
526
|
+
#
|
527
|
+
def self.orm
|
528
|
+
@orm
|
529
|
+
end
|
530
|
+
|
531
|
+
# Directive to create constants from field value names: FIELD_NAME_VALUE_NAME=value
|
532
|
+
def self.constantize=(b)
|
533
|
+
@constantize = b ? true : false
|
534
|
+
end
|
535
|
+
|
536
|
+
def self.constantize
|
537
|
+
@constantize || false
|
538
|
+
end
|
539
|
+
|
540
|
+
# Starting point for enum sequences (usually 0 or 1)
|
541
|
+
def self.enum_start
|
542
|
+
@enum_start || 1
|
543
|
+
end
|
544
|
+
|
545
|
+
def self.enum_start=(n)
|
546
|
+
@enum_start = n
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
# Private Module: FixedPoint handles integer<->fixed-point numbers
|
551
|
+
# Fixed-Point rules are a hash of these values
|
552
|
+
# :units Use this as an alternative suffix name to the money methods ('dollars' gives 'xx_dollars')
|
553
|
+
# :precision The number of digits implied after the decimal, default is 2
|
554
|
+
# :separator The character to use after the integer part, default is '.'
|
555
|
+
# :delimiter The character to use between every 3 digits of the integer part, default none
|
556
|
+
# :positive The sprintf format to use for positive numbers, default is based on precision
|
557
|
+
# :negative The sprintf format to use for negative numbers, default is same as :positive
|
558
|
+
# :zero The sprintf format to use for zero, default is same as :positive
|
559
|
+
# :nil The sprintf format to use for nil values, default none
|
560
|
+
# :unit Prepend this to the front of the money value, say '$', default none
|
561
|
+
# :blank Return this value when the money string is empty or has no digits on assignment
|
562
|
+
# :negative_regex A Regular Expression used to determine if a number is negative (and without a - sign)
|
563
|
+
module FixedPoint
|
564
|
+
###########################################################################
|
565
|
+
# EasyAttributes Fixed-Precision as Integer Helpers
|
566
|
+
###########################################################################
|
567
|
+
|
568
|
+
# Private: Formats an integer value as the defined fixed-point representation
|
569
|
+
# value - integer representation of number
|
570
|
+
# rules - hash of fixed-point conversion rules
|
571
|
+
#
|
572
|
+
# Returns the fixed-point representation as a string for editing
|
573
|
+
def self.integer_to_fixed_point(value, *args)
|
574
|
+
opt = args.last.is_a?(Hash) ? args.pop : {}
|
575
|
+
opt[:positive] ||= "%.#{opt[:precision]||2}f"
|
576
|
+
pattern =
|
307
577
|
if value.nil?
|
308
578
|
value = 0
|
309
579
|
opt[:nil] || opt[:positive]
|
@@ -311,72 +581,464 @@ module EasyAttributes
|
|
311
581
|
case value <=> 0
|
312
582
|
when 1 then opt[:positive]
|
313
583
|
when 0 then opt[:zero] || opt[:positive]
|
314
|
-
else
|
584
|
+
else
|
315
585
|
value = -value if opt[:negative] && opt[:negative] != opt[:positive]
|
316
586
|
opt[:negative] || opt[:positive]
|
317
587
|
end
|
318
588
|
end
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
589
|
+
value = self.format_fixed_point( value, pattern, opt)
|
590
|
+
value = opt[:unit]+value if opt[:unit]
|
591
|
+
value.gsub!(/\./,opt[:separator]) if opt[:separator]
|
592
|
+
if opt[:delimiter] && (m = value.match(/^(\D*)(\d+)(.*)/))
|
593
|
+
# Adapted From Rails' ActionView::Helpers::NumberHelper
|
594
|
+
n = m[2].gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{opt[:delimiter]}")
|
595
|
+
value=m[1]+n+m[3]
|
596
|
+
end
|
597
|
+
value
|
598
|
+
end
|
329
599
|
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
600
|
+
# Private: Converts the integer into a float value with the given fixed-point definition
|
601
|
+
#
|
602
|
+
# value - integer to convert
|
603
|
+
# rules - hash of fixed-point conversion rules
|
604
|
+
#
|
605
|
+
# Returns a float of the converted value
|
606
|
+
def self.integer_to_float(value, *args)
|
607
|
+
opt = args.last.is_a?(Hash) ? args.pop : {}
|
608
|
+
return (opt[:blank]||nil) if value.nil?
|
609
|
+
value = 1.0 * value / (10**(opt[:precision]||2))
|
610
|
+
end
|
335
611
|
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
cents = match[2]
|
351
|
-
cents = cents + '0' while cents.length < (opt[:precision]||2)
|
352
|
-
cents = cents.to_s[0,opt[:precision]||2]
|
353
|
-
value += cents.to_i * (value<0 ? -1 : 1)
|
354
|
-
value
|
355
|
-
end
|
612
|
+
# Private: Takes a string of a fixed-point representation (from editing) and converts it to
|
613
|
+
# the integer representation according to the passed rules hash
|
614
|
+
# rules - hash of fixed-point conversion rules
|
615
|
+
#
|
616
|
+
# Returns the integer of the given money string. Uses relevant options from #easy_money
|
617
|
+
def self.fixed_point_to_integer(value, *args)
|
618
|
+
opt = args.last.is_a?(Hash) ? args.pop : {}
|
619
|
+
value = value.to_s
|
620
|
+
value = value.gsub(opt[:delimiter],'') if opt[:delimiter]
|
621
|
+
value = value.gsub(opt[:separator],'.') if opt[:separator]
|
622
|
+
value = value.gsub(/^[^\d\.\-\,]+/,'')
|
623
|
+
return (opt[:blank]||nil) unless value =~ /\d/
|
624
|
+
m = value.to_s.match(opt[:negative_regex]||/^(-?)(.+\d)\s*cr/i)
|
625
|
+
value = value.match(/^-/) ? m[2] : "-#{m[2]}" if m && m[2]
|
356
626
|
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
value =
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
627
|
+
# Money string ("123.45") to proper integer withough passing through the float transformation
|
628
|
+
match = value.match(/(-?\d*)\.?(\d*)/)
|
629
|
+
return 0 unless match
|
630
|
+
value = match[1].to_i * (10 ** (opt[:precision]||2))
|
631
|
+
cents = match[2]
|
632
|
+
cents = cents + '0' while cents.length < (opt[:precision]||2)
|
633
|
+
cents = cents.to_s[0,opt[:precision]||2]
|
634
|
+
value += cents.to_i * (value<0 ? -1 : 1)
|
635
|
+
value
|
636
|
+
end
|
637
|
+
|
638
|
+
# Returns the integer (cents) value from a Float
|
639
|
+
# rules - hash of fixed-point conversion rules
|
640
|
+
def self.float_to_integer(value, *args)
|
641
|
+
opt = args.last.is_a?(Hash) ? args.pop : {}
|
642
|
+
return (opt[:blank]||nil) if value.nil?
|
643
|
+
value = (value.to_f*(10**((opt[:precision]||2)+1))).to_i/10 # helps rounding 4.56 -> 455 ouch!
|
644
|
+
end
|
645
|
+
|
646
|
+
# Replacing the sprintf function to deal with money as float. "... %[flags]m ..."
|
647
|
+
# rules - hash of fixed-point conversion rules
|
648
|
+
def self.format_fixed_point(value, pattern="%.2m", *args)
|
649
|
+
opt = args.last.is_a?(Hash) ? args.pop : {}
|
650
|
+
sign = value < 0 ? -1 : 1
|
651
|
+
dollars, cents = value.abs.divmod( 10 ** (opt[:precision]||2))
|
652
|
+
dollars *= sign
|
653
|
+
parts = pattern.match(/^(.*)%([-\. \d+]*)[fm](.*)/)
|
654
|
+
return pattern unless parts
|
655
|
+
intdec = parts[2].match(/(.*)\.(\d*)/)
|
656
|
+
dprec, cprec = intdec ? [intdec[1], intdec[2]] : ['', '']
|
657
|
+
dollars = sprintf("%#{dprec}d", dollars)
|
658
|
+
cents = '0' + cents.to_s while cents.to_s.length < (opt[:precision]||2)
|
659
|
+
cents = cents.to_s[0,cprec.to_i]
|
660
|
+
cents = cents + '0' while cents.length < cprec.to_i
|
661
|
+
value = parts[1] + "#{(dollars.to_i==0 && sign==-1) ? '-' : '' }#{dollars}#{cents>' '? '.':''}#{cents}" + parts[3]
|
662
|
+
value
|
663
|
+
end
|
664
|
+
end # FixedPoint
|
665
|
+
|
666
|
+
|
667
|
+
#############################################################################
|
668
|
+
# EasyAttributes::ClassMethods - Module defining methods added to classes when included
|
669
|
+
#
|
670
|
+
# Examples
|
671
|
+
# class MyClass
|
672
|
+
# include EasyAttributes
|
673
|
+
# attr_values :attr, name:value, ...
|
674
|
+
# attr_enum :attr, :name, ....
|
675
|
+
# attr_define :attr, :attr=>:defined_attr
|
676
|
+
# attr_bytes :attr, ..., :base=>2
|
677
|
+
# attr_money :attr, :precision=>2
|
678
|
+
# attr_fixed :attr, :precision=>2
|
679
|
+
#############################################################################
|
680
|
+
module ClassMethods
|
681
|
+
|
682
|
+
# Public: Defines an attribute with a Hash of symbolic synonyms for the values.
|
683
|
+
#
|
684
|
+
# attribute - symbolic name of the attribute
|
685
|
+
# values - a hash of {symbol:value, ....} mappings
|
686
|
+
# a optional key of :options=>{name:value} defines any options for the attribute
|
687
|
+
# Examples
|
688
|
+
#
|
689
|
+
# attr_values :status, active:1, inactive:2
|
690
|
+
#
|
691
|
+
# Creates these instance methods (for a "status" attribute):
|
692
|
+
#
|
693
|
+
# status_sym() # Returns the symbolic name instead of value
|
694
|
+
# status_sym=(:inactive) # Used for setting the attrivute by symbolic name instead
|
695
|
+
# status_in(symbol, ...) # Returns true if the attribute symbol is in the list of symbols
|
696
|
+
# status_cmp(symbol) # Returns the comparison of the value <=> symbol
|
697
|
+
#
|
698
|
+
# And these class methods:
|
699
|
+
#
|
700
|
+
# status_definition()
|
701
|
+
# Returns the EasyAttributes::Definition for the attribute, on which you can call cool things like
|
702
|
+
# value_of(), symbol_of(), select_options(), etc.
|
703
|
+
#
|
704
|
+
# Returns nothing
|
705
|
+
def attr_values(attribute, *args)
|
706
|
+
#defn = Definition.find_or_create(attribute, *args)
|
707
|
+
defn = Definition.new(attribute, *args)
|
708
|
+
easy_attribute_accessors(attribute, defn)
|
709
|
+
end
|
710
|
+
|
711
|
+
# Public: Defines an attribute as an Enumeration of symbol name.
|
712
|
+
#
|
713
|
+
# By default, the first symbol is mapped to 0 (zero), and increments by 1. The :start and
|
714
|
+
# :step values in the options hash can change those settings. Also, "#next()" is called on
|
715
|
+
# the start value, so any non-numeric object that supports that call can be used (such as
|
716
|
+
# a string). A nil in a position will skip that value, and any other non-symbol will reset
|
717
|
+
# the value of the next symbol to that one.
|
718
|
+
#
|
719
|
+
# This is an alternate syntax for the attr_values() method
|
720
|
+
#
|
721
|
+
# attribute - symbolic name of the attribute
|
722
|
+
# args - a list of symbol names, nil skip tokens, and value changes
|
723
|
+
# * A symbol name will map to the current value
|
724
|
+
# * A nil skips the current value in the list
|
725
|
+
# * Any other value replaces the current value for the subsequent symbol
|
726
|
+
# options - a optional key of :options=>{name:value} defines any options for the attribute
|
727
|
+
#
|
728
|
+
# Examples
|
729
|
+
#
|
730
|
+
# attr_enum :status, :active, :inactive # => {active:0, inactive:1}
|
731
|
+
# attr_enum :month, :jan, :feb, ..., :dec, start:1 # => {jan:1, feb:2, ..., dec:12}
|
732
|
+
# attr_enum :status, :active, :inactive, nil, :suspended, 99, :deleted, start:10, step:10
|
733
|
+
# # => same as: {active:10, inactive:20, suspended:40, deleted:99}
|
734
|
+
#
|
735
|
+
# Creates the same methods as attr_values().
|
736
|
+
#
|
737
|
+
# Returns nothing
|
738
|
+
def attr_enum(attribute, *args)
|
739
|
+
opt = args.last.is_a?(Hash) ? args.pop : {}
|
740
|
+
defn = Definition.new(attribute, args, opt)
|
741
|
+
easy_attribute_accessors(attribute, defn)
|
742
|
+
end
|
743
|
+
|
744
|
+
# Public: Defines an attribute as a set of values. For String-type attributes
|
745
|
+
#
|
746
|
+
# attribute - symbolic name of the attribute
|
747
|
+
# args - a list of allowed string names (symbols can be used for convenience as well)
|
748
|
+
#
|
749
|
+
# Examples
|
750
|
+
#
|
751
|
+
# attr_allowed :type, "mammal", :bird, :insect
|
752
|
+
#
|
753
|
+
# Creates the same methods as attr_values().
|
754
|
+
#
|
755
|
+
# Returns nothing
|
756
|
+
def attr_allowed(attribute, *args)
|
757
|
+
opt = args.last.is_a?(Hash) ? args.pop : {}
|
758
|
+
vals = {}
|
759
|
+
args.flatten.map { |v| vals[v.to_sym] = v.to_s }
|
760
|
+
defn = Definition.new(attribute, vals, opt)
|
761
|
+
easy_attribute_accessors(attribute, defn)
|
762
|
+
end
|
763
|
+
|
764
|
+
# Public: Sets global definitions into the class by attribute name or Hash mapping.
|
765
|
+
#
|
766
|
+
# attributes - List of attributes to import from Config definitions
|
767
|
+
# mappings - Hash of atttribute names to a matching alternate name in the Config definitions
|
768
|
+
#
|
769
|
+
# Examples (call like attr_values or attr_enum)
|
770
|
+
#
|
771
|
+
# attr_define :status, :signup, :confirm, :unreachable, :delete
|
772
|
+
# attr_define :status, signup:1, confirm:2, uncreachable:3, delete:4
|
773
|
+
#
|
774
|
+
# Stores the definition in a symbol table for later attr_shared lookup
|
775
|
+
def attr_define(attribute, *definition)
|
776
|
+
find_or_create(attribute, *definition)
|
777
|
+
end
|
778
|
+
|
779
|
+
# Public: Imports previously defined definitions into the class by attribute name or Hash mapping.
|
780
|
+
#
|
781
|
+
# attributes - List of attributes to import from Config definitions
|
782
|
+
# mappings - Hash of atttribute names to a matching alternate name in the Config definitions
|
783
|
+
#
|
784
|
+
# Examples
|
785
|
+
#
|
786
|
+
# attr_shared :status, :role, widget_type: :general_type, colname: :sharedname
|
787
|
+
# attr_shared employee_status:Employee.status_definition()
|
788
|
+
#
|
789
|
+
# Calls attr_values with each definition
|
790
|
+
def attr_shared(*attributes)
|
791
|
+
mapping = attributes.last.is_a?(Hash) ? attributes.pop : {}
|
792
|
+
attributes.each { |a| install_shared_attribute(a) }
|
793
|
+
mapping.each { |a, shared| install_shared_attribute(a, shared) }
|
794
|
+
end
|
795
|
+
|
796
|
+
def install_shared_attribute(name, shared_name=nil)
|
797
|
+
shared_name ||= name
|
798
|
+
defn = Definition.find_or_create(shared_name)
|
799
|
+
easy_attribute_accessors(name, defn)
|
800
|
+
end
|
801
|
+
|
802
|
+
# Private: Creates attribute accessors for the attribute /definition for attr_values
|
803
|
+
#
|
804
|
+
# Creates these methods dynamically on the host class
|
805
|
+
# self.<attribute>_definition()
|
806
|
+
# <attribute>_sym()
|
807
|
+
# <attribute>_sym=()
|
808
|
+
# <attribute>_in()
|
809
|
+
# <attribute>_cmp()
|
810
|
+
#
|
811
|
+
# If Config.constantize, create ATTRIBUTE_SYMBOL=value constants
|
812
|
+
#
|
813
|
+
def easy_attribute_accessors(attribute, defn)
|
814
|
+
attribute = attribute.to_sym
|
815
|
+
@easy_attribute_definitions ||= {}
|
816
|
+
@easy_attribute_definitions[attribute] = defn
|
817
|
+
opt = defn.options
|
818
|
+
code = ''
|
819
|
+
|
820
|
+
if (EasyAttributes::Config.orm == :active_model || opt[:orm] == :active_model) &&
|
821
|
+
self.respond_to?(:validates_inclusion_of)
|
822
|
+
self.validates_inclusion_of attribute, :in=>defn.symbols.values
|
823
|
+
# Add named_scope (scope) for each value
|
824
|
+
if opt[:named_scope]
|
825
|
+
defn.symbols.each { |k,v| code += "named_scope :#{k}, :conditions=>{:#{attribute}=>#{v.inspect}}\n" }
|
826
|
+
end
|
827
|
+
if opt[:scope]
|
828
|
+
defn.symbols.each { |k,v| code += "scope :#{k}, where({:#{attribute}=>#{v.inspect}})\n" }
|
829
|
+
end
|
830
|
+
else
|
831
|
+
attr_accessor attribute
|
832
|
+
end
|
833
|
+
|
834
|
+
#------------------------------------------------------------------------
|
835
|
+
# Class Methods
|
836
|
+
#------------------------------------------------------------------------
|
837
|
+
|
838
|
+
define_easy_attribute_definition
|
839
|
+
|
840
|
+
# <attribute>_options() Returns an array of (HTML Select) option pairs
|
841
|
+
# => [["Option Name", :symbol], ...]
|
842
|
+
self.define_singleton_method("#{attribute}_options") do |*args|
|
843
|
+
easy_attribute_definition(attribute).select_option_symbols(*args)
|
844
|
+
end
|
845
|
+
|
846
|
+
# <attribute>_of(:sym [,:default_sym]) Returns the symbol/value hash for the attribute
|
847
|
+
self.define_singleton_method("#{attribute}_of") do |*args, &block|
|
848
|
+
easy_attribute_definition(attribute).symbols.fetch(args.first.to_sym) do |sym|
|
849
|
+
if args.size>1
|
850
|
+
easy_attribute_definition(attribute).symbols.fetch(args[1].to_sym, &block)
|
851
|
+
else
|
852
|
+
raise "#{attribute} symbolic name #{sym} not found"
|
853
|
+
end
|
854
|
+
end
|
855
|
+
end
|
856
|
+
|
857
|
+
# Define Constants Model::ATTRIBUTE_SYMBOL = value
|
858
|
+
if Config::constantize
|
859
|
+
easy_attribute_definition(attribute).symbols.each do |sym, value|
|
860
|
+
const_set("#{attribute.upcase}_#{sym.to_s.upcase}", value)
|
861
|
+
end
|
862
|
+
end
|
863
|
+
|
864
|
+
#------------------------------------------------------------------------
|
865
|
+
# Instance Methods
|
866
|
+
#------------------------------------------------------------------------
|
867
|
+
|
868
|
+
# <attribute>_definition()
|
869
|
+
# Returns the definition ojbect for the easy attribute
|
870
|
+
define_method("#{attribute}_definition") do
|
871
|
+
self.class.easy_attribute_definition(attribute)
|
872
|
+
end
|
873
|
+
|
874
|
+
# <attribute>_sym()
|
875
|
+
# Returns the symbolic name of the current value of "attribute"
|
876
|
+
define_method("#{attribute}_sym") do
|
877
|
+
self.class.easy_attribute_definition(attribute).symbol_of(self.send(attribute))
|
878
|
+
end
|
879
|
+
|
880
|
+
# <attribute>_sym=(new_symbol)
|
881
|
+
# Sets the value of "attribute" to the associated value of the passed symbolic name
|
882
|
+
define_method("#{attribute}_sym=") do |sym|
|
883
|
+
self.send("#{attribute}=", self.class.easy_attribute_definition(attribute).value_of(sym))
|
884
|
+
end
|
885
|
+
|
886
|
+
# <attribute>_in(*names)
|
887
|
+
# Returns true if the symbolic name of the current value of "attribute" is in the list of names.
|
888
|
+
define_method("#{attribute}_in") do |*args|
|
889
|
+
self.class.easy_attribute_definition(attribute).value_in(self.send(attribute),*args)
|
890
|
+
end
|
891
|
+
|
892
|
+
# <attribute>_cmp(other)
|
893
|
+
# Standard "cmp" or <=> compare for "attribute" against a symbolic name.
|
894
|
+
define_method("#{attribute}_cmp") do |other|
|
895
|
+
self.class.easy_attribute_definition(attribute).cmp(self.send(attribute),other)
|
896
|
+
end
|
897
|
+
end
|
898
|
+
|
899
|
+
# Adds once to class: Returns the EasyAttribute::Definition of the passed
|
900
|
+
def define_easy_attribute_definition
|
901
|
+
unless self.respond_to?(:easy_attribute_definition)
|
902
|
+
define_singleton_method(:easy_attribute_definition) do |attrib|
|
903
|
+
@easy_attribute_definitions.fetch(attrib.to_sym) { raise "EasyAttribute #{attrib} not found" }
|
904
|
+
end
|
905
|
+
end
|
906
|
+
end
|
907
|
+
|
908
|
+
# Public: Adds byte attributes helpers to the class
|
909
|
+
# attr_bytes allows manipultion and display as kb, mb, gb, tb, pb
|
910
|
+
# Adds method: attribute_bytes=() and attribute_bytes(:unit, :option=>value )
|
911
|
+
#
|
912
|
+
# attribites - List of attribute names to generate helpers for
|
913
|
+
# options - Hash of byte helper options
|
914
|
+
#
|
915
|
+
# Example
|
916
|
+
#
|
917
|
+
# attr_bytes :bandwidth
|
918
|
+
# attr_bytes :storage, :kb_size=>1000, :precision=>2
|
919
|
+
#
|
920
|
+
# Adds the following helpers
|
921
|
+
#
|
922
|
+
# bandwidth_bytes() # => "10 GB"
|
923
|
+
# bandwidth_bytes=("10 GB") # => 10_000_000_000
|
924
|
+
#
|
925
|
+
def attr_bytes(*args)
|
926
|
+
define_easy_attribute_definition
|
927
|
+
@easy_attribute_definitions ||= {}
|
928
|
+
opt = args.last.is_a?(Hash) ? args.op : {}
|
929
|
+
|
930
|
+
args.each do |attribute|
|
931
|
+
attribute = attribute.to_sym
|
932
|
+
unless EasyAttributes::Config.orm == :active_model || opt[:orm] == :active_model
|
933
|
+
attr_accessor attribute if EasyAttributes::Config.orm == :attr
|
934
|
+
end
|
935
|
+
#defn = Definition.find_or_create(attribute, {}, opt)
|
936
|
+
defn = Definition.new(attribute, {}, opt)
|
937
|
+
@easy_attribute_definitions[attribute] = defn
|
938
|
+
|
939
|
+
# <attribute>_bytes()
|
940
|
+
# Returns the symbolic name of the current value of "attribute"
|
941
|
+
define_method("#{attribute}_bytes") do |*bargs|
|
942
|
+
self.class.easy_attribute_definition(attribute).format_bytes(self.send(attribute), *bargs)
|
943
|
+
end
|
944
|
+
|
945
|
+
# <attribute>_bytes=(new_symbol)
|
946
|
+
# Sets the value of "attribute" to the associated value of the passed symbolic name
|
947
|
+
define_method("#{attribute}_bytes=") do |sym|
|
948
|
+
self.send("#{attribute}=", self.class.easy_attribute_definition(attribute).parse_bytes(sym))
|
949
|
+
end
|
950
|
+
end
|
951
|
+
end
|
952
|
+
|
953
|
+
# Public: Adds methods to get and set a fixed-point value stored as an integer.
|
954
|
+
#
|
955
|
+
# attributes - list of attribute names to define
|
956
|
+
# options - Optional hash of definitions for the given list of attributes
|
957
|
+
# :method_suffix - Use this as the alternative name to the *_fixed method names
|
958
|
+
# :units - Use this as an alternative suffix name to the money methods ('dollars' gives 'xx_dollars')
|
959
|
+
# :precision - The number of digits implied after the decimal, default is 2
|
960
|
+
# :separator - The character to use after the integer part, default is '.'
|
961
|
+
# :delimiter - The character to use between every 3 digits of the integer part, default none
|
962
|
+
# :positive - The sprintf format to use for positive numbers, default is based on precision
|
963
|
+
# :negative - The sprintf format to use for negative numbers, default is same as :positive
|
964
|
+
# :zero - The sprintf format to use for zero, default is same as :positive
|
965
|
+
# :nil - The sprintf format to use for nil values, default none
|
966
|
+
# :unit - Prepend this to the front of the money value, say '$', default none
|
967
|
+
# :blank - Return this value when the money string is empty or has no digits on assignment
|
968
|
+
# :negative_regex - A Regular Expression used to determine if a number is negative (and without a - sign)
|
969
|
+
#
|
970
|
+
# Examples:
|
971
|
+
# attr_fixed :gpa, precision:1
|
972
|
+
# attr_fixed :price, precision:2
|
973
|
+
#
|
974
|
+
# Adds the following helpers
|
975
|
+
#
|
976
|
+
# gpa_fixed() #=> "3.8"
|
977
|
+
# gpa_fixed=("3.8") #=> 38
|
978
|
+
# gpa_float() #=> 3.8
|
979
|
+
#
|
980
|
+
# Returns nothing
|
981
|
+
def attr_fixed(*args)
|
982
|
+
define_easy_attribute_definition
|
983
|
+
@easy_attribute_definitions ||= {}
|
984
|
+
opt = args.last.is_a?(Hash) ? args.pop : {}
|
985
|
+
suffix = opt.fetch(:method_suffix) { 'fixed' }
|
986
|
+
|
987
|
+
args.each do |attribute|
|
988
|
+
attribute = attribute.to_sym
|
989
|
+
unless EasyAttributes::Config.orm == :active_model || opt[:orm] == :active_model
|
990
|
+
attr_accessor attribute if EasyAttributes::Config.orm == :attr
|
991
|
+
end
|
992
|
+
defn = Definition.new(attribute, {}, opt)
|
993
|
+
@easy_attribute_definitions[attribute] = defn
|
994
|
+
|
995
|
+
# <attribute>_fixed()
|
996
|
+
# Returns the symbolic name of the current value of "attribute"
|
997
|
+
define_method("#{attribute}_#{suffix}") do
|
998
|
+
FixedPoint::integer_to_fixed_point(send(attribute), self.class.easy_attribute_definition(attribute).attr_options)
|
999
|
+
end
|
1000
|
+
|
1001
|
+
# <attribute>_fixed=(new_value)
|
1002
|
+
# Sets the value of "attribute" to the associated value of the passed symbolic name
|
1003
|
+
define_method("#{attribute}_#{suffix}=") do |val|
|
1004
|
+
self.send("#{attribute}=", FixedPoint::fixed_point_to_integer(val,
|
1005
|
+
self.class.easy_attribute_definition(attribute).attr_options))
|
1006
|
+
end
|
1007
|
+
|
1008
|
+
# <attribute>_float()
|
1009
|
+
# Returns the value of the attribite as a float with desired precision point
|
1010
|
+
define_method("#{attribute}_float") do
|
1011
|
+
FixedPoint::integer_to_float(send(attribute), self.class.easy_attribute_definition(attribute).attr_options)
|
1012
|
+
end
|
1013
|
+
end
|
1014
|
+
end
|
1015
|
+
|
1016
|
+
# Public: Alias of attr_fixed for a money type with suffix of 'money' and precision of 2.
|
1017
|
+
#
|
1018
|
+
# args - list of money attributes
|
1019
|
+
# options - hash of attr_fixed options.
|
1020
|
+
#
|
1021
|
+
# Examples:
|
1022
|
+
# attr_money :price
|
1023
|
+
# attr_money :wager, method_suffix:'quatloos', precision:1, unit:'QL'
|
1024
|
+
#
|
1025
|
+
# Adds the following helpers
|
1026
|
+
#
|
1027
|
+
# price_money() #=> "42.00"
|
1028
|
+
# price_money=("3.8") #=> 380
|
1029
|
+
# price_float() #=> 3.8
|
1030
|
+
#
|
1031
|
+
def attr_money(*args)
|
1032
|
+
opt = args.last.is_a?(Hash) ? args.pop : {}
|
1033
|
+
opt = {method_suffix:'money', precision:2}.merge(opt)
|
1034
|
+
args << opt
|
1035
|
+
attr_fixed(*args)
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
def attr_dollars(*args)
|
1039
|
+
opt = args.last.is_a?(Hash) ? args.pop : {}
|
1040
|
+
attr_money(*args, opt.merge(method_suffix:'dollars'))
|
1041
|
+
end
|
1042
|
+
|
1043
|
+
end # EasyAttributes::ClassMethods
|
1044
|
+
end # EasyAttributes Module
|