rubyless 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,4 +1,14 @@
1
- == 0.3.0 2009-10-03
1
+ == 0.3.1 2009-10-07
2
+
3
+ * 3 major enhancements
4
+ * method name in signatures should always be a string
5
+ * type[:method] is always set and is always a string
6
+ * fixed how class type is guessed from ActiveRecord column
7
+
8
+ * 1 minor enhancement
9
+ * added 'safe_read' method to objects
10
+
11
+ == 0.3.1 2009-10-03
2
12
 
3
13
  * 1 major enhancement
4
14
  * Moved from ParseTree to RubyParser
data/lib/processor.rb CHANGED
@@ -12,8 +12,8 @@ module RubyLess
12
12
  class RubyLessProcessor < SexpProcessor
13
13
  attr_reader :ruby
14
14
 
15
- INFIX_OPERATOR = [:"<=>", :==, :<, :>, :<=, :>=, :-, :+, :*, :/, :%]
16
- PREFIX_OPERATOR = [:"-@"]
15
+ INFIX_OPERATOR = ['<=>', '==', '<', '>', '<=', '>=', '-', '+', '*', '/', '%']
16
+ PREFIX_OPERATOR = ['-@']
17
17
 
18
18
  def self.translate(string, helper)
19
19
  sexp = RubyParser.new.parse(string)
@@ -94,7 +94,7 @@ module RubyLess
94
94
  unless opts = get_method([var_name], @helper, false)
95
95
  raise "Unknown variable or method '#{var_name}'."
96
96
  end
97
- method = opts[:method] || var_name.to_s
97
+ method = opts[:method]
98
98
  t method, opts
99
99
  end
100
100
 
@@ -147,7 +147,7 @@ module RubyLess
147
147
  end
148
148
 
149
149
  def method_call(receiver, exp)
150
- method = exp.shift
150
+ method = exp.shift.to_s
151
151
  arg_sexp = args = exp.shift # rescue nil
152
152
  if arg_sexp
153
153
  args = process(arg_sexp)
@@ -169,14 +169,14 @@ module RubyLess
169
169
  cond += receiver.cond
170
170
  end
171
171
  raise "'#{receiver}' does not respond to '#{method}(#{signature[1..-1].join(', ')})'." unless opts = get_method(signature, receiver.klass)
172
- method = opts[:method] if opts[:method]
173
- if method == :/
172
+ method = opts[:method]
173
+ if method == '/'
174
174
  t_if cond, "(#{receiver.raw}#{method}#{args.raw} rescue nil)", opts.merge(:nil => true)
175
175
  elsif INFIX_OPERATOR.include?(method)
176
176
  t_if cond, "(#{receiver.raw}#{method}#{args.raw})", opts
177
177
  elsif PREFIX_OPERATOR.include?(method)
178
178
  t_if cond, "#{method.to_s[0..0]}#{receiver.raw}", opts
179
- elsif method == :[]
179
+ elsif method == '[]'
180
180
  t_if cond, "#{receiver.raw}[#{args.raw}]", opts
181
181
  else
182
182
  args = "(#{args.raw})" if args != ''
@@ -184,7 +184,7 @@ module RubyLess
184
184
  end
185
185
  else
186
186
  raise "Unknown method '#{method}(#{args.raw})'." unless opts = get_method(signature, @helper, false)
187
- method = opts[:method] if opts[:method]
187
+ method = opts[:method]
188
188
  args = "(#{args.raw})" if args != ''
189
189
  t_if cond, "#{method}#{args}", opts
190
190
  end
data/lib/rubyless.rb CHANGED
@@ -6,7 +6,7 @@ require 'processor'
6
6
  =begin rdoc
7
7
  =end
8
8
  module RubyLess
9
- VERSION = '0.3.0'
9
+ VERSION = '0.3.1'
10
10
 
11
11
  def self.translate(string, helper)
12
12
  RubyLessProcessor.translate(string, helper)
data/lib/safe_class.rb CHANGED
@@ -2,12 +2,12 @@ module RubyLess
2
2
  module SafeClass
3
3
  @@_safe_methods ||= {} # defined for each class
4
4
  @@_safe_methods_all ||= {} # full list with inherited attributes
5
-
5
+
6
6
  # List of safe methods for a specific class.
7
7
  def self.safe_methods_for(klass)
8
8
  @@_safe_methods_all[klass] ||= build_safe_methods_list(klass)
9
9
  end
10
-
10
+
11
11
  # Return method type (options) if the given signature is a safe method for the class.
12
12
  def self.safe_method_type_for(klass, signature)
13
13
  if res = safe_methods_for(klass)[signature]
@@ -16,20 +16,22 @@ module RubyLess
16
16
  nil
