ruby_mvc 0.0.1 → 0.0.2

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 (68) hide show
  1. data/README.md +14 -0
  2. data/lib/ruby_mvc.rb +3 -1
  3. data/lib/ruby_mvc/application.rb +52 -2
  4. data/lib/ruby_mvc/controllers.rb +28 -0
  5. data/lib/ruby_mvc/controllers/action.rb +93 -0
  6. data/lib/ruby_mvc/controllers/action_group.rb +70 -0
  7. data/lib/ruby_mvc/controllers/app_controller.rb +4 -2
  8. data/lib/ruby_mvc/controllers/rails_controller.rb +2 -0
  9. data/lib/ruby_mvc/models.rb +2 -0
  10. data/lib/ruby_mvc/models/ar_table_model.rb +133 -0
  11. data/lib/ruby_mvc/models/array_table_model.rb +81 -13
  12. data/lib/ruby_mvc/models/keyed_array_table_model.rb +1 -1
  13. data/lib/ruby_mvc/models/model.rb +129 -0
  14. data/lib/ruby_mvc/models/table_model.rb +107 -10
  15. data/lib/ruby_mvc/models/view_model_template.rb +140 -0
  16. data/lib/ruby_mvc/renderers.rb +1 -0
  17. data/lib/ruby_mvc/renderers/html4_table_model_renderer.rb +7 -6
  18. data/lib/ruby_mvc/renderers/hyperlink_cell_renderer.rb +47 -0
  19. data/lib/ruby_mvc/toolkit.rb +5 -1
  20. data/lib/ruby_mvc/toolkit/browser_history.rb +115 -0
  21. data/lib/ruby_mvc/toolkit/dialog.rb +12 -1
  22. data/lib/ruby_mvc/toolkit/frame.rb +3 -1
  23. data/lib/ruby_mvc/toolkit/grid_view.rb +46 -0
  24. data/lib/ruby_mvc/toolkit/messagebox.rb +32 -0
  25. data/lib/ruby_mvc/toolkit/peers/wxruby.rb +5 -0
  26. data/lib/ruby_mvc/toolkit/peers/wxruby/app.rb +23 -2
  27. data/lib/ruby_mvc/toolkit/peers/wxruby/common.rb +11 -1
  28. data/lib/ruby_mvc/toolkit/peers/wxruby/dialog.rb +82 -0
  29. data/lib/ruby_mvc/toolkit/peers/wxruby/form_builder.rb +108 -0
  30. data/lib/ruby_mvc/toolkit/peers/wxruby/frame.rb +85 -1
  31. data/lib/ruby_mvc/toolkit/peers/wxruby/grid_model.rb +79 -0
  32. data/lib/ruby_mvc/toolkit/peers/wxruby/grid_view.rb +117 -0
  33. data/lib/ruby_mvc/toolkit/peers/wxruby/messagebox.rb +58 -0
  34. data/lib/ruby_mvc/toolkit/peers/wxruby/web_view.rb +40 -10
  35. data/lib/ruby_mvc/toolkit/property_change_notifier.rb +46 -0
  36. data/lib/ruby_mvc/toolkit/signal_handler.rb +149 -0
  37. data/lib/ruby_mvc/toolkit/web_view.rb +1 -1
  38. data/lib/ruby_mvc/toolkit/widget.rb +13 -6
  39. data/lib/ruby_mvc/views.rb +8 -59
  40. data/lib/ruby_mvc/views/{ar_type_list.rb → ar_form_view.rb} +10 -13
  41. data/lib/ruby_mvc/views/ar_support.rb +29 -0
  42. data/lib/ruby_mvc/views/ar_type_editor.rb +17 -28
  43. data/lib/ruby_mvc/views/ar_web_model_view.rb +59 -0
  44. data/lib/ruby_mvc/views/ar_web_type_list.rb +44 -0
  45. data/lib/ruby_mvc/views/browser_view.rb +185 -0
  46. data/lib/ruby_mvc/views/form_view.rb +111 -0
  47. data/lib/ruby_mvc/views/grid_table_view.rb +96 -0
  48. data/lib/ruby_mvc/views/table_view.rb +0 -39
  49. data/lib/ruby_mvc/views/view.rb +121 -0
  50. data/lib/ruby_mvc/views/web_content_table_view.rb +40 -0
  51. data/lib/ruby_mvc/views/{web_view.rb → web_content_view.rb} +17 -28
  52. data/lib/ruby_mvc/views/web_model_view.rb +67 -0
  53. data/ruby_mvc.gemspec +1 -1
  54. data/sample/browser_view.rb +57 -0
  55. data/sample/form.rb +59 -0
  56. data/sample/form2.rb +63 -0
  57. data/sample/grid_table_view.rb +68 -0
  58. data/sample/grid_view.rb +44 -0
  59. data/sample/grid_view2.rb +57 -0
  60. data/sample/test.html +1 -0
  61. data/sample/test2.html +33 -0
  62. data/sample/web_view.rb +4 -0
  63. data/test/unit/models/test_array_table_model.rb +19 -3
  64. data/test/unit/models/test_keyed_array_table_model.rb +3 -2
  65. data/test/unit/models/test_model.rb +88 -0
  66. metadata +39 -20
  67. data/lib/ruby_mvc/toolkit/notification.rb +0 -202
  68. data/lib/ruby_mvc/views/ar_model_editor.rb +0 -84
