netzke-core 0.1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +13 -0
- data/LICENSE +20 -0
- data/Manifest +39 -0
- data/README.mdown +11 -0
- data/Rakefile +14 -0
- data/generators/netzke_core/USAGE +8 -0
- data/generators/netzke_core/netzke_core_generator.rb +13 -0
- data/generators/netzke_core/templates/create_netzke_layouts.rb +14 -0
- data/generators/netzke_core/templates/create_netzke_preferences.rb +18 -0
- data/generators/netzke_core/templates/netzke.html.erb +10 -0
- data/init.rb +1 -0
- data/install.rb +1 -0
- data/javascripts/core.js +124 -0
- data/lib/app/controllers/netzke_controller.rb +16 -0
- data/lib/app/models/netzke_layout.rb +75 -0
- data/lib/app/models/netzke_preference.rb +66 -0
- data/lib/netzke-core.rb +28 -0
- data/lib/netzke/base.rb +210 -0
- data/lib/netzke/controller_extensions.rb +95 -0
- data/lib/netzke/core_ext.rb +77 -0
- data/lib/netzke/js_class_builder.rb +114 -0
- data/lib/vendor/facets/hash/recursive_merge.rb +28 -0
- data/netzke-core.gemspec +32 -0
- data/tasks/netzke_core_tasks.rake +4 -0
- data/test/app_root/app/controllers/application.rb +2 -0
- data/test/app_root/config/boot.rb +114 -0
- data/test/app_root/config/database.yml +21 -0
- data/test/app_root/config/environment.rb +13 -0
- data/test/app_root/config/environments/in_memory.rb +0 -0
- data/test/app_root/config/environments/mysql.rb +0 -0
- data/test/app_root/config/environments/postgresql.rb +0 -0
- data/test/app_root/config/environments/sqlite.rb +0 -0
- data/test/app_root/config/environments/sqlite3.rb +0 -0
- data/test/app_root/config/routes.rb +4 -0
- data/test/app_root/script/console +6 -0
- data/test/core_ext_test.rb +35 -0
- data/test/netzke_core_test.rb +133 -0
- data/test/test_helper.rb +20 -0
- data/uninstall.rb +1 -0
- 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
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,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
|
data/javascripts/core.js
ADDED
@@ -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
|