17
17
  end
18
18
  end
19
-
19
+
20
20
  # Declare a safe method for a given class
21
21
  def self.safe_method_for(klass, hash)
22
22
  list = (@@_safe_methods[klass] ||= {})
23
23
  hash.each do |k,v|
24
24
  k = [k] unless k.kind_of?(Array)
25
+ k[0] = k[0].to_s
25
26
  v = {:class => v} unless v.kind_of?(Hash) || v.kind_of?(Proc)
27
+ v[:method] = v[:method] ? v[:method].to_s : k.first.to_s
26
28
  list[k] = v
27
29
  end
28
30
  end
29
-
31
+
30
32
  def self.included(base)
31
33
  base.class_eval do
32
-
34
+
33
35
  # Declare safe methods. By providing
34
36
  #
35
37
  # The methods hash has the following format:
@@ -55,21 +57,23 @@ module RubyLess
55
57
  def self.safe_method(hash)
56
58
  methods_hash = hash
57
59
  defaults = methods_hash.delete(:defaults) || {}
58
-
60
+
59
61
  list = (@@_safe_methods[self] ||= {})
60
62
  methods_hash.each do |k,v|
61
63
  k = [k] unless k.kind_of?(Array)
64
+ k[0] = k[0].to_s
62
65
  if v.kind_of?(Hash)
63
66
  v = defaults.merge(v)
67
+ v[:method] = v[:method] ? v[:method].to_s : k.first.to_s
64
68
  elsif v.kind_of?(Proc)
65
69
  # cannot merge defaults
66
70
  else
67
- v = defaults.merge(:class => v)
71
+ v = defaults.merge(:class => v, :method => k.first.to_s)
68
72
  end
69
73
  list[k] = v
70
74
  end
71
75
  end
72
-
76
+
73
77
  # Declare a safe method to access a list of attributes.
74
78
  # This method should only be used when the class is linked with a database table and provides
75
79
  # proper introspection to detect types and the possibility of NULL values.
@@ -82,10 +86,8 @@ module RubyLess
82
86
  opts[:class] = Number
83
87
  elsif col.text?
84
88
  opts[:class] = String
85
- elsif att.to_s =~ /_at$/
86
- opts[:class] = Time
87
89
  else
88
- raise "Could not declare safe_method for '#{att}': could not guess return type"
90
+ opts[:class] = col.klass
89
91
  end
90
92
  safe_method att.to_sym => opts
91
93
  else
@@ -93,34 +95,41 @@ module RubyLess
93
95
  end
94
96
  end
95
97
  end
96
-
98
+
97
99
  # Declare a safe method for a given class
98
100
  def self.safe_method_for(klass, signature)
99
101
  SafeClass.safe_method_for(klass, signature)
100
102
  end
101
-
103
+
102
104
  # Hash of all safe methods defined for the class.
103
105
  def self.safe_methods
104
106
  SafeClass.safe_methods_for(self)
105
107
  end
106
-
108
+
107
109
  # Return true if the given signature corresponds to a safe method for the class.
108
110
  def self.safe_method_type(signature)
109
111
  if res = SafeClass.safe_method_type_for(self, signature)
110
- res.dup
112
+ res.dup # TODO: replace by freeze
111
113
  else
112
114
  nil
113
115
  end
114
116
  end
115
-
117
+
116
118
  # Return the method type (options) if the given signature is a safe method for the class.
117
119
  def safe_method_type(signature)
118
120
  self.class.safe_method_type(signature)
119
121
  end
120
122
  end # base.class_eval
121
123
  end # included
122
-
123
- private
124
+
125
+ # Safe attribute reader used when 'safe_readable?' could not be called because the class
126
+ # is not known during compile time.
127
+ def safe_read(key)
128
+ return "'#{key}' not readable" unless type = self.class.safe_method_type([key])
129
+ self.send(type[:method])
130
+ end
131
+
132
+ private
124
133
  def self.build_safe_methods_list(klass)
125
134
  list = klass.superclass.respond_to?(:safe_methods) ? klass.superclass.safe_methods : {}
126
135
  (@@_safe_methods[klass] || {}).map do |signature, return_value|
@@ -129,12 +138,13 @@ module RubyLess
129
138
  elsif !return_value.kind_of?(Proc)
130
139
  return_value = {:class => return_value}
131
140
  end
132
- signature.map! {|e| parse_class(e)}
141
+ method = signature.shift
142
+ signature = [method] + signature.map {|e| parse_class(e)}
133
143
  list[signature] = return_value
134
144
  end
135
145
  list
136
146
  end
137
-
147
+
138
148
  def self.parse_class(klass)