@@ -23,35 +23,103 @@
23
23
  #####################################################################
24
24
  #++
25
25
 
26
- module ShoesMVC
26
+ module RubyMVC
27
27
  module Models
28
28
 
29
- # This class implements the TableModel interface based on
30
- # the data being stored in an array. The elements in the
31
- # array must either be a Hash, or they must respond to the
32
- # [] accessors for retrieving elements by key and implement
33
- # a #keys method for indicating the properties in each row
34
- # instance.
29
+ # This class provides an adapter to expose an array of
30
+ # hashes or anything else that responds to the #keys, #[]
31
+ # and #[]= methods as a TableModel instance.
32
+ #
33
+ # Note that the keys of the objects in the array will be
34
+ # requested as symbols and not as any other object type.
35
35
 
36
- class ArrayTableModel < TableModel
37
- def initialize(data)
36
+ class HashArrayTableModel < TableModel
37
+ def initialize(data, options = {})
38
+ raise ArgumentError, "argument not an Array" if !data.is_a? Array
39
+ super(options)
40
+
41
+ @options = options
38
42
  @data = data
39
43
  end
40
44
 
45
+ def create_rows(count = 1)
46
+ if f = @options[:row_factory]
47
+ f.call(count)
48
+ else
49
+ r = []
50
+ t = @options[:row_template] || @data.last
51
+ count.times { r << t.clone }
52
+ r
53
+ end
54
+ end
55
+
56
+ def insert_row(index, row)
57
+ @data.insert(index, row)
58
+ super(index, Model.adapt(row, @options))
59
+ end
60
+
61
+ def insert_rows(index, rows)
62
+ @data.insert(index, *rows)
63
+ super(index, rows.collect { |r| Model.adapt(r, @options) })
64
+ end
65
+
66
+ def remove_row(index)
67
+ row = Model.adapt(@data.delete_at(index), @options)
68
+ signal_emit("rows-removed", self, index, [ row ])
69
+ row
70
+ end
71
+
72
+ def remove_rows(index, count)
73
+ rows = []
74
+ count.times do
75
+ rows << Model.adapt(@data.delete_at(index), @options)
76
+ end
77
+ signal_emit("rows-removed", self, index, rows)
78
+ rows
79
+ end
80
+
81
+ def update_row(index, row)
82
+ @data[index] = row
83
+ super(index, Model.adapt(row, @options))
84
+ end
85
+
86
+ # The keys used will come from the first element in the
87
+ # array or be empty if the array is empty.
88
+
41
89
  def keys
42
- if @data && @data.size > 0
43
- @data.keys
90
+ if @data.size > 0
91
+ @data.first.keys
44
92
  else
45
93
  {}
46
94
  end
47
95
  end
48
96
 
97
+ def value_for(row, key)
98
+ @data[row][key.to_sym]
99
+ end
100
+
101
+ def [](row)
102
+ @data[row]
103
+ end
104
+
105
+ # Iterates over each of the elements in the array and
106
+ # provides a Model instance to the caller based on the
107
+ # keys contained in the object itself.
108
+
49
109
  def each(&block)
