maskable_attribute 0.0.1

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.
Files changed (67) hide show
  1. data/.gitignore +7 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +17 -0
  4. data/Gemfile.lock +37 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.md +56 -0
  7. data/Rakefile +37 -0
  8. data/lib/maskable_attribute.rb +6 -0
  9. data/lib/maskable_attribute/acts_as_maskable_attribute.rb +48 -0
  10. data/lib/maskable_attribute/formatting.rb +71 -0
  11. data/lib/maskable_attribute/mask.rb +112 -0
  12. data/lib/maskable_attribute/maskable_attribute.rb +58 -0
  13. data/lib/maskable_attribute/version.rb +3 -0
  14. data/lib/tasks/maskable_attribute_tasks.rake +4 -0
  15. data/maskable_attribute.gemspec +22 -0
  16. data/test/dummy/Rakefile +10 -0
  17. data/test/dummy/app/controllers/application_controller.rb +10 -0
  18. data/test/dummy/app/helpers/application_helper.rb +3 -0
  19. data/test/dummy/app/models/hickwell.rb +6 -0
  20. data/test/dummy/config/boot.rb +128 -0
  21. data/test/dummy/config/database.yml +22 -0
  22. data/test/dummy/config/environment.rb +41 -0
  23. data/test/dummy/config/environments/development.rb +17 -0
  24. data/test/dummy/config/environments/production.rb +28 -0
  25. data/test/dummy/config/environments/test.rb +28 -0
  26. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  27. data/test/dummy/config/initializers/cookie_verification_secret.rb +7 -0
  28. data/test/dummy/config/initializers/inflections.rb +10 -0
  29. data/test/dummy/config/initializers/mime_types.rb +5 -0
  30. data/test/dummy/config/initializers/new_rails_defaults.rb +21 -0
  31. data/test/dummy/config/initializers/session_store.rb +15 -0
  32. data/test/dummy/config/locales/en.yml +5 -0
  33. data/test/dummy/config/preinitializer.rb +20 -0
  34. data/test/dummy/config/routes.rb +43 -0
  35. data/test/dummy/db/migrate/20111220153853_create_hickwells.rb +15 -0
  36. data/test/dummy/db/schema.rb +23 -0
  37. data/test/dummy/db/seeds.rb +7 -0
  38. data/test/dummy/public/404.html +30 -0
  39. data/test/dummy/public/422.html +30 -0
  40. data/test/dummy/public/500.html +30 -0
  41. data/test/dummy/public/favicon.ico +0 -0
  42. data/test/dummy/public/images/rails.png +0 -0
  43. data/test/dummy/public/index.html +275 -0
  44. data/test/dummy/public/javascripts/application.js +2 -0
  45. data/test/dummy/public/javascripts/controls.js +963 -0
  46. data/test/dummy/public/javascripts/dragdrop.js +973 -0
  47. data/test/dummy/public/javascripts/effects.js +1128 -0
  48. data/test/dummy/public/javascripts/prototype.js +4320 -0
  49. data/test/dummy/public/robots.txt +5 -0
  50. data/test/dummy/script/about +4 -0
  51. data/test/dummy/script/console +3 -0
  52. data/test/dummy/script/dbconsole +3 -0
  53. data/test/dummy/script/destroy +3 -0
  54. data/test/dummy/script/generate +3 -0
  55. data/test/dummy/script/performance/benchmarker +3 -0
  56. data/test/dummy/script/performance/profiler +3 -0
  57. data/test/dummy/script/plugin +3 -0
  58. data/test/dummy/script/runner +3 -0
  59. data/test/dummy/script/server +3 -0
  60. data/test/dummy/test/fixtures/hickwells.yml +9 -0
  61. data/test/dummy/test/test_helper.rb +40 -0
  62. data/test/dummy/test/unit/hickwell_test.rb +31 -0
  63. data/test/formatting_test.rb +71 -0
  64. data/test/mask_test.rb +95 -0
  65. data/test/maskable_attribute_test.rb +163 -0
  66. data/test/test_helper.rb +10 -0
  67. metadata +174 -0
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ .*.swp
2
+ .bundle/
3
+ log/*.log
4
+ pkg/
5
+ test/dummy/db/*.sqlite3
6
+ test/dummy/log/*.log
7
+ test/dummy/tmp/
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.2-p290@maskable_attribute
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rails", "~> 2.3.10"
4
+ gem "sqlite3"
5
+
6
+ # Declare your gem's dependencies in maskable_attribute.gemspec.
7
+ # Bundler will treat runtime dependencies like base dependencies, and
8
+ # development dependencies will be added by default to the :development group.
9
+ gemspec
10
+
11
+ # Declare any dependencies that are still in development here instead of in
12
+ # your gemspec. These might include edge Rails or gems from your path or
13
+ # Git. Remember to move these dependencies to your gemspec before releasing
14
+ # your gem to rubygems.org.
15
+
16
+ # To use debugger
17
+ # gem 'ruby-debug19', :require => 'ruby-debug'
data/Gemfile.lock ADDED
@@ -0,0 +1,37 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ maskable_attribute (0.0.1)
5
+ rails (>= 2.3.10)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ actionmailer (2.3.14)
11
+ actionpack (= 2.3.14)
12
+ actionpack (2.3.14)
13
+ activesupport (= 2.3.14)
14
+ rack (~> 1.1.0)
15
+ activerecord (2.3.14)
16
+ activesupport (= 2.3.14)
17
+ activeresource (2.3.14)
18
+ activesupport (= 2.3.14)
19
+ activesupport (2.3.14)
20
+ rack (1.1.3)
21
+ rails (2.3.14)
22
+ actionmailer (= 2.3.14)
23
+ actionpack (= 2.3.14)
24
+ activerecord (= 2.3.14)
25
+ activeresource (= 2.3.14)
26
+ activesupport (= 2.3.14)
27
+ rake (>= 0.8.3)
28
+ rake (0.9.2.2)
29
+ sqlite3 (1.3.6)
30
+
31
+ PLATFORMS
32
+ ruby
33
+
34
+ DEPENDENCIES
35
+ maskable_attribute!
36
+ rails (~> 2.3.10)
37
+ sqlite3
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2011 YOURNAME
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.md ADDED
@@ -0,0 +1,56 @@
1
+ # Maskable Attribute
2
+
3
+ maskable_attribute is an ActiveRecord extension that allows for 'masking' a records' attribute. It provides the ability to have an objects' attribute be dynamically assigned at a given point in time while providing the capability to make a one-location change that propagates through all resulting objects containing the resulting attribute mask.
4
+
5
+ ## Install
6
+
7
+ ### Via rubygems:
8
+
9
+ gem install maskable_attribute
10
+
11
+ ### Via bundler:
12
+
13
+ Add the following line to Gemfile:
14
+
15
+ gem 'maskable_attribute'
16
+
17
+ Then, run bundler:
18
+
19
+ bundle install
20
+
21
+ ## Usage
22
+
23
+ To use the maskable_attribute gem, simply create corresponding maskable_attribute attributes in the ActiveRecord model that you wish to affect.
24
+
25
+ For example, the following User class has a maskable attribute of :foo that can contain values of :bar, :baz and :qux
26
+
27
+ class User < ActiveRecord::Base
28
+ maskable_attribute :foo, [ :bar, :baz ]
29
+ end
30
+
31
+ Creating a user with the above mask can be performed via the following:
32
+
33
+ user = User.new :bar => "a", :baz => "b", :foo => "{bar}-{baz}"
34
+
35
+ Now that the user object is created, there are several accessor methods to return different variations of the attribute:
36
+
37
+ user.masks # { :foo => { :bar, :baz } }
38
+ user.foo.masks # [ :bar, :baz ]
39
+ user.foo # "a-b"
40
+ user.foo.unmasked # "{bar}-{baz}"
41
+
42
+ ## Supported Ruby versions
43
+
44
+ Maskable Attribute supports Ruby 1.9.x+
45
+
46
+ ## Additional Information
47
+
48
+ * [Git](https://github.com/billy-ran-away/maskable_attribute)
49
+
50
+ ## Contributing
51
+
52
+ Contributions to the code-base are welcomed and handled via the normal Github pull request process.
53
+
54
+ ## Owner
55
+
56
+ maskable_attribute is written and maintained by Bill Transue
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'MaskableAttribute'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+
24
+
25
+ Bundler::GemHelper.install_tasks
26
+
27
+ require 'rake/testtask'
28
+
29
+ Rake::TestTask.new(:test) do |t|
30
+ t.libs << 'lib'
31
+ t.libs << 'test'
32
+ t.pattern = 'test/**/*_test.rb'
33
+ t.verbose = false
34
+ end
35
+
36
+
37
+ task :default => :test
@@ -0,0 +1,6 @@
1
+ require "maskable_attribute/acts_as_maskable_attribute"
2
+ require "maskable_attribute/maskable_attribute"
3
+ require "maskable_attribute/mask"
4
+ require "maskable_attribute/formatting"
5
+ module MaskableAttribute
6
+ end
@@ -0,0 +1,48 @@
1
+ module MaskableAttribute
2
+ module ActsAsMaskableAttribute
3
+
4
+ def self.included(base)
5
+ base.send :extend, ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ ##
10
+ # Specifies an attribute to be masked, followed by masks to be made available to the attribute.
11
+ #
12
+ # ==== Examples
13
+ #
14
+ # class Foo < ActiveRecord::Base
15
+ # maskable_attrribute :some_attribute, [ :some_method_be_used_as_a_mask, :another_attribute_mask ]
16
+ # end
17
+
18
+ def maskable_attribute(attribute_to_mask, masks)
19
+ raise ArgumentError, "invalid argument (expected attribute)" unless column_names.include? attribute_to_mask.to_s
20
+
21
+ cattr_accessor :masks
22
+ self.masks ||= {}
23
+ self.masks[attribute_to_mask] = masks
24
+ self.masks
25
+
26
+ define_method attribute_to_mask do
27
+ send("maskable_#{attribute_to_mask}").to_s
28
+ end
29
+
30
+ define_method "#{attribute_to_mask}=" do |value|
31
+ write_attribute attribute_to_mask, masked_attribute(attribute_to_mask).set(value)
32
+ end
33
+
34
+ define_method "maskable_#{attribute_to_mask}" do
35
+ masked_attribute attribute_to_mask
36
+ end
37
+ end
38
+ end
39
+
40
+ attr_accessor :masked_attribute
41
+
42
+ def masked_attribute(attribute)
43
+ @masked_attribute ||= MaskableAttribute.new self, attribute, self.class.masks[attribute]
44
+ end
45
+ end
46
+ end
47
+
48
+ ActiveRecord::Base.send :include, MaskableAttribute::ActsAsMaskableAttribute
@@ -0,0 +1,71 @@
1
+ module MaskableAttribute
2
+ module Formatting
3
+ class Formats < Hash
4
+
5
+ #{ :format => :two_digit }
6
+ #{ :formats => [ :upcase, :downcase ] }
7
+ #{ :exclusive_format => { :capitalized => Proc.new{ |mask| mask.captialized } } }
8
+ #{ :exclusive_formats =>{ :capitalized => Proc.new{ |mask| mask.captialized }, :titleized => :titleize } }
9
+ #{ :default_format => :titleize }
10
+ attr_accessor :are_exclusive, :has_default, :default
11
+ def initialize(options = {})
12
+ @are_exclusive, @has_default = false, false
13
+ @default = nil
14
+ unless options.nil?
15
+ options.each do |type, formats|
16
+ if formats.is_a? Symbol or formats.is_a? Proc
17
+ self[formats] = Format.new formats
18
+ elsif formats.is_a? Array
19
+ formats.each do |format|
20
+ self[format] = Format.new format
21
+ end
22
+ else
23
+ formats.each do |format, method|
24
+ self[format] = Format.new method || format
25
+ end
26
+ end
27
+
28
+ if type.to_s.include? "exclusive"
29
+ @are_exclusive = true
30
+ elsif type.to_s.include? "default"
31
+ @has_default = true
32
+ @default = self[formats]
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ def apply(format, &block)
39
+ fetch(format, (has_default? ? @default : Format.new)).apply yield unless yield.nil?
40
+ end
41
+
42
+ def names
43
+ keys
44
+ end
45
+
46
+ def are_exclusive?
47
+ @are_exclusive
48
+ end
49
+
50
+ def has_default?
51
+ @has_default
52
+ end
53
+ end
54
+
55
+ class Format
56
+ def initialize(method=nil)
57
+ @method = method
58
+ end
59
+
60
+ def apply(input)
61
+ if @method.is_a? Symbol
62
+ input.send(@method)
63
+ elsif @method.is_a? Proc
64
+ @method.call input
65
+ else
66
+ input
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,112 @@
1
+ module MaskableAttribute
2
+ class Masks
3
+ include Enumerable
4
+
5
+ #:bar
6
+ #:foo => :bar
7
+ #:foo => [ :bar, baz, :qux ]
8
+ #:foo => { :format => :two_digit }
9
+ #:foo => { :formats => [ :two_digit, :upcase, :downcase ] }
10
+ #:bar => { :exclusive_format => { :capitalized => Proc.new{ |mask| mask.captialized } } }
11
+ #:bar => { :exclusive_formats => { :capitalized => Proc.new{ |mask| mask.captialized }, :titleized => :titleize } }
12
+ #:baz => { :default_format => :titleize }
13
+ #:bar => [ :quux, { :formats => ... } ]
14
+ #:bar => [ Proc.new { |object| object.size * 3 }, { :formats => ... } ]
15
+ def initialize(masks)
16
+ @masks = masks.map do |mask|
17
+ if mask.is_a? Array
18
+ Mask.new mask.first => mask.last
19
+ else
20
+ Mask.new mask
21
+ end
22
+ end
23
+ end
24
+
25
+ def each(&block)
26
+ @masks.each(&block)
27
+ return self
28
+ end
29
+
30
+ def [](accessor)
31
+ (find_by_accessor(accessor) || Mask.new).accessor(accessor)
32
+ end
33
+
34
+ def find_by_accessor(accessor)
35
+ @masks.find do |mask|
36
+ mask.accessed_by? accessor
37
+ end
38
+ end
39
+
40
+ def names
41
+ @masks.map(&:name).map(&:to_sym)
42
+ end
43
+ end
44
+
45
+ class Mask
46
+ attr_accessor :accessor, :name, :method, :formats
47
+
48
+ def initialize(options=nil)
49
+ if options.is_a? Symbol
50
+ @name = @method = options.to_sym
51
+ options = {}
52
+ elsif options.is_a? Hash
53
+ @name, options = options.flatten
54
+ if options.is_a? Symbol or options.is_a? Proc
55
+ @method = options
56
+ options = {}
57
+ else
58
+ if options.is_a? Array
59
+ @method = options.shift
60
+ options = options.extract_options!
61
+ else
62
+ @method = @name
63
+ end
64
+ end
65
+ end
66
+ @formats = Formatting::Formats.new options
67
+ end
68
+
69
+ def name
70
+ @name.to_s.tr "_", " "
71
+ end
72
+
73
+ def accessor(*accessed_with)
74
+ if accessed_with.empty?
75
+ @accessor || ""
76
+ else
77
+ @accessor = accessed_with.first
78
+ self
79
+ end
80
+ end
81
+
82
+ def unmask(*args)
83
+ object = args.first
84
+ options = args.extract_options!
85
+ format = options[:formatted] || accessor.sub(@name.to_s, "").strip.to_sym
86
+
87
+ formats.apply format do
88
+ begin
89
+ if @method.is_a? Proc
90
+ @method.call object
91
+ elsif @method.is_a? Symbol
92
+ object.send(@method) if object.respond_to? @method
93
+ else
94
+ nil
95
+ end
96
+ rescue NoMethodError
97
+ nil
98
+ end
99
+ end
100
+ end
101
+
102
+ def accessed_by
103
+ formats.names.map { |format| format.to_s + " " + name}.tap do |accessed_by|
104
+ accessed_by.push name unless formats.are_exclusive?
105
+ end
106
+ end
107
+
108
+ def accessed_by?(possible_accessor)
109
+ accessed_by.include? possible_accessor.tr('_', ' ')
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,58 @@
1
+ module MaskableAttribute
2
+ class MaskableAttribute
3
+ attr_accessor :object, :attribute, :masks
4
+
5
+ def initialize(object, attribute, masks)
6
+ @object = object
7
+ @attribute = attribute
8
+ @masks = Masks.new masks
9
+ end
10
+
11
+ def masks
12
+ @masks.names
13
+ end
14
+
15
+ def masked
16
+ value = unmasked
17
+ if !value.blank? and value.match(/\{.*\}/)
18
+ value.scan(/(?<={)[^}]+(?=})/).each do |mask| #mask: two_digit model_series
19
+ value = value.sub "{#{mask}}", @masks[mask].unmask(@object).to_s unless @masks[mask].unmask(@object).nil?
20
+ end
21
+ end
22
+ value
23
+ end
24
+
25
+ alias :to_s :masked
26
+
27
+ def masked_object
28
+ @object
29
+ end
30
+
31
+ def unmasked
32
+ @object.read_attribute attribute
33
+ end
34
+
35
+ def set(value)
36
+ unless value.blank?
37
+ @masks.each do |mask|
38
+ mask.accessed_by.each do |mask_accessor|
39
+ value.sub! /#{mask.unmask(@object, :formatted => mask_accessor)}(?![^{]*})/, "{#{mask_accessor}}" unless mask.unmask(@object).blank?
40
+ end
41
+ end
42
+ end
43
+ value
44
+ end
45
+
46
+ class InvalidMask < RuntimeError
47
+ attr :mask, :obj
48
+ def initialize(mask, obj)
49
+ @mask = mask
50
+ @obj = obj
51
+ end
52
+
53
+ def to_s
54
+ "Invalid mask '#{@mask}' for #{@obj.class.name}"
55
+ end
56
+ end
57
+ end
58
+ end