139
149
  if klass.kind_of?(Array)
140
150
  if klass[0].kind_of?(String)
data/rubyless.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{rubyless}
5
- s.version = "0.3.0"
5
+ s.version = "0.3.1"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Gaspard Bucher"]
@@ -3,7 +3,7 @@ require File.dirname(__FILE__) + '/test_helper.rb'
3
3
 
4
4
  class StringDictionary
5
5
  include RubyLess::SafeClass
6
- safe_method [:[], Symbol] => {:class => String, :nil => true}
6
+ safe_method ['[]', Symbol] => {:class => String, :nil => true}
7
7
  end
8
8
 
9
9
  class SimpleHelper < Test::Unit::TestCase
@@ -15,7 +15,7 @@ class SimpleHelper < Test::Unit::TestCase
15
15
  safe_method :node => lambda {|h| {:class => h.context[:node_class], :method => h.context[:node]}}
16
16
  safe_method :now => {:class => Time, :method => "Time.now"}
17
17
  safe_method :birth => {:class => Time, :method => "Date.parse('2009-06-02 18:44')"}
18
- safe_method :dictionary => {:class => StringDictionary, :method => 'get_dict'}
18
+ safe_method 'dictionary' => {:class => StringDictionary, :method => 'get_dict'}
19
19
  safe_method [:vowel_count, String] => Number
20
20
  safe_method [:log_info, Dummy, String] => String
21
21
  safe_method_for String, [:==, String] => Boolean
@@ -46,6 +46,11 @@ class SimpleHelper < Test::Unit::TestCase
46
46
  "[#{obj.name}] #{msg}"
47
47
  end
48
48
 
49
+ def test_safe_read
50
+ assert_equal 10, Dummy.new.safe_read('id')
51
+ assert_equal "'rm' not readable", Dummy.new.safe_read('rm')
52
+ end
53
+
49
54
  def yt_do_test(file, test, context = yt_get('context',file,test))
50
55
  @@test_strings[file][test].keys.each do |key|
51
56
  next if ['src', 'context'].include?(key)
@@ -60,6 +65,7 @@ class SimpleHelper < Test::Unit::TestCase
60
65
  when 'tem'
61
66
  source ? RubyLess.translate(source, self) : yt_get('tem', file, test)
62
67
  when 'res'
68
+ res = RubyLess.translate(source, self)
63
69
  eval(source ? RubyLess.translate(source, self) : yt_get('tem', file, test)).to_s
64
70
  when 'sxp'
65
71
  RubyParser.new.parse(source).inspect
@@ -8,26 +8,49 @@ module RubyLess
8
8
  @opts[:default]
9
9
  end
10
10
 
11
+ def type
12
+ @opts[:type]
13
+ end
14
+
15
+ # Returns +true+ if the column is either of type string or text.
11
16
  def text?
12
- @opts[:text]
17
+ type == :string || type == :text
13
18
  end
14
19
 
20
+ # Returns +true+ if the column is either of type integer, float or decimal.
15
21
  def number?
16
- @opts[:number]
22
+ type == :integer || type == :float || type == :decimal
17
23
  end
24
+
25
+ # Returns the Ruby class that corresponds to the abstract data type.
26
+ def klass
27
+ case type
28
+ when :integer then Fixnum
29
+ when :float then Float
30
+ when :decimal then BigDecimal
31
+ when :datetime then Time
32
+ when :date then Date
33
+ when :timestamp then Time
34
+ when :time then Time
35
+ when :text, :string then String
36
+ when :binary then String
37
+ when :boolean then Object
38
+ end
39
+ end
40
+
18
41
  end
19
42
 
20
43
  class ActiveRecordMock
21
44
  COLUMNS = {
22
- 'format' => ColumnMock.new(:default => '%d.%m.%Y', :text => true),
23
- 'age' => ColumnMock.new(:default => 5, :number => true),
24
- 'friend_id' => ColumnMock.new(:number => true),
25
- 'log_at' => ColumnMock.new,
45
+ 'format' => ColumnMock.new(:default => '%d.%m.%Y', :type => :text),
46
+ 'age' => ColumnMock.new(:default => 5, :type => :float),
47
+ 'friend_id' => ColumnMock.new(:type => :integer),
48
+ 'log_at' => ColumnMock.new(:type => :datetime),
26
49
  }
27
50
  def self.columns_hash
28
51
  COLUMNS
29
52
  end
30
-
53
+
31
54
  COLUMNS.each do |k, v|
32
55
  define_method(k) do
33
56
  v.default
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubyless
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gaspard Bucher
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-03 00:00:00 +02:00
12
+ date: 2009-10-15 00:00:00 +02:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency