netzke-core 0.1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/CHANGELOG +13 -0
  2. data/LICENSE +20 -0
  3. data/Manifest +39 -0
  4. data/README.mdown +11 -0
  5. data/Rakefile +14 -0
  6. data/generators/netzke_core/USAGE +8 -0
  7. data/generators/netzke_core/netzke_core_generator.rb +13 -0
  8. data/generators/netzke_core/templates/create_netzke_layouts.rb +14 -0
  9. data/generators/netzke_core/templates/create_netzke_preferences.rb +18 -0
  10. data/generators/netzke_core/templates/netzke.html.erb +10 -0
  11. data/init.rb +1 -0
  12. data/install.rb +1 -0
  13. data/javascripts/core.js +124 -0
  14. data/lib/app/controllers/netzke_controller.rb +16 -0
  15. data/lib/app/models/netzke_layout.rb +75 -0
  16. data/lib/app/models/netzke_preference.rb +66 -0
  17. data/lib/netzke-core.rb +28 -0
  18. data/lib/netzke/base.rb +210 -0
  19. data/lib/netzke/controller_extensions.rb +95 -0
  20. data/lib/netzke/core_ext.rb +77 -0
  21. data/lib/netzke/js_class_builder.rb +114 -0
  22. data/lib/vendor/facets/hash/recursive_merge.rb +28 -0
  23. data/netzke-core.gemspec +32 -0
  24. data/tasks/netzke_core_tasks.rake +4 -0
  25. data/test/app_root/app/controllers/application.rb +2 -0
  26. data/test/app_root/config/boot.rb +114 -0
  27. data/test/app_root/config/database.yml +21 -0
  28. data/test/app_root/config/environment.rb +13 -0
  29. data/test/app_root/config/environments/in_memory.rb +0 -0
  30. data/test/app_root/config/environments/mysql.rb +0 -0
  31. data/test/app_root/config/environments/postgresql.rb +0 -0
  32. data/test/app_root/config/environments/sqlite.rb +0 -0
  33. data/test/app_root/config/environments/sqlite3.rb +0 -0
  34. data/test/app_root/config/routes.rb +4 -0
  35. data/test/app_root/script/console +6 -0
  36. data/test/core_ext_test.rb +35 -0
  37. data/test/netzke_core_test.rb +133 -0
  38. data/test/test_helper.rb +20 -0
  39. data/uninstall.rb +1 -0
  40. metadata +109 -0