50
- @data.each(&block)
110
+ return if !block
111
+
112
+ @data.each do |x|
113
+ block.call(Model.adapt(x, @options))
114
+ end
51
115
  end
52
116
 
53
117
  def each_with_index(&block)
54
- @data.each_with_index(&block)
118
+ return if !block
119
+
120
+ @data.each_with_index do |x, i|
121
+ block.call(Model.adapt(x, @options), i)
122
+ end
55
123
  end
56
124
  end
57
125
 
@@ -23,7 +23,7 @@
23
23
  #####################################################################
24
24
  #++
25
25
 
26
- module ShoesMVC
26
+ module RubyMVC
27
27
  module Models
28
28
 
29
29
  class KeyedArrayTableModel < TableModel
@@ -0,0 +1,129 @@
1
+ #--
2
+ ######################################################################
3
+ #
4
+ # Copyright 2011 Andrew S. Townley
5
+ #
6
+ # Permission to use, copy, modify, and disribute this software for
7
+ # any purpose with or without fee is hereby granted, provided that
8
+ # the above copyright notices and this permission notice appear in
9
+ # all copies.
10
+ #
11
+ # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL
12
+ # WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
13
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14
+ # AUTHORS BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT OR
15
+ # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
16
+ # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
17
+ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
18
+ # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
+ #
20
+ # File: model.rb
21
+ # Created: Fri 30 Dec 2011 12:46:32 CET
22
+ #
23
+ #####################################################################
24
+ #++
25
+
26
+ module RubyMVC
27
+ module Models
28
+
29
+ # This class defines the generic API for models in RubyMVC.
30
+ # Models expose key/value pairs to other parts of the
31
+ # system, but they are more than simple Hash instances
32
+ # because not all properties of a model are always editable.
33
+
34
+ class Model
35
+ include Toolkit::SignalHandler
36
+ extend Toolkit::SignalHandler::ClassMethods
37
+
38
+ # this signal is emitted when any property is changed on
39
+ # the model. The arguments are the sender, the old and
40
+ # the new values
41
+
42
+ signal "property-changed"
43
+
44
+ # This method is used to use the Model class to adapt any
45
+ # object that responds to the #keys, #size, #[] and #[]=
46
+ # methods
47
+
48
+ def self.adapt(obj, options = {})
49
+ return obj if obj.is_a? Model
50
+ self.new(options.merge({:data => obj}))
51
+ end
52
+
53
+ # The base model is backed by a simple Hash, and supports
54
+ # specifying the following options for defining which
55
+ # properties are editable or not.
56
+
57
+ def initialize(options = {})
58
+ @data = options.delete(:data) || {}
59
+ @options = options
60
+ end
61
+
62
+ # This method is used to provide the property keys of the
63
+ # model as symbols.
64
+
65
+ def keys
66
+ @data.keys.sort.collect { |k| k.to_sym }
67
+ end
68
+
69
+ # This method is used to retrieve the property key and the
70
+ # display label for the key to be used by views when
71
+ # displaying model data.
72
+
73
+ def labels
74
+ @labels = {}
75
+ self.keys.collect do |k|
76
+ x = { :key => k.to_sym, :label => k.to_s.capitalize }
77
+ @labels[x[:key]] = x[:label]
78
+ x
79
+ end
80
+ end
81
+
82
+ # This method is used to check whether a property key is
83
+ # editable or not
84
+
85
+ def is_editable?(key)
86
+ (@options[:editable] || {})[key.to_sym] || true
87
+ end
88
+
89
+ def [](key)
90
+ @data[key.to_sym]
91
+ end
92
+
93
+ def []=(key, val)
94
+ k = key.to_sym
95
+ old = @data[k]
96
+ @data[k] = val
97
+
98
+ if old != val
99
+ signal_emit("property-changed", self, k, old, val)
100
+ end
101
+ end
102
+
103
+ def size
104
+ @data.size
105
+ end
106
+
107
+ # This method is used to iterate over the properties of
108
+ # the model. For each property, the key, label and value
109
+ # are provide as arguments to the block
110
+
111
+ def each_label(&block)
112
+ labels.each do |l|
113
+ block.call((k = l[:key]), l[:label], @data[k])
114
+ end
115
+ end
116
+
117
+ # This method is used to retrieve the label for the
118
+ # specified model key.
119
+
120
+ def label_for(key)
121
+ if !@labels
122
+ labels
123
+ end
124
+ @labels[key.to_sym]
125
+ end
126
+ end
127
+
128
+ end
129
+ end
@@ -23,19 +23,116 @@
23
23
  #####################################################################
