representations 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  Rails helpers, including form builders are great to allow rapid development of applications and views.
4
4
 
5
- They are procedural in nature and have hard time to adapt to complex models. They also live in a single namespace making it difficult to find which helpers apply to which models.
5
+ On the other hand they are procedural in nature and have hard time to adapt to complex models. They also live in a single namespace making it difficult to find which helpers apply to which models.
6
6
 
7
- Representations change syntax to object oriented and model specific.
7
+ Representations do a paradigm switch and change syntax to object oriented and model specific.
8
8
 
9
9
  ## Example usage
10
10
 
@@ -25,7 +25,7 @@ Rails helpers:
25
25
  = f.text_field(:last_name)
26
26
  = f.radio_button(:eye_color, 'blue')
27
27
  = f.label(:eye_color_blue, "Blue")
28
- = f.submit("Submit")
28
+ = f.submit("ok")
29
29
 
30
30
  Representations:
31
31
 
@@ -42,24 +42,42 @@ Representations:
42
42
  = p.last_name.text_field
43
43
  = p.eye_color.radio_button('blue')
44
44
  = p.eye_color.radio_button_label('blue', 'Blue')
45
+
45
46
  ##Extensions
47
+
46
48
  Representations can be altered. For example to add new method DefaultRepresentation create file app/representations/default_representation.rb with the content:
47
49
  module DefaultRepresentation
48
50
  def new_method
49
51
  some code
50
52
  end
51
53
  end
54
+
52
55
  ##Nested attributes
56
+
53
57
  - user.children.each do |child|
54
58
  = child.name.label
55
59
  = child.name.text_field
56
60
  = child.delete_checkbox
57
61
  = child.delete_checkbox_label
62
+
58
63
  Or even:
64
+
59
65
  - user.children.build do |child|
60
66
  = child.name.label
61
67
  = child.name.text_field
68
+
62
69
  ## Automatic wrapping
70
+
63
71
  Create file config/initializers/representations.rb
72
+
64
73
  Representations.enable_automatic_wrapping = true
74
+
65
75
  if you want Representations to automatically wrap variables from the controller
76
+
77
+ ##Notes
78
+
79
+ Use id method for named routes and helpers generated by `map :resource` for example
80
+
81
+ <%@users.each do |u|%>
82
+ <%= link_to "edit user", edit_user_path(u.id) %>
83
+ <%end%>
data/Rakefile CHANGED
@@ -19,8 +19,7 @@ Resource representations change syntax to object oriented and model specific."
19
19
  "Rakefile",
20
20
  "VERSION",
21
21
  "init.rb",
22
- "lib/representations.rb",
23
- "lib/view_helpers.rb",
22
+ "lib/*",
24
23
  "rails/init.rb",
25
24
  "representations.gemspec"
26
25
  ]
@@ -34,12 +33,12 @@ end
34
33
  require 'spec/rake/spectask'
35
34
  Spec::Rake::SpecTask.new(:spec) do |spec|
36
35
  spec.libs << 'lib' << 'spec'
37
- spec.spec_files = FileList['spec/test_application/spec/helpers/*_spec.rb']
36
+ spec.spec_files = FileList['spec/test_application/spec/representations/*_spec.rb']
38
37
  end
39
38
 
40
39
  Spec::Rake::SpecTask.new(:rcov) do |spec|
41
40
  spec.libs << 'lib' << 'spec'
42
- spec.pattern = 'spec/test_application/spec/helpers/*_spec.rb'
41
+ spec.pattern = 'spec/test_application/spec/representations/*_spec.rb'
43
42
  spec.rcov = true
44
43
  end
45
44
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.3
1
+ 0.0.4
data/init.rb CHANGED
@@ -1,5 +1,3 @@
1
1
  require 'view_helpers'
2
2
  require 'representations'
3
3
  ActionView::Base.send :include, Representations::ViewHelpers
