simple_descriptor 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1 @@
1
+ 2006 11 26 first release
data/README ADDED
@@ -0,0 +1,165 @@
1
+ = Simple Descriptor -- manage class specific informations
2
+
3
+ Simple Descriptor is a library that allows you add extra information about
4
+ your class using the class itself.
5
+ It provides a collection of class methods to store and manage the extra
6
+ informations about the class.
7
+
8
+ The basic problem it tries to solve is to group your instance method and
9
+ to bind to the instance method some values if needed.
10
+
11
+ Even if this library was conceived to be used in the rails framework it
12
+ doesn't require any part of the rails framework to be used.
13
+
14
+ == A first example: defining required fields in an ActiveRecord class
15
+
16
+ Let's start with a Employee class which defines the fields name, surname and
17
+ address that are required
18
+
19
+ class Employee < ActiveRecord::Base
20
+ extend SimpleDescriptor::ClassDescriptor
21
+ describe :name, :surname, :address, :as => :required
22
+ end
23
+
24
+ In the extend line we're simply including calling the SimpleDescriptor so
25
+ we can use it from the within of our class
26
+ In the second line we declare or better, describe as required the fields.
27
+ The describe method just adds the descriptions to our class, in this case we
28
+ have described :name, :surname and :address as :required
29
+
30
+ Until now we have just added a description, but we're not doing any validation
31
+ of the data, so let's add the validation in the model
32
+
33
+ class Employee < ActiveRecord::Base
34
+ extend SimpleDescriptor::ClassDescriptor
35
+ describe :name, :surname, :address, :as => :required
36
+ validates_presence_of described_as(:required)
37
+ end
38
+
39
+ The described_as method returns all the elements described with the specified
40
+ argument. So now we're actually enforcing the data validation in our model
41
+
42
+ But the Employee class now can be asked about its required fields using
43
+
44
+ Employee.described_as :required
45
+
46
+ will return the list of all the required fields
47
+
48
+ Or if we're interested in a single field
49
+
50
+ Employee.describe? :name, :as => :required
51
+
52
+ this will return true since :name was described as required
53
+
54
+ So, now if we want to do something specific (i.e. setting a red colour) in our
55
+ views for the required fields
56
+
57
+ def custom_helper(object, method)
58
+ colour = "green"
59
+ if object.class.describes? method, :as => :required
60
+ colour = "red"
61
+ end
62
+ do_the other display things
63
+ end
64
+
65
+ If the descriptions are active for every model in the application this helper
66
+ will always work and will display in red colour every required field.
67
+
68
+ == Another example: defining maximum length of fields in an ActiveRecord class
69
+
70
+ Simple Descriptor allows to specify a value binded to the given description.
71
+ We want to limit the length of name and surname to 10 characters and the
72
+ address to 50
73
+
74
+ class Employee < ActiveRecord::Base
75
+ extend SimpleDescriptor::ClassDescriptor
76
+ describe :name, :surname, :as => :limited, :with_value => 10
77
+ describe :address, :as => :limited, :with_value => 50
78
+ described_as(:limited).each do |lim_field|
79
+ validates_length_of lim_field, :maximum => self.description_of(lim_field, :as => :limited)
80
+ end
81
+ end
82
+
83
+ In the code above name surname and address are described as limited with a value
84
+ and the limit check is enforced using standard rails validation.
85
+
86
+ The description_of method returns the value for a given description
87
+
88
+ The code above is a bit verbose, that's why Simple descriptor provides
89
+ shortcuts let's rewrite a part of the code above using shortcuts
90
+
91
+ class Employee < ActiveRecord::Base
92
+ extend SimpleDescriptor::ClassDescriptor
93
+ describe :name, :surname, :as => :limited, :with_value => 10
94
+ describe :address, :as => :limited, :with_value => 50
95
+ shortcut_description :limited, :as => :maxlength
96
+ described_as(:limited).each do |lim_field|
97
+ validates_length_of lim_field, :maximum => maxlength(lim_field)
98
+ end
99
+ end
100
+
101
+ The line
102
+
103
+ shortcut_description :limited, :as => :maxlength
104
+
105
+ adds some new methods to our class that makes more easy to access a description
106
+
107
+ == Where are the description stored and how?
108
+
109
+ All the descriptions are stored in a DESCRIPTIONS constant, you can always
110
+ access them using Employee::DESCRIPTIONS
111
+ Descriptions are just a collection of hashes
112
+
113
+ == Are descriptions just for instance methods?
114
+
115
+ No, in theory in any class you can describe any entity as anything, but be
116
+ careful since the tests cover just a limited range of use-case. You're strongly
117
+ invited to submit new tests to cover your usage of the library
118
+
119
+ Example of non method related usage of descriptions:
120
+
121
+ class Silly
122
+ extend SimpleDescriptor::ClassDescriptor
123
+ describe "this is a string" :as => String
124
+ describe "this is a string" :as => String
125
+ describe :dog, :as => :animal
126
+ describe 10, :as => :integer, :with_value => 'ten'
127
+ end
128
+
129
+ All the descriptions specified above are valid are not related to any entity of
130
+ the Silly class
131
+
132
+ == Configuration
133
+
134
+ No configuration is needed to use the library, just extend your class
135
+
136
+
137
+ == Dependencies
138
+
139
+ No dependencies are required
140
+
141
+ == Download
142
+
143
+ The latest version of Simple Descriptor can be found at
144
+
145
+ http://rubyforge.org/projects/simpledesc/
146
+
147
+ Documentation can be found at
148
+
149
+ http://simpledesc.rubyforge.org/
150
+
151
+ == Installation
152
+
153
+ Installation can be done fomr gem command
154
+
155
+ == License
156
+
157
+ Simple Descriptor is released under the MIT license.
158
+
159
+ == Support
160
+
161
+ http://rubyforge.org/projects/simpledesc/
162
+
163
+ == Author
164
+
165
+ Original Author of Simple Descriptor is Paolo Negri
data/Rakefile ADDED
@@ -0,0 +1,85 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/clean'
4
+ require 'rake/testtask'
5
+ require 'rake/packagetask'
6
+ require 'rake/gempackagetask'
7
+ require 'rake/rdoctask'
8
+ require 'rake/contrib/rubyforgepublisher'
9
+ require 'fileutils'
10
+ include FileUtils
11
+ require File.join(File.dirname(__FILE__), 'lib', 'simple_descriptor', 'version')
12
+
13
+ AUTHOR = "blank"
14
+ EMAIL = "your contact email for bug fixes and info"
15
+ DESCRIPTION = "description of gem"
16
+ RUBYFORGE_PROJECT = "simple_descriptor"
17
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
18
+ BIN_FILES = %w( )
19
+
20
+
21
+ NAME = "simple_descriptor"
22
+ REV = File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
23
+ VERS = ENV['VERSION'] || (SimpleDescriptor::VERSION::STRING + (REV ? ".#{REV}" : ""))
24
+ CLEAN.include ['**/.*.sw?', '*.gem', '.config']
25
+ RDOC_OPTS = ['--quiet', '--title', "simple_descriptor documentation",
26
+ "--opname", "index.html",
27
+ "--line-numbers",
28
+ "--main", "README",
29
+ "--inline-source"]
30
+
31
+ desc "Packages up simple_descriptor gem."
32
+ task :default => [:test]
33
+ task :package => [:clean]
34
+
35
+ Rake::TestTask.new("test") { |t|
36
+ t.libs << "test"
37
+ t.pattern = "test/**/*_test.rb"
38
+ t.verbose = true
39
+ }
40
+
41
+ spec =
42
+ Gem::Specification.new do |s|
43
+ s.name = NAME
44
+ s.version = VERS
45
+ s.platform = Gem::Platform::RUBY
46
+ s.has_rdoc = true
47
+ s.extra_rdoc_files = ["README", "CHANGELOG"]
48
+ s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)/']
49
+ s.summary = DESCRIPTION
50
+ s.description = DESCRIPTION
51
+ s.author = AUTHOR
52
+ s.email = EMAIL
53
+ s.homepage = HOMEPATH
54
+ s.executables = BIN_FILES
55
+ s.rubyforge_project = RUBYFORGE_PROJECT
56
+ s.bindir = "bin"
57
+ s.require_path = "lib"
58
+ s.autorequire = "simple_descriptor"
59
+
60
+ #s.add_dependency('activesupport', '>=1.3.1')
61
+ #s.required_ruby_version = '>= 1.8.2'
62
+
63
+ s.files = %w(README CHANGELOG Rakefile) +
64
+ Dir.glob("{bin,doc,test,lib,templates,generator,extras,website,script}/**/*") +
65
+ Dir.glob("ext/**/*.{h,c,rb}") +
66
+ Dir.glob("examples/**/*.rb") +
67
+ Dir.glob("tools/*.rb")
68
+
69
+ # s.extensions = FileList["ext/**/extconf.rb"].to_a
70
+ end
71
+
72
+ Rake::GemPackageTask.new(spec) do |p|
73
+ p.need_tar = true
74
+ p.gem_spec = spec
75
+ end
76
+
77
+ task :install do
78
+ name = "#{NAME}-#{VERS}.gem"
79
+ sh %{rake package}
80
+ sh %{sudo gem install pkg/#{name}}
81
+ end
82
+
83
+ task :uninstall => [:clean] do
84
+ sh %{sudo gem uninstall #{NAME}}
85
+ end
@@ -0,0 +1,181 @@
1
+ module SimpleDescriptor #:nodoc:
2
+ module ClassDescriptor
3
+ LEGAL_DESCRIBE_OPTIONS = [:as, :with_value]
4
+ def create_descriptions #:nodoc:
5
+ class_eval "DESCRIPTIONS = Hash.new({})"
6
+ end
7
+ #returns all the descriptions for the current class
8
+ def descriptions
9
+ class_eval "DESCRIPTIONS"
10
+ end
11
+ #adds a description to the current set
12
+ #examples
13
+ #
14
+ # Person.describe :name, :surname, :as => :required
15
+ # Person.describe :heigth, :eye_colour, :as => :public_data
16
+ # Person.describe :religion, :as => :sensible_data
17
+ #
18
+ #It's possible to specify an optional value for your descriptions
19
+ #using the :with_value option
20
+ #example
21
+ # Person.describe :name, :surname, :as => :limited, :with_value => 30
22
+ #
23
+ def describe(*args)
24
+ raise ArgumentError unless args.last.is_a?(Hash)
25
+ options = args.pop
26
+ create_descriptions unless self.constants.member?('DESCRIPTIONS')
27
+ args.flatten.each do |arg|
28
+ puts "adding #{arg.inspect} as #{options[:as]}" if $DEBUG
29
+ descriptions[options[:as]] = {} unless descriptions.key?(options[:as])
30
+ descriptions[options[:as]].update({ arg => options[:with_value] ? options[:with_value] : nil})
31
+ puts descriptions.inspect if $DEBUG
32
+ end
33
+ end
34
+ #retrieves all the descriptions for the given argument
35
+ #returns an hash of all the available descriptions
36
+ #
37
+ #example
38
+ #
39
+ # Person.descriptions_for(:name)
40
+ # => { :required => nil, :limited => 30 }
41
+ def descriptions_for(args)
42
+ if args.respond_to? :map
43
+ args.map { |el| find_description_for el }
44
+ else
45
+ find_description_for args
46
+ end
47
+ end
48
+ #returns the elements that have been described with the given arguments
49
+ #if multiple arguments are specified all the elements that are described
50
+ #with all the arguments will be returned
51
+ #
52
+ #example
53
+ # Person.described_as(:required, :limited)
54
+ # => [ :name, :surname ]
55
+ def described_as(*args)
56
+ if args.size > 1
57
+ cumulated_keys = args.map { |arg| descriptions[arg].keys }
58
+ cumulated_keys.sort! { |x, y| x.size <=> y.size }
59
+ res = []
60
+ base = cumulated_keys.shift
61
+ base.each do |b|
62
+ res << b if cumulated_keys.inject(true) {|v, k| v && k.member?(b)}
63
+ end
64
+ res
65
+ else
66
+ descriptions[args.first].keys
67
+ end
68
+ end
69
+ #this method is used to test if an item has been described in a specifi way
70
+ #the methods accepts one or more arguments and returns a bool (true/false)
71
+ #
72
+ #examples
73
+ #
74
+ # Person.describes?(:heigth)
75
+ # => true
76
+ #
77
+ #so there are descriptions for :height in class Person
78
+ #
79
+ # Person.describes?(:heigth, :as => :public_data)
80
+ # => true
81
+ #
82
+ #so :heigth is described as :public data
83
+ #
84
+ # Person.describes?(:name, :as => :limited, :with_value => 30)
85
+ # => true
86
+ #
87
+ #so :name is described as :limited with value 30
88
+ #
89
+ def describes?(*args)
90
+ options = {}
91
+ options.update(args.pop) if args.last.is_a?(Hash)
92
+ if options.key?(:as)
93
+ cond = cond_key = lambda {|el| descriptions[options[:as]].key?(el)}
94
+ if options.key?(:with_value)
95
+ cond = lambda {|el| cond_key.call(el) && descriptions[options[:as]][el] == options[:with_value]}
96
+ end
97
+ args.each { |arg| return false unless cond.call(arg)}
98
+ else
99
+ args.each do |arg|
100
+ res = []
101
+ descriptions.each { |d| res << d[1].key?(arg) }
102
+ return false if res.uniq == [false]
103
+ end
104
+ end
105
+ true
106
+ end
107
+ #fetches the value for a given description
108
+ #
109
+ #example
110
+ # Person.described_value_for(:name, :as => :limited)
111
+ # => 30
112
+ #
113
+ #This method is aliased as description_of
114
+ #
115
+ #example
116
+ # Person.description_of(:name, :as => :limited)
117
+ # => 30
118
+ def described_value_for(*args)
119
+ raise ArgumentError unless args.last.is_a?(Hash) && args.last.key?(:as)
120
+ options = args.pop
121
+ if args.size > 1
122
+ args.map { |el| descriptions[options[:as]][el] }
123
+ else
124
+ descriptions[options[:as]][args.first]
125
+ end
126
+ end
127
+ alias :description_of :described_value_for
128
+ #creates some shortcut method to access a description
129
+ #
130
+ #example
131
+ # Person.shortcut_desctiption :limited, :as => :maxlength
132
+ #
133
+ #will add the methods
134
+ # Person.maxlength
135
+ # => all the :limited definitions
136
+ # Person.maxlength(element)
137
+ # => limited value for element
138
+ # Person.maxlength?(element)
139
+ # => true/false if element is described as :limited
140
+ # Person.maxlength_add(element, limit)
141
+ # => adds the new limit definition
142
+ def shortcut_description(*args)
143
+ raise ArgumentError unless args.last.is_a?(Hash) && args.last.key?(:as)
144
+ options = args.pop
145
+ @@shortcut = options[:as]
146
+ @@shortcut_description = args.first
147
+ class << self
148
+ define_method :cippa do
149
+ "cippa"
150
+ end
151
+ method_name = class_variable_get("@@shortcut")
152
+ description = class_variable_get("@@shortcut_description")
153
+ raise "#{method_name} already defined" if methods.member?(method_name)
154
+ define_method(method_name) do |*args|
155
+ if args.size == 0
156
+ descriptions[description].keys
157
+ else
158
+ descriptions[description][args.first]
159
+ end
160
+ end
161
+ define_method("#{method_name}?") do |arg|
162
+ descriptions[description].key?(arg)
163
+ end
164
+ define_method("#{method_name}_add") do |*args|
165
+ raise ArgumentError if args.size > 2
166
+ value = args.size == 2 ? args[1] : nil
167
+ descriptions[description][args.first]=value
168
+ end
169
+ end
170
+ end
171
+ private
172
+ def find_description_for(arg)
173
+ descriptions.inject({}) do |h, d|
174
+ puts h.inspect if $DEBUG
175
+ puts d.inspect if $DEBUG
176
+ (h[d[0]] = d[1][arg]) if d[1].key?(arg)
177
+ h
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,9 @@
1
+ module SimpleDescriptor #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1 @@
1
+ Dir[File.join(File.dirname(__FILE__), 'simple_descriptor/**/*.rb')].sort.each { |lib| require lib }
@@ -0,0 +1,67 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class DummySample
4
+ extend SimpleDescriptor::ClassDescriptor
5
+ attr_accessor :dummy_var
6
+ end
7
+ class SimpleDescriptorTest < Test::Unit::TestCase
8
+ #:shaped => { :apple => :spheric, :peach => :spheric, :dice => :cubic },
9
+ #'chunky' => { 'bacon' => nil } }
10
+ def setup
11
+ DummySample.describe :apple, :peach, :pear, :as => :fruit
12
+ DummySample.describe 'bacon', :as => 'chunky'
13
+ DummySample.describe :apple, :peach, :as => :shaped, :with_value => :spheric
14
+ DummySample.describe :pear, :as => :shaped, :with_value => :pear_shaped
15
+ DummySample.describe :peach, :as => :seasonal
16
+ DummySample.describe :dice, :as => :shaped, :with_value => :cubic
17
+ end
18
+ #checks the values are correctly stored in class::DESCRIPTIONS
19
+ def test_descriptions
20
+ assert_equal({ :apple => nil, :peach => nil, :pear => nil }, DummySample::DESCRIPTIONS[:fruit])
21
+ assert_equal({ :apple => :spheric, :peach => :spheric, :pear => :pear_shaped, :dice => :cubic }, DummySample::DESCRIPTIONS[:shaped])
22
+ assert_equal({ 'bacon' => nil }, DummySample::DESCRIPTIONS['chunky'])
23
+ end
24
+ #checks description retrieval
25
+ def test_descriptions_for
26
+ assert_equal({ :shaped => :spheric, :fruit => nil }, DummySample.descriptions_for(:apple))
27
+ end
28
+ #checks if the descriptions with value are correctly handled
29
+ def test_described_value_for
30
+ assert_equal :spheric, DummySample.described_value_for(:apple, :as => :shaped)
31
+ assert_equal [:spheric, :cubic, :spheric], DummySample.described_value_for(:apple, :dice, :peach, :as => :shaped)
32
+ assert_equal :spheric, DummySample.description_of(:apple, :as => :shaped)
33
+ assert_equal [:spheric, :cubic, :spheric], DummySample.description_of(:apple, :dice, :peach, :as => :shaped)
34
+ end
35
+ #checks the describes? method
36
+ def test_describes?
37
+ assert_equal false, DummySample.describes?(:urgh)
38
+ assert_equal true, DummySample.describes?(:apple)
39
+ assert_equal true, DummySample.describes?(:apple, :peach)
40
+ assert_equal false, DummySample.describes?(:apple, :peach, :strawberry)
41
+ assert_equal true, DummySample.describes?(:apple, :as => :fruit)
42
+ assert_equal true, DummySample.describes?(:apple, :peach, :as => :fruit)
43
+ assert_equal true, DummySample.describes?(:apple, :peach, :as => :shaped, :with_value => :spheric)
44
+ assert_equal true, DummySample.describes?(:dice, :as => :shaped, :with_value => :cubic)
45
+ assert_equal false, DummySample.describes?(:apple, :peach, :dice, :as => :shaped, :with_value => :spheric)
46
+ assert_equal false, DummySample.describes?(:apple, :as => :fish)
47
+ end
48
+ #checks the described_as method
49
+ def test_described_as
50
+ assert_equal [:apple, :peach, :pear], DummySample.described_as(:fruit)
51
+ assert_equal [:apple, :peach, :pear], DummySample.described_as(:fruit, :shaped)
52
+ assert_equal [:peach], DummySample.described_as(:fruit, :shaped, :seasonal)
53
+ end
54
+ #checks shortcut methods
55
+ def test_shortcut
56
+ DummySample.shortcut_description(:fruit, :as => :fruity)
57
+ DummySample.shortcut_description(:shaped, :as => :shape)
58
+ assert_equal true, DummySample.fruity?(:apple)
59
+ assert_equal :spheric, DummySample.shape(:apple)
60
+ assert_equal nil, DummySample.fruity(:apple)
61
+ assert_equal DummySample.described_as(:fruit), DummySample.fruity
62
+ DummySample.fruity_add(:banana)
63
+ assert_equal [:banana, :apple, :peach, :pear], DummySample.described_as(:fruit)
64
+ DummySample.shape_add(:banana, :banana_shaped)
65
+ assert_equal :banana_shaped, DummySample.description_of(:banana, :as => :shaped)
66
+ end
67
+ end
@@ -0,0 +1,2 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/simple_descriptor'
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.11
3
+ specification_version: 1
4
+ name: simple_descriptor
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.1.0
7
+ date: 2006-11-24 00:00:00 +00:00
8
+ summary: description of gem
9
+ require_paths:
10
+ - lib
11
+ email: your contact email for bug fixes and info
12
+ homepage: http://simple_descriptor.rubyforge.org
13
+ rubyforge_project: simple_descriptor
14
+ description: description of gem
15
+ autorequire: simple_descriptor
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ authors:
29
+ - blank
30
+ files:
31
+ - README
32
+ - CHANGELOG
33
+ - Rakefile
34
+ - test/test_helper.rb
35
+ - test/simple_descriptor_test.rb
36
+ - lib/simple_descriptor
37
+ - lib/simple_descriptor.rb
38
+ - lib/simple_descriptor/class_descriptor.rb
39
+ - lib/simple_descriptor/version.rb
40
+ test_files: []
41
+
42
+ rdoc_options:
43
+ - --quiet
44
+ - --title
45
+ - simple_descriptor documentation
46
+ - --opname
47
+ - index.html
48
+ - --line-numbers
49
+ - --main
50
+ - README
51
+ - --inline-source
52
+ - --exclude
53
+ - ^(examples|extras)/
54
+ extra_rdoc_files:
55
+ - README
56
+ - CHANGELOG
57
+ executables: []
58
+
59
+ extensions: []
60
+
61
+ requirements: []
62
+
63
+ dependencies: []
64
+