24
24
  #++
25
25
 
26
- module ShoesMVC
26
+ module RubyMVC
27
27
  module Models
28
28
 
29
- # This class defines the behavior of a base TableModel API
30
- # that is used by the TableView class. The model represents
31
- # an ordered set of data rows which are "square" in that
32
- # they are expected to all have the same number of columns.
33
- #
34
- # Logically, each of the elements in the table model is a
35
- # row instance that provides keyed property access to the
36
- # data in the model based on the [] method.
29
+ # The TableModel class expands the concepts of the Model
30
+ # class to a number of related rows with the same properties
31
+ # defined for the model.
37
32
 
38
- class TableModel
33
+ class TableModel < Model
34
+ # This signal is triggered when contiguous rows are
35
+ # inserted into the model. The arguments are the sender
36
+ # model, the insertion index and the row models that were
37
+ # inserted.
38
+
39
+ signal "rows-inserted"
40
+
41
+ # This signal is triggered when contiguous rows are
42
+ # removed from the model. The arguments are the index at
43
+ # which the removal took place and the row models that
44
+ # were removed from the model.
45
+
46
+ signal "rows-removed"
47
+
48
+ # This signal is triggered when an existing row has been
49
+ # changed. The arguments are the sender, the index and
50
+ # the changed row model
51
+
52
+ signal "row-changed"
53
+
54
+ # This method is used to create a number of row data
55
+ # elements that will be inserted into the model at the
56
+ # specified location using the #insert_row or #insert_rows
57
+ # method.
58
+ #
59
+ # The result is an array of row data instances in whatever
60
+ # format is deemed suitable for the model instance to
61
+ # create.
62
+
63
+ def create_rows(count = 1)
64
+ end
65
+
66
+ # This method is used to insert a single row into the
67
+ # model at the given row index.
68
+
69
+ def insert_row(index, row)
70
+ signal_emit("rows-inserted", self, index, [ row ])
71
+ end
72
+
73
+ # This method is used to insert an array of row objects
74
+ # into the model at the specified index
75
+
76
+ def insert_rows(index, rows)
77
+ signal_emit("rows-inserted", self, index, rows)
78
+ end
79
+
80
+ # This method is used remove a single row from the model
81
+ # and return a reference to the row model removed to the
82
+ # caller.
83
+ #
84
+ # Note, the derived classes are responsible for emitting
85
+ # the appropriate signal since the data access is opaque
86
+ # to the base class.
87
+
88
+ def remove_row(index)
89
+ end
90
+
91
+ # This method is used remove multiple rows from the model
92
+ # and return a reference to the row models removed to the
93
+ # caller.
94
+ #
95
+ # Note, the derived classes are responsible for emitting
96
+ # the appropriate signal since the data access is opaque
97
+ # to the base class.
98
+
99
+ def remove_rows(index, count)
100
+ end
101
+
102
+ # This method is used to update the specified row in the
103
+ # model and ensure that the appropriate notifications for
104
+ # all linked views are sent.
105
+
106
+ def update_row(index, row)
107
+ signal_emit("row-changed", index, row)
108
+ end
109
+
110
+ # This method will iterate over each of the rows and
111
+ # provide the caller with a reference to the row instance,
112
+ # which is also conformant with the Model API
113
+
114
+ def each(&block)
115
+ end
116
+
117
+ # This method will iterate over each of rows and provide
118
+ # the caller with a reference to the row instance as a
119
+ # model and the index of the row.
120
+
121
+ def each_with_index(&block)
122
+ end
123
+
124
+ # This method allows direct access into the model rows, in
125
+ # row-major order for the specified, zero-based index and
126
+ # property key
127
+
128
+ def value_for(row, key)
129
+ end
130
+
131
+ # This method is used to retrieve the model instance for
132
+ # the given row index
133
+
134
+ def [](idx)
135
+ end
39
136
  end
40
137
 
41
138
  end
@@ -0,0 +1,140 @@
1
+ #--
2
+ ######################################################################
3
+ #
4
+ # Copyright 2011 Andrew S. Townley
5
+ #
6
+ # Permission to use, copy, modify, and disribute this software for
7
+ # any purpose with or without fee is hereby granted, provided that
8
+ # the above copyright notices and this permission notice appear in
9
+ # all copies.
10
+ #
11
+ # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL
12
+ # WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
13
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14
+ # AUTHORS BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT OR
15
+ # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
16
+ # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
17
+ # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
18
+ # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
+ #
20
+ # File: view_model_template.rb
21
+ # Created: Mon 12 Dec 2011 06:30:57 CST
22
+ #
23
+ #####################################################################
24
+ #++
25
+
26
+ module RubyMVC
27
+ module Models
28
+
29
+ # This class provides a proxy for Model instances that
30
+ # allows them to control the visibility of which properties
31
+ # are exposed for a given view. ViewModelTemplate instances can
32
+ # be reused across view instances where the same properties
33
+ # are to be exposed.
34
+ #
35
+ # The ViewModelTemplate class implements the same interface as
36
+ # Model instances so that they can be used as drop-in
37
+ # replacements everywhere a model instance can be used.
38
+ #
39
+ # The key difference is that the ViewModelTemplate exists outside
40
+ # of any particular model instances, allowing the same view
41
+ # binding to work for numerous concrete model instances,
42
+ # effectively speifying the template through which the model
43
+ # may be accessed.
44
+
45
+ class ViewModelTemplate
46
+ attr_accessor :title
47
+
48
+ # When the ViewModelTemplate is initialized, it requires a set
49
+ # of options and/or an optional initializer block that is
50
+ # executed within the scope of the newly created instance.
51
+
52
+ def initialize(options = {}, &block)
53
+ @options = options
54
+ @labels = []
55
+ @options[:editable] ||= {}
56
+ @options[:properties] ||= []
57
+ self.instance_eval(&block) if block
58
+ end
59
+
60
+ def keys
61
+ @options[:properties]
62
+ end
63
+
64
+ # Implement the Model#labels method in terms of the
65
+ # template definition
66
+
67
+ def labels
68
+ @labels
69
+ end
70
+
71
+ # Implement the Model#is_editable? method in terms of the
72
+ # template definition. If the template doesn't further
73
+ # restrict the behavior, the model's definition is used
74
+ # instead.
75
+
76
+ def is_editable?(key)
77
+ k = key.to_sym
78
+ if @options[:editable].has_key? k
79
+ @options[:editable][k]
80
+ else
81
+ true
82
+ end
83
+ end
84
+
85
+ # This method is used to mark a property key editable or
86
+ # not through the view. This method does not impact the
87
+ # editability of the underlying model
88
+
89
+ def editable(key, val = true)
90
+ @options[:editable][key.to_sym] = val
91
+ end
92
+
93
+ # This method is used to mark a property visible or not
94
+ # through the view.
95
+
96
+ def property(key, options = {})
97
+ puts "options: #{options.inspect}"
98
+ key = key.to_sym
99
+
100
+ if false == options[:show]
101
+ show = false
102
+ else
103
+ show = true
104
+ end
105
+
106
+ editable(key, false) if false == options[:editable]
107
+
108
+ if show && !@options[:properties].include?(key)
109
+ @options[:properties] << key
110
+ l = options[:alias] || key.to_s.capitalize
111
+ @labels << options.merge({ :key => key, :label => l })
112
+ elsif !show
113
+ @options[:properties].delete(key)
114
+ @labels.delete_if do |k|
115
+ k[:key] == key
116
+ end
117
+ end
118
+ end
119
+
120
+ # This method is used to apply the template to a specific,
121
+ # concrete model instance, creating a clone of the
122
+ # template in the process so that multiple different
123
+ # models can be used with the same template definition.
124
+
125
+ def apply(model)
126
+ @model = model
127
+ self.clone
128
+ end
129
+
130
+ def method_missing(m, *args, &block)
131
+ if @model
132
+ @model.send(m, *args, &block)
133
+ else
134
+ super
135
+ end
136
+ end
137
+ end
138
+
139
+ end
140
+ end