4
- ActiveSupport::Dependencies.load_paths << "#{RAILS_ROOT}/app/representations/"
5
-
@@ -0,0 +1,60 @@
1
+ module Representations
2
+ #Representation for ActiveRecord::Base objects
3
+ class ActiveRecordRepresentation < Representation
4
+ #Render partial with the given name and given namespace as a parameter
5
+ def partial(partial_name, namespace = nil)
6
+ namespace = get_namespace unless namespace
7
+ namespace += '/'
8
+ path = @name.pluralize
9
+ path = namespace + path
10
+ path.downcase!
11
+ @template.render(:partial => "#{path}/#{partial_name}")
12
+ end
13
+ #Render partial if it has 'has_one' association with the other model, otherwise do normal to_s
14
+ def to_s
15
+ @parent && @parent.instance_variable_get(:@value).class.reflections[:"#{@name}"].macro == :has_one ? partial(@name) : super
16
+ end
17
+ #Form tag, namespace depends on the namespace of the controller.
18
+ def form(&block)
19
+ raise "You need to provide block to form representation" unless block_given?
20
+ namespace = get_namespace
21
+ namespace = '/' + namespace unless namespace.blank?
22
+ path = namespace + '/' + @name.pluralize
23
+ path.downcase!
24
+ content = @template.capture(self, &block)
25
+ if @value.new_record?
26
+ @template.concat(@template.form_tag(path, :method => "post"))
27
+ else
28
+ path += '/' + "#{@value.id}"
29
+ @template.concat(@template.form_tag(path, :method => "put"))
30
+ end
31
+ @template.concat(content)
32
+ @template.concat(@template.submit_tag("ok"))
33
+ @template.concat("</form>")
34
+ self
35
+ end
36
+ #Forwards ActiveRecord invocation and wraps result in appropriate Representation
37
+ #Suppose that User extends ActiveRecord::Base:
38
+ #ar_user = User.new
39
+ #ar_user.nick = 'foo'
40
+ #user = r(ar_user) #user is now ActiveRecordRepresentation
41
+ #user.nick.text_field #method_missing will be called on user with method_name = 'nick' in which new method for user will be created and will be called. The newly created method will create a new DefaultRepresentation with @value set to the string 'foo'. Next the text_field will be called on the newly created DefaultRepresentation
42
+ def method_missing(method_name, *args, &block)
43
+ method = <<-EOF
44
+ def #{method_name}(*args, &block)
45
+ @__#{method_name} ||= Representations.representation_for(@value.#{method_name}, @template, "#{method_name}", self)
46
+ @__#{method_name}.with_block(&block)
47
+ @__#{method_name} if block.nil?
48
+ end
49
+ EOF
50
+ ::Representations::ActiveRecordRepresentation.class_eval(method, __FILE__, __LINE__)
51
+ self.__send__(method_name, &block)
52
+ end
53
+ private
54
+ #Gets path to namespace (i.e. if controller is Good::Bad::Ugly::UsersController the R is in the namespace good/bad/ugly)
55
+ def get_namespace
56
+ namespace = @template.controller.class.parent_name.split('::') rescue []
57
+ namespace = namespace.join('/')
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,60 @@
1
+ module Representations
2
+ #Representation for Collections
3
+ class AssociationsRepresentation < Representation
4
+ #initilize @num variable
5
+ def initialize(object, template, name, parent)
6
+ super
7
+ @num = -1 #-1 so first call to num will result in 0
8
+ end
9
+ #Creates Representation for every object in the Array and invokes passed block with this Representation as the argument
10
+ def each
11
+ @value.each_index do |idx|
12
+ representation_object = Representations.representation_for(@value[idx], @template, idx.to_s, self)
13
+ #add to page hidden input with id of the object in the collection
14
+ tree = representation_object.get_parents_tree
15
+ name = get_html_name_attribute_value(tree)
16
+ name << '[id]'
17
+ tags = get_tags({}, {:value => @value[idx].id.to_s, :name=>name})
18
+ @template.concat("<input type='hidden' #{tags}/>")
19
+ yield representation_object
20
+ end
21
+ end
22
+ #Creates new object in the collection and input fields for it defined in the passed block
23
+ def build
24
+ new_object = @value.build
25
+ representation_object = AssociationsRepresentation::NewRecordRepresentation.new(new_object, @template, 'new_' + num.to_s, self)
26
+ yield representation_object
27
+ end
28
+ private
29
+ attr_reader :num
30
+ #Used for generating unique @name for ArrayRepresentation::NewRecordRepresentation
31
+ def num
32
+ @num += 1
33
+ end
34
+ #Representation that wraps newly created ActiveRecord::Base that will be added to some collection
35
+ class NewRecordRepresentation < Representation
36
+ #Creates new method which wraps call for ActionRecord
37
+ #New method returns Representation which represents datatype in the appropriate column
38
+ def method_missing(method_name_symbol, *args, &block)
39
+ method_name = method_name_symbol.to_s
40
+ representation_class = case @value.class.columns_hash[method_name].type
41
+ when :date
42
+ Representations::DateRepresentation
43
+ when :datetime
44
+ Representations::DateRepresentation
45
+ else
46
+ Representations::DefaultRepresentation
47
+ end
48
+ method = <<-EOF
49
+ def #{method_name}(*args, &block)
50
+ @__#{method_name} ||= #{representation_class}.new(@value.#{method_name}, @template, "#{method_name}", self)
51
+ @__#{method_name}.with_block(&block)
52
+ @__#{method_name} if block.nil?
53
+ end
54
+ EOF
55
+ ::Representations::AssociationsRepresentation::NewRecordRepresentation.class_eval(method, __FILE__, __LINE__)
56
+ self.__send__(method_name_symbol, &block)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,10 @@
1
+ module Representations
2
+ #unused
3
+ module ControllerHelpers
4
+ def uses_representations(hash)
5
+ raise 'Ambiguous options' if hash.keys.size > 1
6
+ raise 'Automatic wrapping is disabled' unless Representations.automatic_wrapping
7
+ @use_r_for = hash[:only] rescue actions.collect{|a| a if hash[:except].include?(a).compact}
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,19 @@
1
+ module Representations
2
+ #Representation for Date object
3
+ class DateRepresentation < Representation
4
+ def select(passed_options = {}, html_options = {})
5
+ tree = get_parents_tree
6
+ names = get_html_name_attribute_value(tree)
7
+ names.pop
8
+ options = { :object => @parent.instance_variable_get(:@value) }
9
+ options.merge!(passed_options)
10
+ @template.date_select(names, @name, options, html_options)
11
+ end
12
+ end
13
+ #Something like aliases
14
+ class TimeWithZoneRepresentation < DateRepresentation
15
+ end
16
+ class DateTimeRepresentation < DateRepresentation
17
+ end
18
+ end
19
+
@@ -0,0 +1,71 @@
1
+ module Representations
2
+ class DefaultRepresentation < Representation
3
+ #not tested in the view
4
+ #Returns string with html check box tag
5
+ def check_box(checked_value = "1", unchecked_value = "0", html_options = {})
6
+ tree = get_parents_tree
7
+ id_attr_value = tree.collect{ |x| x[0] }.join('_')
8
+ name_attr_value = get_html_name_attribute_value(tree)
9
+ tags = get_tags(html_options, {:value => checked_value, :id => id_attr_value, :name=>name_attr_value})
10
+ %Q{<input type="checkbox" #{tags}/>\n<input type="hidden" value="#{unchecked_value}" id="#{id_attr_value}" name="#{name_attr_value}"/>}
11
+ end
12
+ #not tested in the view
13
+ #Returns string with html file field tag
14
+ def file_field(html_options = {})
15
+ tree = get_parents_tree
16
+ id_attr_value = tree.collect{ |x| x[0] }.join('_')
17
+ tags = get_tags(html_options, {:value => to_s, :id => id_attr_value, :name=>get_html_name_attribute_value(tree)})
18
+ %Q{<input type="file" #{tags}/>}
19
+ end
20
+ #not tested in the view
21
+ #Returns string with html hidden input tag
22
+ def hidden_field(html_options = {})
23
+ tree = get_parents_tree
24
+ id_attr_value = tree.collect{ |x| x[0] }.join('_')
25
+ tags = get_tags(html_options, {:value => to_s, :id => id_attr_value, :name=>get_html_name_attribute_value(tree)})
26
+ %Q{<input type="hidden" #{tags}/>}
27
+ end
28
+ #Returns string with html text input tag
29
+ def text_field(html_options = {})
30
+ tree = get_parents_tree
31
+ id_attr_value = tree.collect{ |x| x[0] }.join('_')
32
+ tags = get_tags(html_options, {:value => to_s, :id => id_attr_value, :name=>get_html_name_attribute_value(tree)})
33
+ %Q{<input type="text" #{tags}/>}
34
+ end
35
+ #Returns string with html text area tag
36
+ def text_area(html_options = {})
37
+ tree = get_parents_tree
38
+ id_attr_value = tree.collect{ |x| x[0] }.join('_')
39
+ tags = get_tags(html_options, {:id => id_attr_value, :name => get_html_name_attribute_value(tree)})
40
+ %Q{<textarea #{tags}>\n#{to_s}\n</textarea>}
41
+ end
42
+ #Returns string with html password tag
43
+ def password_field(html_options = {})
44
+ tree = get_parents_tree
45
+ id_attr_value = tree.collect{ |x| x[0] }.join('_')
46
+ tags = get_tags(html_options, {:value => to_s, :id => id_attr_value, :name=>get_html_name_attribute_value(tree)})
47
+ %Q{<input type="password" #{tags}/>}
48
+ end
49
+ #Returns string with html radio button tag
50
+ def radio_button(value, html_options = {})
51
+ tree = get_parents_tree
52
+ id_attr_value = tree.collect{ |x| x[0] }.join('_') + "_#{value}"
53
+ name_attr_value = get_html_name_attribute_value(tree)
54
+ if @value && @value.capitalize==value.capitalize #if editing existing record and values do match
55
+ options = {:name => name_attr_value, :value=>value, :id=>id_attr_value, :checked=>"true"}
56
+ else
57
+ options = {:name => name_attr_value, :value=>value, :id=>id_attr_value, :checked=>"false"}
58
+ end
59
+ tags = get_tags(html_options, options)
60
+ %Q{<input type="radio" #{tags}/>}
61
+ end
62
+ #Returns string with html label tag with 'for' attribute set to the radio button of this object
63
+ def radio_button_label(radio_button_value, value = nil, html_options = {})
64
+ tree = get_parents_tree
65
+ for_attr_value = tree.collect{ |x| x[0] }.join('_') + "_#{radio_button_value}"
66
+ value = radio_button_value.capitalize if value.nil?
67
+ tags = get_tags(html_options, {:for => for_attr_value})
68
+ %Q{<label #{tags}>#{ERB::Util::h(value)}</label>}
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,96 @@
1
+ module Representations
2
+ class Representation
3
+ #value - object for which the representation is created
4
+ #template - template view (needed because some ActionView::Base methods are private)
5
+ #name - the actuall name of the method that was called on the object's parent that is being initialize
6
+ #parent - Representation object which contains the object that is being initialize
7
+ def initialize(value, template, name, parent=nil)
8
+ @value = value
9
+ @name = name
10
+ @template = template
11
+ @parent = parent
12
+
13
+ #extend class if user provided appropriate file (look at the files app/representations/*_representation.rb)
14
+ #first check if file exists in app/representations
15
+ file_name = "#{RAILS_ROOT}/app/representations/#{send(:class).to_s.demodulize.tableize.singularize}.rb"
16
+ if File.exist?(file_name)
17
+ ActiveSupport::Dependencies.require_or_load(file_name)
18
+ Rails.logger.info "Extending Representation ::#{self.class.to_s.demodulize}"
19
+ self.class.send(:include, "::#{self.class.to_s.demodulize}".constantize)
20
+ end
21
+ #extend this object's class if user provided per-model extensions (i.e. for Job model look at app/representations/job_representation.rb)
22
+ file_name = "#{RAILS_ROOT}/app/representations/#{value.class.to_s.demodulize.tableize.singularize}_representation.rb"
23
+ if File.exist?(file_name)
24
+ ActiveSupport::Dependencies.require_or_load(file_name)
25
+ Rails.logger.info "Extending Representation ::#{self.class.to_s.demodulize} for model #{value.class.to_s}"
26
+ send(:extend, "::#{value.class.to_s}Representation".constantize)
27
+ end
28
+ end
29
+ def +(arg)
30
+ to_s + arg.to_s
31
+ end
32
+ def id
33
+ @value.id if @value
34
+ end
35
+ #returns escaped string from the object's to_s method
36
+ def to_s
37
+ @value ? ERB::Util::h(@value.to_s) : ''
38
+ end
39
+ #returns html label tag for the representation
40
+ def label(value = nil, html_options = {})
41
+ tree = get_parents_tree
42
+ for_attr_value = tree.collect{ |x| x[0] }.join('_')
43
+ tags = get_tags(html_options, {:for => for_attr_value})
44
+ value = ERB::Util::h(@name.humanize) if value.nil?
45
+ %Q{<label #{tags}>#{value}</label>}
46
+ end
47
+ protected
48
+ #Call the passed block (if any)
49
+ def with_block(&block)
50
+ yield self if block_given? && @value
51
+ end
52
+ #Returns two dimensional array based on the tree of the Represantation objects which are linked together by the @parent field
53
+ #First element of the array consists of Representation's @name and the second of Representation's class
54
+ def get_parents_tree
55
+ tree = Array.new
56
+ tree[0] = []
57
+ tree[0][0] = @name
58
+ tree[0][1] = self.class
59
+ parent = @parent
60
+ while parent do #iterate parent tree
61
+ array = []
62
+ array[0] = parent.instance_variable_get(:@name)
63
+ array[1] = parent.class
64
+ tree.unshift(array)
65
+ parent = parent.instance_variable_get(:@parent)
66
+ end
67
+ tree #tree now looks something like this [['user', ActiverRecordRepresentation], ['nick', DefaultRepresentation]]
68
+ end
69
+ #Creates value of the html name attribute according to the passed tree
70
+ def get_html_name_attribute_value(tree)
71
+ first = tree.delete_at(0)
72
+ root_name = first[0]
73
+ name = []
74
+ prev = nil
75
+ tree.each do |elem|
76
+ if elem[1] == DefaultRepresentation || elem[1] == TimeWithZoneRepresentation || prev == AssociationsRepresentation
77
+ name.push "[" + elem[0] + "]"
78
+ else
79
+ name.push "[" + elem[0] + "_attributes]"
80
+ end
81
+ prev = elem[1]
82
+ end
83
+ name.unshift(root_name)
84
+ end
85
+ #Returns string created by merging two hashes of html options passed as the arguments
86
+ def get_tags(user_options, base_options)
87
+ options = base_options.merge(user_options)
88
+ options.stringify_keys!
89
+ options = options.sort
90
+ options.map{ |key, value| %(#{key}="#{value}" ) }
91
+ end
92
+ def method_missing(method_name)
93
+ @value ? super : self
94
+ end
95
+ end
96
+ end
@@ -1,25 +1,38 @@
1
+ require 'representation.rb'
2
+ require 'default_representation.rb'
3
+ require 'associations_representation.rb'
4
+ require 'active_record_representation.rb'
5
+ require 'date_representation.rb'
1
6
  module Representations
7
+
8
+ #Currently this method is never called but maybe someday it will have to be :-)
9
+ #Changes ActionController::PolymorphicRoutes#polymorphic_path so it can handle R
10
+ def self.eval_polymorphic_routes
11
+ ActionController::PolymorphicRoutes.class_eval do
12
+ def polymorphic_path_with_r(object, options = {})
13
+ object.is_a?(Representation) ? polymorphic_path_without_r(object.instance_variable_get(:@value), options) : polymorphic_path_without_r(object, options)
14
+ end
15
+ alias_method_chain :polymorphic_path, :r
16
+ end
17
+ end
2
18
  #Enables automatic wrapping
3
- #TODO should be disabled until module unloading will be fixed
19
+ #Currently there's no way of disabling it
4
20
  def self.enable_automatic_wrapping=(value)
5
21
  if value
6
- ActionView::Base.class_eval do
7
- def instance_variable_set_with_r(symbol, obj)
8
- load ActiveSupport::Dependencies.search_for_file('representations.rb')
9
- obj = Representations.representation_for(obj, self, symbol.to_s[1..-1]) if obj.is_a?(ActiveRecord::Base)
10
- instance_variable_set_without_r(symbol, obj) #call to the original method
11
- end
12
- self.alias_method_chain :instance_variable_set, :r
13
- end
22
+ ActionView::Base.class_eval{ self.alias_method_chain :instance_variable_set, :r }
14
23
  end
15
24
  end
16
25
  #Creates Representation for object passed as a paremeter, type of the representation
17
26
  #depends on the type of the object
18
- def representation_for(object, template, name=nil, parent=nil)
27
+ def representation_for(object, template, name, parent=nil)
19
28
  representation_class =
20
29
  begin
21
30
  if object.is_a?(ActiveRecord::Base)
22
31
  ActiveRecordRepresentation
32
+ elsif parent && parent.instance_variable_get(:@value).class.reflections[name.to_sym] && parent.instance_variable_get(:@value).class.reflections[name.to_sym].macro == :has_one
33
+ parent.instance_variable_get(:@value).send(name+'=', parent.instance_variable_get(:@value).class.reflections[name.to_sym].klass.new) if parent.instance_variable_get(:@value).send(name).nil? #create new AR object
34
+ object = parent.instance_variable_get(:@value).send(name)
35
+ Representations::ActiveRecordRepresentation
23
36
  else
24
37
  "Representations::#{object.class.to_s.demodulize}Representation".constantize
25
38
  end
@@ -30,251 +43,5 @@ module Representations
30
43
  end
31
44
 
32
45
  module_function :representation_for
33
- class Representation
34
-
35
- #value - object for which the representation is created
36
- #template - template view (needed because some ActionView::Base methods are private)
37
- #name - the actuall name of the method that was called on the object's parent that is being initialize
38
- #parent - Representation object which contains the object that is being initialize
39
- def initialize(value, template, name=nil, parent=nil)
40
- @value = value
41
- @name = name
42
- @template = template
43
- @parent = parent
44
- #extend class if user provided appropriate file (look at the files app/representations/*_representation.rb)
45
- self.send(:extend, "::#{self.class.to_s.demodulize}".constantize) rescue Rails.logger.info "No extension defined for ::#{self.class.to_s}"
46
- end
47
-
48
- def id
49
- @value
50
- end
51
- #returns escaped string from the object's to_s method
52
- def to_s
53
- ERB::Util::h(@value.to_s)
54
- end
55
- #returns html label tag for the representation
56
- def label(value = nil, html_options = {})
57
- tree = get_parents_tree
58
- for_attr_value = tree.collect{ |x| x[0] }.join('_')
59
- tags = get_tags(html_options, {:for => for_attr_value})
60
- value = ERB::Util::h(@name.humanize) if value.nil?
61
- %Q{<label #{tags}>#{value}</label>}
62
- end
63
46
 
64
- protected
65
- #Call the passed block (if any)
66
- def with_block(&block)
67
- yield self if block_given?
68
- end
69
- #Returns two dimensional array based on the tree of the Represantation objects which are linked together by the @parent field
70
- #First element of the array consists of Representation's @name and the second of Representation's class
71
- def get_parents_tree
72
- tree = Array.new
73
- tree[0] = []
74
- tree[0][0] = @name
75
- tree[0][1] = self.class
76
- parent = @parent
77
- while parent do #iterate parent tree
78
- array = []
79
- array[0] = parent.instance_variable_get(:@name)
80
- array[1] = parent.class
81
- tree.unshift(array)
82
- parent = parent.instance_variable_get(:@parent)
83
- end
84
- tree #tree now looks something like this [['user', ActiverRecordRepresentation], ['nick', DefaultRepresentation]]
85
- end
86
- #Creates value of the html name attribute according to the passed tree
87
- def get_html_name_attribute_value(tree)
88
- first = tree.delete_at(0)
89
- root_name = first[0]
90
- name = []
91
- prev = nil
92
- tree.each do |elem|
93
- if elem[1] == DefaultRepresentation || elem[1] == TimeWithZoneRepresentation || prev == AssociationsRepresentation
94
- name.push "[" + elem[0] + "]"
95
- else
96
- name.push "[" + elem[0] + "_attributes]"
97
- end
98
- prev = elem[1]
99
- end
100
- name.unshift(root_name)
101
- end
102
- #Returns string created by merging two hashes of html options passed as the arguments
103
- def get_tags(user_options, base_options)
104
- options = base_options.merge(user_options)
105
- options.stringify_keys!
106
- options = options.sort
107
- options.map{ |key, value| %(#{key}="#{value}" ) }
108
- end
109
- end
110
-
111
- class DefaultRepresentation < Representation
112
- #not tested in the view
113
- #Returns string with html check box tag
114
- def check_box(checked_value = "1", unchecked_value = "0", html_options = {})
115
- tree = get_parents_tree
116
- id_attr_value = tree.collect{ |x| x[0] }.join('_')
117
- name_attr_value = get_html_name_attribute_value(tree)
118
- tags = get_tags(html_options, {:value => checked_value, :id => id_attr_value, :name=>name_attr_value})
119
- %Q{<input type="checkbox" #{tags}/>\n<input type="hidden" value="#{unchecked_value}" id="#{id_attr_value}" name="#{name_attr_value}"/>}
120
- end
121
- #not tested in the view
122
- #Returns string with html file field tag
123
- def file_field(html_options = {})
124
- tree = get_parents_tree
125
- id_attr_value = tree.collect{ |x| x[0] }.join('_')
126
- tags = get_tags(html_options, {:value => to_s, :id => id_attr_value, :name=>get_html_name_attribute_value(tree)})
127
- %Q{<input type="file" #{tags}/>}
128
- end
129
- #not tested in the view
130
- #Returns string with html hidden input tag
131
- def hidden_field(html_options = {})
132
- tree = get_parents_tree
133
- id_attr_value = tree.collect{ |x| x[0] }.join('_')
134
- tags = get_tags(html_options, {:value => to_s, :id => id_attr_value, :name=>get_html_name_attribute_value(tree)})
135
- %Q{<input type="hidden" #{tags}/>}
136
- end
137
- #Returns string with html text input tag
138
- def text_field(html_options = {})
139
- tree = get_parents_tree
140
- id_attr_value = tree.collect{ |x| x[0] }.join('_')
141
- tags = get_tags(html_options, {:value => to_s, :id => id_attr_value, :name=>get_html_name_attribute_value(tree)})
142
- %Q{<input type="text" #{tags}/>}
143
- end
144
- #Returns string with html text area tag
145
- def text_area(html_options = {})
146
- tree = get_parents_tree
147
- id_attr_value = tree.collect{ |x| x[0] }.join('_')
148
- tags = get_tags(html_options, {:id => id_attr_value, :name => get_html_name_attribute_value(tree)})
149
- %Q{<textarea #{tags}>\n#{to_s}\n</textarea>}
150
- end
151
- #Returns string with html password tag
152
- def password_field(html_options = {})
153
- tree = get_parents_tree
154
- id_attr_value = tree.collect{ |x| x[0] }.join('_')
155
- tags = get_tags(html_options, {:value => to_s, :id => id_attr_value, :name=>get_html_name_attribute_value(tree)})
156
- %Q{<input type="password" #{tags}/>}
157
- end
158
- #Returns string with html radio button tag
159
- def radio_button(value, html_options = {})
160
- tree = get_parents_tree
161
- id_attr_value = tree.collect{ |x| x[0] }.join('_') + "_#{value}"
162
- name_attr_value = get_html_name_attribute_value(tree)
163
- tags = get_tags(html_options, {:name => name_attr_value, :value=>value, :id=>id_attr_value, :checked=>"#{@value.capitalize==value.capitalize}"})
164
- %Q{<input type="radio" #{tags}/>}
165
- end
166
- #Returns string with html label tag with for attribute set to the radio button of this object
167
- def radio_button_label(radio_button_value, value = nil, html_options = {})
168
- tree = get_parents_tree
169
- for_attr_value = tree.collect{ |x| x[0] }.join('_') + "_#{radio_button_value}"
170
- value = radio_button_value.capitalize if value.nil?
171
- tags = get_tags(html_options, {:for => for_attr_value})
172
- %Q{<label #{tags}>#{ERB::Util::h(value)}</label>}
173
- end
174
- end
175
- #Representation for objects which are nil
176
- class NilClassRepresentation < Representation
177
- #Returns self so the calls:
178
- #nil_object.not_defined_method.another_not_defined_method
179
- #want raise an error
180
- def method_missing(method_name, *args)
181
- return self
182
- end
183
- #Passed block shouldn't be called
184
- def with_block(&block)
185
- end
186
- #Returns blank string
187
- def to_s
188
- return ''
189
- end
190
- end
191
- #Representation for ActiveRecord::Base object's
192
- class ActiveRecordRepresentation < Representation
193
- #Form builder
194
- def form(&block)
195
- raise "You need to provide block to form representation" unless block_given?
196
- content = @template.capture(self, &block)
197
- @template.concat(@template.form_tag(@value))
198
- @template.concat(content)
199
- @template.concat("</form>")
200
- self
201
- end
202
- #Forwards ActiveRecord invocation and wraps result in appropriate Representation
203
- #Suppose that User extends ActiveRecord::Base :
204
- #ar_user = User.new
205
- #ar_user.nick = 'foo'
206
- #user = r(ar_user) #user is now ActiveRecordRepresentation
207
- #user.nick.text_field #method_missing will be called on user with method_name = 'nick' in which new method for user will be created and will be called. The newly created method will create a new DefaultRepresentation with @value set to the string 'foo'. Next the text_field will be called on the newly created DefaultRepresentation
208
- def method_missing(method_name, *args, &block)
209
- method = <<-EOF
210
- def #{method_name}(*args, &block)
211
- @__#{method_name} ||= Representations.representation_for(@value.#{method_name}, @template, "#{method_name}", self)
212
- @__#{method_name}.with_block(&block)
213
- @__#{method_name} if block.nil?
214
- end
215
- EOF
216
- ::Representations::ActiveRecordRepresentation.class_eval(method, __FILE__, __LINE__)
217
- self.__send__(method_name, &block)
218
- end
219
- end
220
- #Representation for TimeWithZone object
221
- class TimeWithZoneRepresentation < Representation
222
- def select(passed_options = {}, html_options = {})
223
- options = {:defaults => {:day => @value.day, :month => @value.month, :year => @value.year}}
224
- options.merge!(passed_options)
225
- tree = get_parents_tree
226
- name = get_html_name_attribute_value(tree)
227
- @template.date_select(name, @name, options, html_options)
228
- end
229
- end
230
- #Representation for Collections
231
- class AssociationsRepresentation < Representation
232
- #initilize @num variable
233
- def initialize(object, template, name, parent)
234
- super
235
- @num = 0
236
- end
237
- #Creates Representation for every object in the Array and invokes passed block with this Representation as the argument
238
- def each
239
- @value.each do |object|
240
- representation_object = Representations.representation_for(object, @template, object.id.to_s, self)
241
- yield representation_object
242
- end
243
- end
244
- #Creates new object in the collection and input fields for it defined in the passed block
245
- def build
246
- new_object = @value.build
247
- representation_object = AssociationsRepresentation::NewRecordRepresentation.new(new_object, @template, 'new_' + num.to_s, self)
248
- yield representation_object
249
- end
250
- private
251
- attr_reader :num
252
- #Used for generating unique @name for ArrayRepresentation::NewRecordRepresentation
253
- def num
254
- @num += 1
255
- end
256
- #Representation that wraps newly created ActiveRecord::Base that will be added to some collection
257
- class NewRecordRepresentation < Representation
258
- #Creates new method which wraps call for ActionRecord
259
- #New method returns Representation which represents datatype in the appropriate column
260
- def method_missing(method_name_symbol, *args, &block)
261
- method_name = method_name_symbol.to_s
262
- representation_class = case @value.class.columns_hash[method_name].type
263
- when :string
264
- "DefaultRepresentation"
265
- when :date
266
- "TimeWithZoneRepresentation"
267
- end
268
- method = <<-EOF
269
- def #{method_name}(*args, &block)
270
- @__#{method_name} ||= #{representation_class}.new(@value.#{method_name}, @template, "#{method_name}", self)
271
- @__#{method_name}.with_block(&block)
272
- @__#{method_name} if block.nil?
273
- end
274
- EOF
275
- ::Representations::AssociationsRepresentation::NewRecordRepresentation.class_eval(method, __FILE__, __LINE__)
276
- self.__send__(method_name_symbol, &block)
277
- end
278
- end
279
- end
280
47
  end
data/lib/view_helpers.rb CHANGED
@@ -1,15 +1,34 @@
1
1
  module Representations
2
- module ViewHelpers
3
- def r(model)
4
- r = Representations.representation_for(model, self, find_variables_name(model))
5
- yield r if block_given?
6
- r
7
- end
8
- private
9
- def find_variables_name(object)
10
- self.instance_variables.each do |name|
11
- return name[1..-1] if instance_variable_get(name) == object
12
- end
2
+ module ViewHelpers
3
+ def r(model)
4
+ if model.class == Representations::ActiveRecordRepresentation
5
+ Rails.logger.info 'Object is already wrapped in Representation'
6
+ r = model
7
+ else
8
+ if model.class == Array #model is an array of AR objects
9
+ model.map!{|m| representation_for(m, self, find_variable_name(model)) if m.is_a?(ActiveRecord::Base)}
10
+ else
11
+ r = Representations.representation_for(model, self, find_variable_name(model))
13
12
  end
13
+ end
14
+ yield r if block_given?
15
+ r
16
+ end
17
+ #This method is aliased when Representations.enable_automatic_wrappint(true) is called
18
+ def instance_variable_set_with_r(symbol, obj)
19
+ load ActiveSupport::Dependencies.search_for_file('representations.rb')
20
+ if obj.is_a?(ActiveRecord::Base)
21
+ obj = Representations.representation_for(obj, self, symbol.to_s[1..-1])
22
+ elsif obj.class == Array #handle case when controller sends array of AR objects
23
+ obj.map!{|o| Representations.representation_for(o, self, symbol.to_s) if o.is_a?(ActiveRecord::Base)}
24
+ end
25
+ instance_variable_set_without_r(symbol, obj) #call to the original method
26
+ end
27
+ private
28
+ def find_variable_name(object)
29
+ self.instance_variables.each do |name|
30
+ return name[1..-1] if instance_variable_get(name) == object
31
+ end
14
32
  end
33
+ end
15
34
  end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{representations}
8
- s.version = "0.0.3"
8
+ s.version = "0.0.4"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["\305\201ukasz Piestrzeniewicz", "Adam Sokolnicki"]
12
- s.date = %q{2009-10-24}
12
+ s.date = %q{2009-11-12}
13
13
  s.description = %q{Rails helpers, including form builders are great to allow rapid development of applications and views.
14
14
  They are procedural in nature and have hard time to adapt to complex models. They also live in a single namespace making it difficult to find which helpers apply to which models.
15
15
  Resource representations change syntax to object oriented and model specific.}
@@ -25,6 +25,12 @@ Resource representations change syntax to object oriented and model specific.}
25
25
  "Rakefile",
26
26
  "VERSION",
27
27
  "init.rb",
28
+ "lib/active_record_representation.rb",
29
+ "lib/associations_representation.rb",
30
+ "lib/controller_helpers.rb",
31
+ "lib/date_representation.rb",
32
+ "lib/default_representation.rb",
33
+ "lib/representation.rb",
28
34
  "lib/representations.rb",
29
35
  "lib/view_helpers.rb",
30
36
  "rails/init.rb",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: representations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - "\xC5\x81ukasz Piestrzeniewicz"
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2009-10-24 00:00:00 +02:00
13
+ date: 2009-11-12 00:00:00 +01:00
14
14
  default_executable:
15
15
  dependencies: []
16
16
 
@@ -33,6 +33,12 @@ files:
33
33
  - Rakefile
34
34
  - VERSION
35
35
  - init.rb
36
+ - lib/active_record_representation.rb
37
+ - lib/associations_representation.rb
38
+ - lib/controller_helpers.rb
39
+ - lib/date_representation.rb
40
+ - lib/default_representation.rb
41
+ - lib/representation.rb
36
42
  - lib/representations.rb
37
43
  - lib/view_helpers.rb
38
44
  - rails/init.rb