data/CHANGELOG ADDED
@@ -0,0 +1,13 @@
1
+ v0.1.1.1
2
+ Meta: moving from GitHub to RubyForge
3
+
4
+ v0.1.1
5
+ Inter-widget dependencies code reworked
6
+ JS-class code generation code slightly reworked
7
+
8
+ v0.1.0.2
9
+ Meta: fix outdated Manifest
10
+
11
+ v0.1.0.1 Meta work: replacing underscore with dash in the name
12
+
13
+ v0.1.0 Initial release
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Sergei Kozlov
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest ADDED
@@ -0,0 +1,39 @@
1
+ CHANGELOG
2
+ generators/netzke_core/netzke_core_generator.rb
3
+ generators/netzke_core/templates/create_netzke_layouts.rb
4
+ generators/netzke_core/templates/create_netzke_preferences.rb
5
+ generators/netzke_core/templates/netzke.html.erb
6
+ generators/netzke_core/USAGE
7
+ init.rb
8
+ install.rb
9
+ javascripts/core.js
10
+ lib/app/controllers/netzke_controller.rb
11
+ lib/app/models/netzke_layout.rb
12
+ lib/app/models/netzke_preference.rb
13
+ lib/netzke/base.rb
14
+ lib/netzke/controller_extensions.rb
15
+ lib/netzke/core_ext.rb
16
+ lib/netzke/js_class_builder.rb
17
+ lib/netzke-core.rb
18
+ lib/vendor/facets/hash/recursive_merge.rb
19
+ LICENSE
20
+ Manifest
21
+ netzke-core.gemspec
22
+ Rakefile
23
+ README.mdown
24
+ tasks/netzke_core_tasks.rake
25
+ test/app_root/app/controllers/application.rb
26
+ test/app_root/config/boot.rb
27
+ test/app_root/config/database.yml
28
+ test/app_root/config/environment.rb
29
+ test/app_root/config/environments/in_memory.rb
30
+ test/app_root/config/environments/mysql.rb
31
+ test/app_root/config/environments/postgresql.rb
32
+ test/app_root/config/environments/sqlite.rb
33
+ test/app_root/config/environments/sqlite3.rb
34
+ test/app_root/config/routes.rb
35
+ test/app_root/script/console
36
+ test/core_ext_test.rb
37
+ test/netzke_core_test.rb
38
+ test/test_helper.rb
39
+ uninstall.rb
data/README.mdown ADDED
@@ -0,0 +1,11 @@
1
+ netzke-core
2
+ ==========
3
+
4
+ Create ExtJS/Rails reusable components (widgets) with minimum effort.
5
+
6
+ Example
7
+ =======
8
+
9
+ See the tutorials on http://blog.writelesscode.com
10
+
11
+ Copyright (c) 2008 Sergei Kozlov, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'echoe'
2
+
3
+ Echoe.new("netzke-core") do |p|
4
+ p.author = "Sergei Kozlov"
5
+ p.email = "sergei@writelesscode.com"
6
+ p.summary = "Build ExtJS/Rails widgets with minimum effort"
7
+ p.url = "http://writelesscode.com"
8
+ p.development_dependencies = []
9
+ p.test_pattern = 'test/**/*_test.rb'
10
+ p.retain_gemspec = true
11
+
12
+ # fixing the problem with lib/*-* files being removed while doing manifest
13
+ p.clean_pattern = ["pkg", "doc", 'build/*', '**/coverage', '**/*.o', '**/*.so', '**/*.a', '**/*.log', "{ext,lib}/*.{bundle,so,obj,pdb,lib,def,exp}", "ext/Makefile", "{ext,lib}/**/*.{bundle,so,obj,pdb,lib,def,exp}", "ext/**/Makefile", "pkg", "*.gem", ".config"]
14
+ end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Explain the generator
3
+
4
+ Example:
5
+ ./script/generate netzke-core Thing
6
+
7
+ This will create:
8
+ what/will/it/create
@@ -0,0 +1,13 @@
1
+ # class NetzkeCoreGenerator < Rails::Generator::NamedBase
2
+ class NetzkeCoreGenerator < Rails::Generator::Base
3
+ def manifest
4
+ record do |m|
5
+ # m.directory "public/javascripts/netzke"
6
+ # m.file 'netzke.js', "public/javascripts/netzke/netzke.js"
7
+ m.file 'netzke.html.erb', "app/views/layouts/netzke.html.erb"
8
+ m.migration_template 'create_netzke_preferences.rb', "db/migrate", {:migration_file_name => "create_netzke_preferences"}
9
+ # FIXME: how do we avoid getting the same migration IDs?
10
+ m.migration_template 'create_netzke_layouts.rb', "db/migrate", {:migration_file_name => "create_netzke_layouts"}
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ class CreateNetzkeLayouts < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :netzke_layouts do |t|
4
+ t.string :widget_name
5
+ t.string :items_class
6
+ t.integer :user_id
7
+ t.timestamps
8
+ end
9
+ end
10
+
11
+ def self.down
12
+ drop_table :netzke_layouts
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ class CreateNetzkePreferences < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :netzke_preferences do |t|
4
+ t.string :name
5
+ t.string :pref_type
6
+ t.string :value
7
+ t.integer :user_id
8
+ t.integer :role_id
9
+ t.string :custom_field
10
+
11
+ t.timestamps
12
+ end
13
+ end
14
+
15
+ def self.down
16
+ drop_table :netzke_preferences
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ <head>
2
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
3
+ <title><%= @widget_name %></title>
4
+ <%= javascript_include_tag("/extjs/adapter/ext/ext-base.js", "/extjs/ext-all-debug.js") %>
5
+ <%= javascript_include_tag("/netzke/netzke.js") %>
6
+ <%= stylesheet_link_tag("/extjs/resources/css/ext-all.css") %>
7
+ </head>
8
+ <body id="">
9
+ <%= yield %>
10
+ </body>
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ # require 'netzke-core'
data/install.rb ADDED
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,124 @@
1
+ Ext.BLANK_IMAGE_URL = "/extjs/resources/images/default/s.gif";
2
+ Ext.componentCache = {};
3
+
4
+ Ext.namespace('Ext.netzke');
5
+
6
+ // helper method to do multiple Ext.apply's
7
+ Ext.chainApply = function(objectArray){
8
+ var res = {};
9
+ Ext.each(objectArray, function(obj){Ext.apply(res, obj)});
10
+ return res;
11
+ };
12
+
13
+ // implementation of totalProperty, successProperty and root configuration options for ArrayReader
14
+ Ext.data.ArrayReader = Ext.extend(Ext.data.JsonReader, {
15
+ readRecords : function(o){
16
+ var sid = this.meta ? this.meta.id : null;
17
+ var recordType = this.recordType, fields = recordType.prototype.fields;
18
+ var records = [];
19
+ // console.info(this.meta);
20
+ var root = o[this.meta.root] || o, totalRecords = o[this.meta.totalProperty], success = o[this.meta.successProperty];
21
+ for(var i = 0; i < root.length; i++){
22
+ var n = root[i];
23
+ var values = {};
24
+ var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
25
+ for(var j = 0, jlen = fields.length; j < jlen; j++){
26
+ var f = fields.items[j];
27
+ var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
28
+ var v = n[k] !== undefined ? n[k] : f.defaultValue;
29
+ v = f.convert(v, n);
30
+ values[f.name] = v;
31
+ }
32
+ var record = new recordType(values, id);
33
+ record.json = n;
34
+ records[records.length] = record;
35
+ }
36
+ return {
37
+ records : records,
38
+ totalRecords : totalRecords,
39
+ success : success
40
+ };
41
+ }
42
+ });
43
+
44
+ // Methods common to all widget classes
45
+ Ext.widgetMixIn = {
46
+ widgetInit:function(config){
47
+ this.app = Ext.getCmp('application');
48
+ if (config.tools) Ext.each(config.tools, function(i){i.on.click = this[i.on.click].createDelegate(this)}, this);
49
+ if (config.actions) Ext.each(config.actions, function(i){i.handler = this[i.handler].createDelegate(this);}, this);
50
+ },
51
+
52
+ setEvents: function(){
53
+ this.on('beforedestroy', function(){
54
+ // clean-up menus
55
+ if (this.app && !!this.app.unhostMenus) {
56
+ // alert('beforedestroy');
57
+ this.app.unhostMenus(this)
58
+ }
59
+ }, this);
60
+
61
+ this.on('render', this.onWidgetLoad, this);
62
+ },
63
+
64
+ feedback:function(msg){
65
+ if (this.app && !!this.app.showFeedback) {
66
+ this.app.showFeedback(msg)
67
+ } else {
68
+ // there's no application to show the feedback - so, we do it ourselves
69
+ if (typeof msg == 'string'){
70
+ alert(msg)
71
+ } else {
72
+ var compoundResponse = ""
73
+ Ext.each(msg, function(m){
74
+ compoundResponse += m.msg + "\n"
75
+ })
76
+ if (compoundResponse != "") alert(compoundResponse);
77
+ }
78
+ };
79
+ },
80
+
81
+ addMenus:function(menus){
82
+ if (this.app && !!this.app.hostMenu) {
83
+ Ext.each(menus, function(menu){this.app.hostMenu(menu, this)}, this)
84
+ }
85
+ },
86
+
87
+ onWidgetLoad:Ext.emptyFn // gets overridden
88
+ };
89
+
90
+ // Make Panel with layout 'fit' capable to dynamically load widgets
91
+ Ext.override(Ext.Panel, {
92
+ getWidget: function(){
93
+ return this.items.get(0)
94
+ },
95
+
96
+ loadWidget: function(url, params){
97
+ if (!params) params = {}
98
+
99
+ this.remove(this.getWidget()); // first delete previous widget
100
+
101
+ // we will let the server know which components we have cached
102
+ var cachedComponentNames = [];
103
+ for (name in Ext.componentCache) {
104
+ cachedComponentNames.push(name);
105
+ }
106
+
107
+ this.disable(); // to visually emphasize loading
108
+
109
+ Ext.Ajax.request(
110
+ {url:url, params:Ext.apply(params, {components_cache:Ext.encode(cachedComponentNames)}), script:false, callback:function(panel, success, response){
111
+ var response = Ext.decode(response.responseText);
112
+ if (response['classDefinition']) eval(response['classDefinition']); // evaluate widget's class if it was sent
113
+
114
+ response.config.parent = this // we might want to know the parent panel in advance (e.g. to know its size)
115
+ var instance = new Ext.componentCache[response.config.widgetClassName](response.config)
116
+
117
+ this.add(instance);
118
+ this.doLayout();
119
+ this.enable();
120
+ }, scope:this}
121
+ )
122
+
123
+ }
124
+ });
@@ -0,0 +1,16 @@
1
+ class NetzkeController < ActionController::Base
2
+
3
+ # collect javascripts from all plugins that registered it in Netzke::Base.config[:javascripts]
4
+ def netzke
5
+ respond_to do |format|
6
+ format.js {
7
+ res = ""
8
+ Netzke::Base.config[:javascripts].each do |path|
9
+ f = File.new(path)
10
+ res << f.read
11
+ end
12
+ render :text => res
13
+ }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,75 @@
1
+ class NetzkeLayout < ActiveRecord::Base
2
+ # has_many :layout_items#, :class_name => "ExtWidget::LayoutItem", :order => :position
3
+ # has_many :objects, :class_name => "Objects", :foreign_key => "class_name_id"
4
+ # belongs_to :role
5
+ # belongs_to :user
6
+
7
+ UNRELATED_ATTRS = %w(created_at updated_at position layout_id)
8
+
9
+ def self.user_id
10
+ @@user_id ||= nil
11
+ end
12
+
13
+ def layout_items
14
+ items_class.constantize.find_all_by_layout_id(id, :order => 'position')
15
+ end
16
+
17
+ #
18
+ # def self.user_id=(user_id)
19
+ # @@user_id = user_id
20
+ # end
21
+ #
22
+ # def self.layout_items(widget_name)
23
+ # layout = self.layout(widget_name)
24
+ # layout.nil? ? nil : layout.layout_items.map(&:attributes).map{|item| item.delete_if{|k,v| UNRELATED_ATTRS.include?(k)}}
25
+ # end
26
+ #
27
+ def self.by_widget(widget_name)
28
+ self.find(:first, :conditions => {:widget_name => widget_name, :user_id => self.user_id})
29
+ end
30
+
31
+ def move_item(old_index, new_index)
32
+ layout_item = layout_items[old_index]
33
+ layout_item.remove_from_list
34
+ layout_item.insert_at(new_index + 1)
35
+ end
36
+
37
+ # def self.layout_items(widget_name)
38
+ # layout = self.by_widget(widget_name)
39
+ # if layout
40
+ # layout.layout_items
41
+ # else
42
+ # # create new layout and fill it out with default values
43
+ # layout = Layout.create({:widget_name => widget_name, :user_id => self.user_id})
44
+ # end
45
+ # end
46
+
47
+ def items_hash
48
+ layout_items.map(&:attributes).map{|item| item.delete_if{|k,v| UNRELATED_ATTRS.include?(k)}}.map{ |i| i.convert_keys{ |k| k.to_sym } }
49
+ end
50
+
51
+ # if layout items are provided, use them instead of defaults (exsposed) layout items, but merge their configs with the default
52
+ # def self.create_layout_for_widget(widget_name, data_class_name, layout_item_class_name, items = nil)
53
+ # layout = self.create(:widget_name => widget_name, :items_class => layout_item_class_name, :user_id => self.user_id)
54
+ # data_class = data_class_name.constantize
55
+ # layout_item_class = layout_item_class_name.constantize
56
+ #
57
+ #
58
+ # if items.nil?
59
+ # complete_items = data_class.exposed_columns
60
+ # else
61
+ # # normalize columns
62
+ # columns = columns.
63
+ # default_columns = data_class.exposed_columns.map{|c| c.is_a?(Symbol) ? {:name => c} : c}
64
+ # columns.each
65
+ #
66
+ # complete_columns = columns.nil? ? : columns
67
+ # complete_columns.each do |c|
68
+ # config = c.is_a?(Hash) ? c : {:name => c}
69
+ # # we have to merge layout_id in order to have :position set up properly
70
+ # item = layout_item_class.create_with_defaults(config.merge({:layout_id => layout.id}), data_class)
71
+ # end
72
+ # layout
73
+ # end
74
+
75
+ end
@@ -0,0 +1,66 @@
1
+ class NetzkePreference < ActiveRecord::Base
2
+ CONVERTION_METHODS= {'Fixnum' => 'to_i', 'String' => 'to_s', 'Float' => 'to_f', 'Symbol' => 'to_sym'}
3
+
4
+ def self.user=(user)
5
+ @@user = user
6
+ end
7
+
8
+ def self.user
9
+ @@user ||= nil
10
+ end
11
+
12
+ def self.custom_field=(value)
13
+ @@custom_field = value
14
+ end
15
+
16
+ def self.custom_field
17
+ @@custom_field ||= nil
18
+ end
19
+
20
+ def normalized_value
21
+ klass = read_attribute(:pref_type)
22
+ norm_value = read_attribute(:value)
23
+ if klass.nil?
24
+ # do not cast
25
+ r = norm_value
26
+ elsif klass == 'Boolean'
27
+ r = norm_value == 'false' ? false : (norm_value == 'true' || norm_value)
28
+ elsif klass == 'NilClass'
29
+ r = nil
30
+ elsif klass == 'Array'
31
+ r = JSON.parse(norm_value)
32
+ else
33
+ r = norm_value.send(CONVERTION_METHODS[klass])
34
+ end
35
+ r
36
+ end
37
+
38
+ def normalized_value=(new_value)
39
+ # norm_value = (new_value.to_s if new_value == true or new_value == false) || new_value
40
+ case new_value.class.to_s
41
+ when "Array"
42
+ write_attribute(:value, new_value.to_json)
43
+ else
44
+ write_attribute(:value, new_value.to_s)
45
+ end
46
+ write_attribute(:pref_type, [TrueClass, FalseClass].include?(new_value.class) ? 'Boolean' : new_value.class.to_s)
47
+ end
48
+
49
+ def self.[](pref_name)
50
+ pref_name = pref_name.to_s
51
+ conditions = {:name => pref_name, :user_id => self.user, :custom_field => self.custom_field}
52
+ pref = self.find(:first, :conditions => conditions)
53
+ # pref = @@user.nil? ? self.find_by_name(pref_name) : self.find_by_name_and_user_id(pref_name, @@user.id)
54
+ pref && pref.normalized_value
55
+ end
56
+
57
+ def self.[]=(pref_name, new_value)
58
+ pref_name = pref_name.to_s
59
+ conditions = {:name => pref_name, :user_id => self.user, :custom_field => self.custom_field}
60
+ pref = self.find(:first, :conditions => conditions) || self.create(conditions)
61
+ # pref = self.user.nil? ? self.find_or_create_by_name(pref_name) : self.find_or_create_by_name_and_user_id(pref_name, self.user.id)
62
+ pref.normalized_value = new_value
63
+ pref.save!
64
+ end
65
+
66
+ end