sethyates-content_manager 0.4.0 → 1.0.0
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.
- data/README.rdoc +253 -1
- data/VERSION +1 -1
- data/content_manager.gemspec +38 -2
- data/doc/created.rid +1 -0
- data/doc/files/README_rdoc.html +487 -0
- data/doc/fr_class_index.html +26 -0
- data/doc/fr_file_index.html +27 -0
- data/doc/fr_method_index.html +26 -0
- data/doc/index.html +24 -0
- data/doc/rdoc-style.css +208 -0
- data/generators/component_scaffold/USAGE +28 -0
- data/generators/component_scaffold/component_scaffold_generator.rb +84 -0
- data/generators/component_scaffold/templates/controller.rb +85 -0
- data/generators/component_scaffold/templates/model.rb +5 -0
- data/generators/component_scaffold/templates/style.css +1 -0
- data/generators/component_scaffold/templates/view_edit.html.erb +13 -0
- data/generators/component_scaffold/templates/view_index.html.erb +22 -0
- data/generators/component_scaffold/templates/view_new.html.erb +12 -0
- data/generators/component_scaffold/templates/view_show.html.erb +3 -0
- data/generators/content_scaffold/USAGE +28 -0
- data/generators/content_scaffold/content_scaffold_generator.rb +83 -0
- data/generators/content_scaffold/templates/controller.rb +85 -0
- data/generators/content_scaffold/templates/model.rb +8 -0
- data/generators/content_scaffold/templates/view_edit.html.erb +13 -0
- data/generators/content_scaffold/templates/view_index.html.erb +22 -0
- data/generators/content_scaffold/templates/view_new.html.erb +12 -0
- data/generators/content_scaffold/templates/view_show.html.erb +8 -0
- data/lib/component.rb +28 -0
- data/lib/content/adapters/base.rb +79 -0
- data/lib/content/adapters/cabinet_adapter.rb +62 -0
- data/lib/content/adapters/tyrant_adapter.rb +73 -0
- data/lib/content/item.rb +178 -0
- data/lib/content/item_association_class_methods.rb +148 -0
- data/lib/content/item_class_methods.rb +71 -0
- data/lib/content/item_dirty_methods.rb +171 -0
- data/lib/content/item_finder_class_methods.rb +203 -0
- data/lib/content/manager.rb +105 -0
- data/lib/content/sublayout.rb +44 -0
- data/lib/content/template.rb +24 -0
- metadata +38 -2
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'rufus/tokyo'
|
2
|
+
|
3
|
+
module Content
|
4
|
+
module Adapters
|
5
|
+
class CabinetAdapter < Base
|
6
|
+
def initialize(options = {})
|
7
|
+
file = options[:database] || options["database"] || "db/content.tct"
|
8
|
+
mode = options[:mode] || options["mode"] || "create"
|
9
|
+
@connection = Rufus::Tokyo::Table.new(file, mode)
|
10
|
+
end
|
11
|
+
|
12
|
+
def prepare_query(klass, query_options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def run_query(klass, query_options)
|
16
|
+
results = nil
|
17
|
+
ms = Benchmark.ms do
|
18
|
+
query_options[:limit] ||= 1000
|
19
|
+
results = @connection.query { |q|
|
20
|
+
(query_options[:conditions] || {}).each { |cond| q.add_condition cond[0], :eq, cond[1] }
|
21
|
+
(query_options[:order] || []).each {|order| q.order_by order }
|
22
|
+
}.collect {|r| r.symbolize_keys }
|
23
|
+
end
|
24
|
+
log_select(query_options, klass, ms)
|
25
|
+
results
|
26
|
+
end
|
27
|
+
|
28
|
+
def count(klass, query_options)
|
29
|
+
run_query(klass, query_options).length
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_record_by_id(klass, id)
|
33
|
+
record = nil
|
34
|
+
ms = Benchmark.ms do
|
35
|
+
record = @connection[id.to_s].symbolize_keys
|
36
|
+
end
|
37
|
+
log_select({:conditions => {:__id => id, :content_type => klass.name.to_s}}, klass, ms)
|
38
|
+
record
|
39
|
+
end
|
40
|
+
|
41
|
+
def save_record(klass, id, attributes)
|
42
|
+
ms = Benchmark.ms do
|
43
|
+
@connection[id.to_s] = attributes.stringify_keys
|
44
|
+
end
|
45
|
+
log_update(id, attributes, klass, ms)
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
def delete_record_by_id(klass, id)
|
50
|
+
ms = Benchmark.ms do
|
51
|
+
@connection.delete id
|
52
|
+
end
|
53
|
+
log_delete(id, klass, ms)
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
def genuid
|
58
|
+
@connection.genuid
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'tokyo_tyrant'
|
2
|
+
|
3
|
+
module Content
|
4
|
+
module Adapters
|
5
|
+
class TyrantAdapter < Base
|
6
|
+
def initialize(options = {})
|
7
|
+
host = options[:host] || options["host"] || "localhost"
|
8
|
+
port = options[:port] || options["port"] || 1978
|
9
|
+
@connection = TokyoTyrant::Table.new(host, port)
|
10
|
+
@logger = ActiveRecord::Base.logger
|
11
|
+
end
|
12
|
+
|
13
|
+
def prepare_query(klass, query_options)
|
14
|
+
query_options[:limit] ||= 1000
|
15
|
+
query_options[:offset] || -1
|
16
|
+
returning @connection.query do |q|
|
17
|
+
(query_options[:conditions] || {}).each { |key, value| q.condition(key, :streq, value) }
|
18
|
+
(query_options[:order] || []).each { |order| q.order_by(order, :strasc) }
|
19
|
+
q.limit(query_options[:limit], query_options[:offset])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def run_query(klass, query_options)
|
24
|
+
results = nil
|
25
|
+
ms = Benchmark.ms do
|
26
|
+
query = prepare_query klass, query_options
|
27
|
+
results = query.get
|
28
|
+
end
|
29
|
+
log_select(query_options, klass, ms)
|
30
|
+
results
|
31
|
+
end
|
32
|
+
|
33
|
+
def count(klass, query_options)
|
34
|
+
results = nil
|
35
|
+
ms = Benchmark.ms do
|
36
|
+
query = prepare_query klass, query_options
|
37
|
+
results = query.searchcount
|
38
|
+
end
|
39
|
+
log_count(query_options, klass, ms)
|
40
|
+
results
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_record_by_id(klass, id)
|
44
|
+
record = nil
|
45
|
+
ms = Benchmark.ms do
|
46
|
+
record = @connection[id]
|
47
|
+
end
|
48
|
+
log_select({:conditions => {:__id => id, :content_type => klass.name.to_s}}, klass, ms)
|
49
|
+
record
|
50
|
+
end
|
51
|
+
|
52
|
+
def save_record(klass, id, attributes)
|
53
|
+
ms = Benchmark.ms do
|
54
|
+
@connection[id] = attributes
|
55
|
+
end
|
56
|
+
log_update(id, attributes, klass, ms)
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
def delete_record_by_id(klass, id)
|
61
|
+
ms = Benchmark.ms do
|
62
|
+
@connection.delete id
|
63
|
+
end
|
64
|
+
log_delete(id, klass, ms)
|
65
|
+
true
|
66
|
+
end
|
67
|
+
|
68
|
+
def genuid
|
69
|
+
@connection.genuid
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/content/item.rb
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
module Content
|
2
|
+
class Item
|
3
|
+
extend ItemAssociationClassMethods
|
4
|
+
extend ItemFinderClassMethods
|
5
|
+
extend ItemClassMethods
|
6
|
+
|
7
|
+
fields :status, :version, :url, :heading, :summary, :content_type
|
8
|
+
belongs_to :template
|
9
|
+
|
10
|
+
def initialize(attrs = {})
|
11
|
+
@attributes = { :content_type => self.class.name.to_s }
|
12
|
+
@attributes.merge!(attrs.symbolize_keys) unless attrs.nil?
|
13
|
+
@new_record = !@attributes.has_key?(:__id)
|
14
|
+
yield(self) if block_given?
|
15
|
+
end
|
16
|
+
|
17
|
+
def save
|
18
|
+
create_or_update
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_or_update
|
22
|
+
raise ActiveRecord::ReadOnlyRecord if readonly?
|
23
|
+
result = new_record? ? create : update
|
24
|
+
result != false
|
25
|
+
end
|
26
|
+
|
27
|
+
def create
|
28
|
+
self[:__id] = self.class.connection.genuid if self[:__id].nil?
|
29
|
+
if new_record? or changed?
|
30
|
+
saved_attributes = {}
|
31
|
+
self.class.ignored_attributes.each {|k| self["#{k.to_s.singularize}_ids"] = self[k].collect(&:id) if instance_variable_get("@#{k}_loaded".to_sym) }
|
32
|
+
@attributes.each {|k,v| saved_attributes[k] = v.is_a?(String) ? v : v.to_json unless self.class.ignored_attributes.include? k }
|
33
|
+
self.class.connection.save_record(self.class, self.id, saved_attributes)
|
34
|
+
@new_record = false
|
35
|
+
changed_attributes.clear
|
36
|
+
end
|
37
|
+
self
|
38
|
+
end
|
39
|
+
alias update create
|
40
|
+
alias respond_to_without_attributes? respond_to?
|
41
|
+
|
42
|
+
def save!
|
43
|
+
save
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def destroy
|
48
|
+
self.class.connection.delete_record_by_id self.class, id
|
49
|
+
end
|
50
|
+
|
51
|
+
def readonly?
|
52
|
+
false
|
53
|
+
end
|
54
|
+
|
55
|
+
def new_record?
|
56
|
+
@new_record
|
57
|
+
end
|
58
|
+
|
59
|
+
def changed?
|
60
|
+
!changed_attributes.empty?
|
61
|
+
end
|
62
|
+
|
63
|
+
def [](key)
|
64
|
+
read_attribute key
|
65
|
+
end
|
66
|
+
|
67
|
+
def []=(key, value)
|
68
|
+
write_attribute key, value
|
69
|
+
end
|
70
|
+
|
71
|
+
def read_attribute(attr_name)
|
72
|
+
@attributes[attr_name.to_sym]
|
73
|
+
end
|
74
|
+
|
75
|
+
def write_attribute(attr_name, value)
|
76
|
+
sym = attr_name.to_sym
|
77
|
+
if !new_record? and (@attributes[sym].nil? or @attributes[sym] != value)
|
78
|
+
attribute_will_change!(sym)
|
79
|
+
end
|
80
|
+
if value.nil?
|
81
|
+
@attributes.delete sym
|
82
|
+
else
|
83
|
+
@attributes[sym] = value
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def query_attribute(attr_name)
|
88
|
+
value = read_attribute(attr_name.to_sym)
|
89
|
+
if Numeric === value || value !~ /[^0-9]/
|
90
|
+
!value.to_i.zero?
|
91
|
+
else
|
92
|
+
return false if ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
|
93
|
+
!value.blank?
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def update_attributes(attrs)
|
98
|
+
unless attrs.nil?
|
99
|
+
attrs.each do |key, value|
|
100
|
+
self[key.to_sym] = value
|
101
|
+
end
|
102
|
+
end
|
103
|
+
save
|
104
|
+
end
|
105
|
+
|
106
|
+
def method_missing(name, *arguments)
|
107
|
+
if arguments.length == 0
|
108
|
+
self[name] if @attributes.has_key? name
|
109
|
+
elsif arguments.length == 1 and name.to_s.match(/^(.+)=$/)
|
110
|
+
self[$1.to_sym] = arguments.first
|
111
|
+
else
|
112
|
+
super
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def to_param
|
117
|
+
id.to_s
|
118
|
+
end
|
119
|
+
|
120
|
+
def to_yaml
|
121
|
+
@attributes.to_yaml
|
122
|
+
end
|
123
|
+
|
124
|
+
def to_json
|
125
|
+
@attributes.to_json
|
126
|
+
end
|
127
|
+
|
128
|
+
def to_xml(options = {})
|
129
|
+
xml = Builder::XmlMarkup.new(:indent => options[:indent])
|
130
|
+
xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
|
131
|
+
xml.tag!(self.class.name.underscore.gsub('/', '-').dasherize) do |inner_xml|
|
132
|
+
self.class.serialized_attributes.each {|attr_name| inner_xml.tag! attr_name, @attributes[attr_name] unless @attributes[attr_name].nil? }
|
133
|
+
yield(inner_xml) if block_given?
|
134
|
+
end
|
135
|
+
xml.target!
|
136
|
+
end
|
137
|
+
|
138
|
+
def id
|
139
|
+
self[:__id].to_i unless self[:__id].nil?
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
def changed_attributes
|
144
|
+
@changed_attributes ||= {}
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
Content::Item.class_eval do
|
150
|
+
include Content::ItemDirtyMethods
|
151
|
+
include ActiveRecord::Validations
|
152
|
+
include ActiveRecord::Callbacks
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
# RDBQC*
|
157
|
+
# STREQ - for string which is equal to the expression
|
158
|
+
# STRINC - for string which is included in the expression
|
159
|
+
# STRBW - for string which begins with the expression
|
160
|
+
# STREW - for string which ends with the expression
|
161
|
+
# STRAND - for string which includes all tokens in the expression
|
162
|
+
# STROR - for string which includes at least one token in the expression
|
163
|
+
# STROREQ - for string which is equal to at least one token in the expression
|
164
|
+
# STRRX - for string which matches regular expressions of the expression
|
165
|
+
# NUMEQ - for number which is equal to the expression
|
166
|
+
# NUMGT - for number which is greater than the expression
|
167
|
+
# NUMGE - for number which is greater than or equal to the expression
|
168
|
+
# NUMLT - for number which is less than the expression
|
169
|
+
# NUMLE - for number which is less than or equal to the expression
|
170
|
+
# NUMBT - for number which is between two tokens of the expression
|
171
|
+
# NUMOREQ - for number which is equal to at least one token in the expression
|
172
|
+
# FTSPH - for full-text search with the phrase of the expression
|
173
|
+
# FTSAND - for full-text search with all tokens in the expression
|
174
|
+
# FTSOR - for full-text search with at least one token in the expression
|
175
|
+
# FTSEX - for full-text search with the compound expression.
|
176
|
+
# All operations can be flagged by bitwise-or:
|
177
|
+
# NEGATE - for negation
|
178
|
+
# NOIDX - for using no index.
|
@@ -0,0 +1,148 @@
|
|
1
|
+
module Content
|
2
|
+
module ItemAssociationClassMethods
|
3
|
+
def ignored_attributes
|
4
|
+
@ignored_attributes ||= []
|
5
|
+
end
|
6
|
+
|
7
|
+
def serialized_attributes
|
8
|
+
@serialized_attributes ||= []
|
9
|
+
end
|
10
|
+
|
11
|
+
def has_many(name, options = {})
|
12
|
+
raise "name must be plural" unless name.to_s == name.to_s.pluralize
|
13
|
+
name_ids = "#{name.to_s.singularize}_ids".to_sym
|
14
|
+
ignored_attributes << name
|
15
|
+
serialized_attributes << name_ids
|
16
|
+
|
17
|
+
define_method(name_ids) do
|
18
|
+
if instance_variable_get("@#{name}_loaded".to_sym)
|
19
|
+
self[name_ids] = self[name].collect(&:id)
|
20
|
+
self[name] = nil
|
21
|
+
instance_variable_set("@#{name}_loaded".to_sym, false)
|
22
|
+
else
|
23
|
+
if self[name_ids].nil?
|
24
|
+
self[name_ids] = []
|
25
|
+
else
|
26
|
+
self[name_ids] = ActiveSupport::JSON.decode(self[name_ids]) if !self[name_ids].is_a?(Array) and self[name_ids].is_a? String
|
27
|
+
end
|
28
|
+
end
|
29
|
+
self[name_ids]
|
30
|
+
end
|
31
|
+
|
32
|
+
define_method("#{name_ids}=".to_sym) do |val|
|
33
|
+
self[name] = nil
|
34
|
+
instance_variable_set("@#{name}_loaded".to_sym, false)
|
35
|
+
|
36
|
+
if val.is_a? Content::Item
|
37
|
+
self[name_ids] = [val.id.to_i]
|
38
|
+
elsif !val.is_a? Array
|
39
|
+
self[name_ids] = [val.to_i]
|
40
|
+
else
|
41
|
+
self[name_ids] = val
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
define_method(name) do
|
46
|
+
ary = self[name]
|
47
|
+
if ary.nil?
|
48
|
+
self[name] = ary = (self.send(name_ids) || []).collect {|id| self.class.find(id.to_i) }
|
49
|
+
instance_variable_set("@#{name}_loaded".to_sym, true)
|
50
|
+
self[name_ids] = nil
|
51
|
+
end
|
52
|
+
ary
|
53
|
+
end
|
54
|
+
|
55
|
+
define_method("#{name}=".to_sym) do |val|
|
56
|
+
if val.is_a? Array
|
57
|
+
instance_variable_set("@#{name}_loaded".to_sym, true)
|
58
|
+
self[name] = val
|
59
|
+
self[name_ids] = nil
|
60
|
+
elsif val.nil?
|
61
|
+
instance_variable_set("@#{name}_loaded".to_sym, false)
|
62
|
+
self[name] = nil
|
63
|
+
self[name_ids] = nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
define_method("#{name}_loaded".to_sym) do
|
68
|
+
instance_variable_get("@#{name}_loaded".to_sym)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def belongs_to(name, options = {})
|
73
|
+
raise "name must be singular" unless name.to_s == name.to_s.singularize
|
74
|
+
name_id = "#{name}_id".to_sym
|
75
|
+
ignored_attributes << name
|
76
|
+
serialized_attributes << name_id
|
77
|
+
|
78
|
+
define_method(name_id) do
|
79
|
+
self[name_id].to_i
|
80
|
+
end
|
81
|
+
|
82
|
+
define_method("#{name_id}=".to_sym) do |val|
|
83
|
+
if val.is_a? Content::Item
|
84
|
+
self[name] = val
|
85
|
+
self[name_id] = val.id.to_i
|
86
|
+
else
|
87
|
+
self[name_id] = val.to_i
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
define_method(name) do
|
92
|
+
self[name] ||= self.class.find(self[name_id].to_i)
|
93
|
+
end
|
94
|
+
|
95
|
+
define_method("#{name}=".to_sym) do |val|
|
96
|
+
if val.is_a? Content::Item
|
97
|
+
val.save! if val.new_record?
|
98
|
+
self[name] = val
|
99
|
+
self[name_id] = val.id.to_i
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
alias :has_one :belongs_to
|
105
|
+
|
106
|
+
def field(name_or_ary, field_type = :string)
|
107
|
+
names = name_or_ary
|
108
|
+
names = [name_or_ary] unless name_or_ary.is_a? Array
|
109
|
+
names.each do |name|
|
110
|
+
serialized_attributes << name
|
111
|
+
field_klass = field_type.to_s.camelcase.constantize
|
112
|
+
|
113
|
+
define_method(name) do
|
114
|
+
self[name] = ActiveSupport::JSON.decode(self[name]) if !self[name].is_a?(field_klass) and self[name].is_a? String
|
115
|
+
self[name]
|
116
|
+
end
|
117
|
+
|
118
|
+
define_method("#{name}=".to_sym) do |val|
|
119
|
+
self[name] = val
|
120
|
+
end
|
121
|
+
|
122
|
+
define_method("#{name}_changed?".to_sym) do
|
123
|
+
self.changed_attributes.has_key? name
|
124
|
+
end
|
125
|
+
|
126
|
+
define_method("#{name}_change".to_sym) do
|
127
|
+
[self.changed_attributes[name], self[name]] if self.changed_attributes.has_key? name
|
128
|
+
end
|
129
|
+
|
130
|
+
define_method("#{name}_was".to_sym) do
|
131
|
+
if self.changed_attributes.has_key? name
|
132
|
+
self.changed_attributes[name]
|
133
|
+
else
|
134
|
+
self[name]
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def fields(*names)
|
141
|
+
field names
|
142
|
+
end
|
143
|
+
|
144
|
+
def index(name, index_type = :lexical)
|
145
|
+
self.connection.set_index name, index_type
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|