simple_descriptor 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/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
+