look_up_table 0.1.0.rc8 → 0.1.0.rc9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. data/lib/look_up_table/base.rb +155 -0
  2. data/lib/look_up_table/cache.rb +170 -0
  3. data/lib/look_up_table/method_missing.rb +22 -0
  4. data/lib/look_up_table/no_cache.rb +45 -0
  5. data/lib/look_up_table/version.rb +2 -1
  6. data/lib/look_up_table.rb +7 -268
  7. data/test/dummy/app/models/bar.rb +4 -0
  8. data/test/dummy/app/models/foo.rb +4 -0
  9. data/test/dummy/app/models/foobar.rb +9 -0
  10. data/test/dummy/config/database.yml +9 -14
  11. data/test/dummy/config/environments/test.rb +3 -0
  12. data/test/dummy/db/development.sqlite3 +0 -0
  13. data/test/dummy/db/migrate/20111024130333_create_foobars.rb +2 -1
  14. data/test/dummy/db/migrate/20111024130334_create_foos.rb +11 -0
  15. data/test/dummy/db/migrate/20111024130335_create_bars.rb +11 -0
  16. data/test/dummy/db/schema.rb +13 -1
  17. data/test/dummy/log/development.log +14 -0
  18. data/test/dummy/log/test.log +59 -0
  19. data/test/dummy/tmp/cache/3F4/570/Foobarid%2F0 +0 -0
  20. data/test/dummy/tmp/cache/3F5/580/Foobarid%2F1 +0 -0
  21. data/test/dummy/tmp/cache/45C/750/Foobarbar%2F0 +0 -0
  22. data/test/dummy/tmp/cache/45D/760/Foobarbar%2F1 +0 -0
  23. data/test/dummy/tmp/cache/45E/770/Foobarbar%2F2 +0 -0
  24. data/test/dummy/tmp/cache/45F/780/Foobarbar%2F3 +0 -0
  25. data/test/dummy/tmp/cache/460/790/Foobarbar%2F4 +0 -0
  26. data/test/dummy/tmp/cache/461/7A0/Foobarbar%2F5 +0 -0
  27. data/test/dummy/tmp/cache/462/7B0/Foobarbar%2F6 +0 -0
  28. data/test/dummy/tmp/cache/463/7C0/Foobarbar%2F7 +0 -0
  29. data/test/dummy/tmp/cache/464/7D0/Foobarbar%2F8 +0 -0
  30. data/test/dummy/tmp/cache/465/7E0/Foobarbar%2F9 +0 -0
  31. data/test/dummy/tmp/cache/46B/D60/Foobarfoo%2F0 +0 -0
  32. data/test/dummy/tmp/cache/46C/D70/Foobarfoo%2F1 +0 -0
  33. data/test/dummy/tmp/cache/46D/D80/Foobarfoo%2F2 +0 -0
  34. data/test/dummy/tmp/cache/46E/D90/Foobarfoo%2F3 +0 -0
  35. data/test/dummy/tmp/cache/46F/DA0/Foobarfoo%2F4 +0 -0
  36. data/test/dummy/tmp/cache/470/DB0/Foobarfoo%2F5 +0 -0
  37. data/test/dummy/tmp/cache/471/DC0/Foobarfoo%2F6 +0 -0
  38. data/test/dummy/tmp/cache/472/DD0/Foobarfoo%2F7 +0 -0
  39. data/test/dummy/tmp/cache/473/DE0/Foobarfoo%2F8 +0 -0
  40. data/test/dummy/tmp/cache/474/DF0/Foobarfoo%2F9 +0 -0
  41. data/test/dummy/tmp/cache/48D/030/Foobarbar%2F10 +0 -0
  42. data/test/dummy/tmp/cache/48E/040/Foobarbar%2F11 +0 -0
  43. data/test/dummy/tmp/cache/48E/050/Foobarbar%2F20 +0 -0
  44. data/test/dummy/tmp/cache/48F/050/Foobarbar%2F12 +0 -0
  45. data/test/dummy/tmp/cache/48F/060/Foobarbar%2F21 +0 -0
  46. data/test/dummy/tmp/cache/48F/070/Foobarbar%2F30 +0 -0
  47. data/test/dummy/tmp/cache/490/060/Foobarbar%2F13 +0 -0
  48. data/test/dummy/tmp/cache/490/070/Foobarbar%2F22 +0 -0
  49. data/test/dummy/tmp/cache/490/080/Foobarbar%2F31 +0 -0
  50. data/test/dummy/tmp/cache/490/090/Foobarbar%2F40 +0 -0
  51. data/test/dummy/tmp/cache/491/070/Foobarbar%2F14 +0 -0
  52. data/test/dummy/tmp/cache/491/080/Foobarbar%2F23 +0 -0
  53. data/test/dummy/tmp/cache/491/090/Foobarbar%2F32 +0 -0
  54. data/test/dummy/tmp/cache/491/0A0/Foobarbar%2F41 +0 -0
  55. data/test/dummy/tmp/cache/492/080/Foobarbar%2F15 +0 -0
  56. data/test/dummy/tmp/cache/492/090/Foobarbar%2F24 +0 -0
  57. data/test/dummy/tmp/cache/492/0A0/Foobarbar%2F33 +0 -0
  58. data/test/dummy/tmp/cache/493/090/Foobarbar%2F16 +0 -0
  59. data/test/dummy/tmp/cache/493/0A0/Foobarbar%2F25 +0 -0
  60. data/test/dummy/tmp/cache/493/0B0/Foobarbar%2F34 +0 -0
  61. data/test/dummy/tmp/cache/494/0A0/Foobarbar%2F17 +0 -0
  62. data/test/dummy/tmp/cache/494/0B0/Foobarbar%2F26 +0 -0
  63. data/test/dummy/tmp/cache/494/0C0/Foobarbar%2F35 +0 -0
  64. data/test/dummy/tmp/cache/495/0B0/Foobarbar%2F18 +0 -0
  65. data/test/dummy/tmp/cache/495/0C0/Foobarbar%2F27 +0 -0
  66. data/test/dummy/tmp/cache/495/0D0/Foobarbar%2F36 +0 -0
  67. data/test/dummy/tmp/cache/496/0C0/Foobarbar%2F19 +0 -0
  68. data/test/dummy/tmp/cache/496/0D0/Foobarbar%2F28 +0 -0
  69. data/test/dummy/tmp/cache/496/0E0/Foobarbar%2F37 +0 -0
  70. data/test/dummy/tmp/cache/497/0E0/Foobarbar%2F29 +0 -0
  71. data/test/dummy/tmp/cache/497/0F0/Foobarbar%2F38 +0 -0
  72. data/test/dummy/tmp/cache/498/100/Foobarbar%2F39 +0 -0
  73. data/test/dummy/tmp/cache/49C/730/Foobarfoo%2F10 +0 -0
  74. data/test/dummy/tmp/cache/49D/740/Foobarfoo%2F11 +0 -0
  75. data/test/dummy/tmp/cache/49D/750/Foobarfoo%2F20 +0 -0
  76. data/test/dummy/tmp/cache/49E/750/Foobarfoo%2F12 +0 -0
  77. data/test/dummy/tmp/cache/49E/760/Foobarfoo%2F21 +0 -0
  78. data/test/dummy/tmp/cache/49E/770/Foobarfoo%2F30 +0 -0
  79. data/test/dummy/tmp/cache/49F/760/Foobarfoo%2F13 +0 -0
  80. data/test/dummy/tmp/cache/49F/770/Foobarfoo%2F22 +0 -0
  81. data/test/dummy/tmp/cache/49F/780/Foobarfoo%2F31 +0 -0
  82. data/test/dummy/tmp/cache/49F/790/Foobarfoo%2F40 +0 -0
  83. data/test/dummy/tmp/cache/4A0/770/Foobarfoo%2F14 +0 -0
  84. data/test/dummy/tmp/cache/4A0/780/Foobarfoo%2F23 +0 -0
  85. data/test/dummy/tmp/cache/4A0/790/Foobarfoo%2F32 +0 -0
  86. data/test/dummy/tmp/cache/4A0/7A0/Foobarfoo%2F41 +0 -0
  87. data/test/dummy/tmp/cache/4A1/780/Foobarfoo%2F15 +0 -0
  88. data/test/dummy/tmp/cache/4A1/790/Foobarfoo%2F24 +0 -0
  89. data/test/dummy/tmp/cache/4A1/7A0/Foobarfoo%2F33 +0 -0
  90. data/test/dummy/tmp/cache/4A2/790/Foobarfoo%2F16 +0 -0
  91. data/test/dummy/tmp/cache/4A2/7A0/Foobarfoo%2F25 +0 -0
  92. data/test/dummy/tmp/cache/4A2/7B0/Foobarfoo%2F34 +0 -0
  93. data/test/dummy/tmp/cache/4A3/7A0/Foobarfoo%2F17 +0 -0
  94. data/test/dummy/tmp/cache/4A3/7B0/Foobarfoo%2F26 +0 -0
  95. data/test/dummy/tmp/cache/4A3/7C0/Foobarfoo%2F35 +0 -0
  96. data/test/dummy/tmp/cache/4A4/7B0/Foobarfoo%2F18 +0 -0
  97. data/test/dummy/tmp/cache/4A4/7C0/Foobarfoo%2F27 +0 -0
  98. data/test/dummy/tmp/cache/4A4/7D0/Foobarfoo%2F36 +0 -0
  99. data/test/dummy/tmp/cache/4A5/7C0/Foobarfoo%2F19 +0 -0
  100. data/test/dummy/tmp/cache/4A5/7D0/Foobarfoo%2F28 +0 -0
  101. data/test/dummy/tmp/cache/4A5/7E0/Foobarfoo%2F37 +0 -0
  102. data/test/dummy/tmp/cache/4A6/7E0/Foobarfoo%2F29 +0 -0
  103. data/test/dummy/tmp/cache/4A6/7F0/Foobarfoo%2F38 +0 -0
  104. data/test/dummy/tmp/cache/4A7/800/Foobarfoo%2F39 +0 -0
  105. metadata +191 -7
@@ -0,0 +1,155 @@
1
+ require 'look_up_table'
2
+ require 'look_up_table/base'
3
+ require 'look_up_table/cache'
4
+ require 'look_up_table/method_missing'
5
+ require 'look_up_table/no_cache'
6
+
7
+ module LookUpTable
8
+ module ClassMethods
9
+ # Defining a LookUpTable
10
+ # TODO: Usage
11
+ def look_up_table(lut_key, options = {}, &block)
12
+ options = {
13
+ :batch_size => 10000,
14
+ :prefix => self.name,
15
+ :read_on_init => false,
16
+ :use_cache => true,
17
+ :sql_mode => true,
18
+ :where => nil
19
+ }.merge(options)
20
+
21
+ self.lut_set_proc(lut_key, block)
22
+ self.lut_set_options(lut_key, options)
23
+
24
+ self.lut(lut_key) if options[:read_on_init]
25
+ end
26
+
27
+ # Call to a LookUpTable.
28
+ # * Example:
29
+ # * Tag.lut (Returns all LookUpTables)
30
+ # * Tag.lut :name (Returns LookUpTable given by :name)
31
+ # * Tag.lut(:name, "Berlin") (Returns Value of LookUpTable named :name with :key "Berlin")
32
+ # * Tag.lut({:name => "Berlin", :title => "Berlin"}) TODO: returns hash
33
+ # TODO: TestCases
34
+ def lut(lut_key = nil, lut_item_key = nil)
35
+ @lut ||= {}
36
+
37
+ if lut_key.nil?
38
+ hash = {}
39
+ self.lut_keys.each { |key| hash[key] = self.lut(key) } # CHECK: use .inject?
40
+ return hash
41
+ end
42
+
43
+ if (lut_key.respond_to?(:keys))
44
+ hash = {}
45
+ lut_key.each { |k,v| hash[k.intern] = self.lut(k,v) }
46
+ return hash
47
+ end
48
+
49
+ lut = @lut[lut_key.to_sym] ||= lut_read(lut_key) || {}
50
+ lut_item_key ? lut[lut_item_key] : lut
51
+ end
52
+
53
+ # Reset complete lut if name is omitted, resets given lut otherwise.
54
+ # HACK: not cool do access and define @lut here
55
+ def lut_reset(name = nil)
56
+ @lut ||= {}
57
+
58
+ if name
59
+ @lut[name.to_sym] = nil
60
+ lut_write_cache_item(name, 0, nil) unless lut_options[:skip_memcached] # CHECK: options call w/o name?
61
+ else
62
+ lut_keys.each { |k| lut_reset(k) }
63
+ @lut = {}
64
+ end
65
+ end
66
+
67
+ # Reading LUT and writing cache again
68
+ def lut_reload(name = nil)
69
+ if name
70
+ lut_reset(name)
71
+ lut(name)
72
+ else
73
+ lut_keys.each { |k| lut_reload(k) }
74
+ end
75
+
76
+ lut_keys
77
+ end
78
+
79
+ # Init complete LUT with all keys define.
80
+ # But won't rewrite cache if allready written!
81
+ def lut_init(name = nil)
82
+ if name
83
+ lut(name)
84
+ else
85
+ lut_keys.each { |k| lut_init(k) }
86
+ end
87
+
88
+ lut_keys
89
+ end
90
+
91
+ # Returns keys of LookUpTables defined
92
+ def lut_keys
93
+ lut_options.keys
94
+ end
95
+
96
+ # Usage
97
+ # * Klass.lut_options
98
+ # * Klass.lut_options(:lut_key)
99
+ # * Klass.lut_options(:lut_key, :option_key)
100
+ # * Klass.lut_options(:lut_key => :option_key)
101
+ # * Klass.lut_options(:lut_key => [:option_key, :second_option_key])
102
+ # TODO: test_cases
103
+ # CHECK: some sweeter implementation?
104
+ def lut_options(name = nil)#, option_key = nil)
105
+ @lut_options ||= {}
106
+
107
+ # if name && name.respond_to?(:keys)
108
+ # if name[:lut_key] && name[:lut_key].respond_to?(:values)
109
+ # return name[:lut_key].each.inject([]) do |arr, el|
110
+ # arr << @lut_options[name.intern][el.intern]
111
+ # end
112
+ # elsif name[:lut_key]
113
+ # return @lut_options[name.intern][option_key.intern]
114
+ # else
115
+ # return @lut_options[name.intern]
116
+ # end
117
+ # elsif name
118
+ # (option_key) ? @lut_options[name.intern][option_key.intern] : @lut_options[name.intern]
119
+ # else
120
+ # return @lut_options
121
+ # end
122
+
123
+ (name) ? @lut_options[name.to_sym] : @lut_options
124
+ end
125
+
126
+ protected
127
+ def lut_proc(lut_key = nil)
128
+ @lut_proc ||= {}
129
+
130
+ (lut_key) ? @lut_proc[lut_key.to_sym] : @lut_proc
131
+ end
132
+
133
+ # lut_set_proc(lut_name, block) / lut_proc(lut_name)
134
+ def lut_set_proc(lut_key, block)
135
+ lut_proc[lut_key.to_sym] = block
136
+ end
137
+
138
+ # lut_set_options(lut_name, options) / lut_options(lut_name)
139
+ def lut_set_options(lut_key, options)
140
+ lut_options[lut_key.to_sym] = options
141
+ end
142
+
143
+ # Reads a single lut
144
+ def lut_read(name)
145
+ return nil unless options = lut_options(name)# HACK
146
+
147
+ if options[:use_cache]
148
+ lut_read_from_cache(name)
149
+ else
150
+ lut_read_without_cache(name)
151
+ end
152
+ end
153
+ end
154
+ end
155
+
@@ -0,0 +1,170 @@
1
+ module LookUpTable
2
+ module ClassMethods
3
+ protected
4
+
5
+ # Cache entry for given LUT exists?
6
+ def lut_cache_exists?(name)
7
+ !lut_read_cache_item(name, 0).nil?
8
+ end
9
+
10
+ # Reads a complete lut from cache
11
+ # HACK: this still looks ugly somehow
12
+ def lut_read_from_cache(name)
13
+ lut_write_to_cache(name) unless lut_cache_exists?(name)
14
+
15
+ i = 0
16
+ lut = {}
17
+
18
+ while item = lut_read_cache_item(name, i)
19
+ # FIXME: merge will override existing values
20
+ # lut.merge!(item) if item
21
+ if item
22
+ item.each do |k,v|
23
+ if lut[k]
24
+ v = [v] unless v.respond_to?(:concat)
25
+ lut[k] = [lut[k]] unless lut[k].respond_to?(:concat)
26
+ lut[k].concat(v)
27
+ else
28
+ lut[k] = v
29
+ end
30
+ end
31
+ end
32
+
33
+ i += 1
34
+ end
35
+
36
+ return lut
37
+ end
38
+
39
+ # Write a LookUpTable into Cache
40
+ def lut_write_to_cache(lut_key)
41
+ if lut_options(lut_key)[:sql_mode]
42
+ count = lut_write_to_cache_sql_mode(lut_key)
43
+ else
44
+ count = lut_write_to_cache_no_sql_mode(lut_key)
45
+ end
46
+
47
+ # HACK: Writing a \0 to terminate batch_items
48
+ lut_write_cache_item(lut_key, count, nil)
49
+ end
50
+
51
+ # HACK: somehow long method
52
+ def lut_write_to_cache_sql_mode(lut_key)
53
+ batch_count = 0
54
+
55
+ self.where(lut_options(lut_key)[:where]).find_in_batches(:batch_size => lut_options(lut_key)[:batch_size]) do |items| #FIXME not DRY here
56
+ lut = {}
57
+ block = lut_proc(lut_key)
58
+
59
+ items.each do |item|
60
+ if block
61
+ block.call(lut, item)
62
+ else
63
+ # HACK: doing a merge w/o replacing, just add elements for childs and may transform elements to array
64
+ k = item.send(lut_key)
65
+ v = item.id
66
+ if lut[k]
67
+ v = [v] unless v.respond_to?(:concat)
68
+ lut[k] = [lut[k]] unless lut[k].respond_to?(:concat)
69
+ lut[k].concat(v)
70
+ else
71
+ lut[k] = v
72
+ end
73
+ end
74
+ end
75
+
76
+ batch_count = self.lut_write_cache_item(lut_key, batch_count, lut)
77
+ batch_count += 1
78
+ end
79
+
80
+ batch_count
81
+ end
82
+
83
+ # HACK: somehow long method
84
+ def lut_write_to_cache_no_sql_mode(lut_key)
85
+ lut = {}
86
+ batch_count = 0
87
+
88
+ block = lut_proc(lut_key)
89
+ block.call(lut)
90
+
91
+ keys = lut.keys
92
+
93
+ while
94
+ key_block = keys.slice!(0, lut_options(lut_key)[:batch_size])
95
+ break if key_block.empty?
96
+
97
+ lut_block = {}
98
+ key_block.each{|key| lut_block[key] = lut[key]}
99
+
100
+ batch_count = self.lut_write_cache_item(lut_key, batch_count, lut_block)
101
+ batch_count += 1
102
+ end
103
+
104
+ batch_count
105
+ end
106
+
107
+ # Reads a single item of a LookUpTable from Cache
108
+ def lut_read_cache_item(lut_key, lut_item_key)
109
+ prefix = lut_options(lut_key)[:prefix]
110
+
111
+ Rails.cache.read("#{prefix}#{lut_key}/#{lut_item_key}")
112
+ end
113
+
114
+ # Write a single Item into LookUpTable-Cache
115
+ # TODO: refactor
116
+ def lut_write_cache_item(lut_key, lut_item_count, lut_data)
117
+ prefix = lut_options(lut_key)[:prefix]
118
+ success = Rails.cache.write("#{prefix}#{lut_key}/#{lut_item_count}", lut_data)
119
+
120
+ # Do some magic here and just throw a warning
121
+ # * Divide and conquer on error
122
+ if !success
123
+ warn "WARNING - LookUpTable: Cache.write failed, trying to 'divide and conquer'"
124
+
125
+ if lut_data.respond_to?(:keys) && lut_data.respond_to?(:values)
126
+ # Got a hash
127
+ keys = lut_data.keys
128
+ keys_length = keys.length
129
+
130
+ if keys_length < 2
131
+ # Try to slice down values
132
+ entries = lut_data[keys.first].entries
133
+
134
+ first_entries = { keys.first => entries.slice!(0, entries.length/2) }
135
+ second_entries = { keys.first => entries }
136
+
137
+ lut_item_count = self.lut_write_cache_item(lut_key, lut_item_count, first_entries)
138
+ lut_item_count = self.lut_write_cache_item(lut_key, lut_item_count + 1, second_entries)
139
+
140
+ else
141
+ first_keys = keys.slice!(0, keys.length/2)
142
+ second_keys = keys
143
+ first_hash = {}
144
+ second_hash = {}
145
+
146
+ first_keys.each { |k| first_hash[k] = lut_data[k] }
147
+ second_keys.each { |k| second_hash[k] = lut_data[k] }
148
+
149
+ lut_item_count = self.lut_write_cache_item(lut_key, lut_item_count, first_hash)
150
+ lut_item_count = self.lut_write_cache_item(lut_key, lut_item_count + 1, second_hash)
151
+ end
152
+ elsif lut_data.respond_to?(:entries)
153
+ # Got an Array or a Set
154
+ entries = lut_data.entries
155
+ first_entries = entries.slice!(0, entries.length/2)
156
+ second_entries = entries
157
+
158
+ lut_item_count = self.lut_write_cache_item(lut_key, lut_item_count, first_entries)
159
+ lut_item_count = self.lut_write_cache_item(lut_key, lut_item_count + 1, second_entries)
160
+ else
161
+ # Finally we can't help here
162
+ raise "Cache::write failed - Try lower :batch_size"
163
+ end
164
+ end
165
+
166
+ return lut_item_count
167
+ end
168
+ end
169
+ end
170
+
@@ -0,0 +1,22 @@
1
+ module LookUpTable
2
+ module ClassMethods
3
+ # Delegating <attribute>_lut(args) method calls
4
+ # e.g.: Klass.foo_lut => Klass.lut :foo
5
+ def method_missing(sym, *args, &block)
6
+ method_name = sym.to_s
7
+
8
+ if method_name.end_with?("_lut")
9
+ lut_name = method_name[0..-5]
10
+ self.lut(lut_name, args.first)
11
+ else
12
+ super(sym, *args, &block)
13
+ end
14
+ end
15
+
16
+ # CHECK: what's bool?
17
+ def respond_to?(sym, bool)
18
+ sym.to_s.end_with?("_lut") || super(sym, bool)
19
+ end
20
+ end
21
+ end
22
+
@@ -0,0 +1,45 @@
1
+ module LookUpTable
2
+ module ClassMethods
3
+ protected
4
+
5
+ # Reads a complete from given block or generic version
6
+ # HACK: some duplicated methods from Class.lut_write_to_cache
7
+ def lut_read_without_cache(lut_key)
8
+ if lut_options(lut_key)[:sql_mode]
9
+ return lut_read_without_cache_sql_mode(lut_key)
10
+ else
11
+ return lut_read_without_cache_no_sql_mode(lut_key)
12
+ end
13
+ end
14
+
15
+ # CHECK: somehow long method
16
+ def lut_read_without_cache_sql_mode(lut_key)
17
+ lut = {}
18
+ block = lut_proc(lut_key)
19
+
20
+ self.where(lut_options(lut_key)[:where]).find_in_batches(:batch_size => lut_options(lut_key)[:batch_size]) do |items| #FIXME not DRY here
21
+ items.each do |item|
22
+ if block
23
+ block.call(lut, item)
24
+ else
25
+ lut[item.send(lut_key)] = item.id
26
+ end
27
+ end
28
+ end
29
+
30
+ return lut
31
+ end
32
+
33
+ # CHECK: ugly method_name
34
+ def lut_read_without_cache_no_sql_mode(lut_key)
35
+ lut = {}
36
+
37
+ block = lut_proc(lut_key)
38
+ block.call(lut)
39
+
40
+ return lut
41
+ end
42
+
43
+ end
44
+ end
45
+
@@ -1,3 +1,4 @@
1
1
  module LookUpTable
2
- VERSION = "0.1.0.rc8"
2
+ VERSION = "0.1.0.rc9"
3
3
  end
4
+
data/lib/look_up_table.rb CHANGED
@@ -7,278 +7,17 @@
7
7
  # * use symbols freely
8
8
 
9
9
  # FIXME:
10
- # * Avoid @@class_variables, issues if "subclassed"
11
- module LookUpTable
12
- extend ActiveSupport::Concern
13
-
14
- # Class methods
15
- module ClassMethods
16
- # Defining a LookUpTable
17
- # TODO: Usage
18
- def look_up_table(lut_name, options={}, &block)
19
- options = {
20
- :batch_size => 10000,
21
- :prefix => "",
22
- :read_on_init => false,
23
- :skip_memcached => false,
24
- :sql_mode => true,
25
- :where => nil
26
- }.merge(options)
27
-
28
- self.lut_proc[lut_name.to_sym] = block
29
- self.lut_options[lut_name.to_sym] = options
30
-
31
- self.lut(lut_name) if options[:read_on_init]
32
- end
33
-
34
- # Call to a LookUpTable.
35
- # * Example: Tag.lut(:name, "Berlin")
36
- # * Returns: nil or stored objects
37
- def lut(name, search_by=nil)
38
- @@lut ||= {}
39
- @@lut[name.to_sym] ||= lut_read(name) || {}
40
-
41
- search_by ? @@lut[name.to_sym][search_by] : @@lut[name.to_sym]
42
- end
43
-
44
- # Reset complete lut if name is omitted, resets given lut otherwise.
45
- # HACK
46
- def lut_reset(name=nil)
47
- @@lut ||= {}
48
-
49
- if name
50
- @@lut[name.to_sym] = nil
51
- lut_write_cache_item(name, 0, nil) unless lut_options[:skip_memcached]
52
- else
53
- lut_keys.each { |k| lut_reset(k) }
54
- @@lut = {}
55
- end
56
- end
57
-
58
- # Reloads allready loaded LUTs.
59
- # Will also rewrites cache.
60
- def lut_reload(name = nil)
61
- if name
62
- lut_reset(name)
63
- lut(name)
64
- else
65
- lut_keys.each { |k| lut_reload(k) }
66
- end
67
-
68
- return lut_keys
69
- end
70
-
71
- # Init complete LUT with all keys define.
72
- # But won't rewrite cache if allready written!
73
- def lut_init(name = nil)
74
- if name
75
- lut(name)
76
- else
77
- lut_keys.each { |k| lut_init(k) }
78
- end
79
-
80
- return lut_keys
81
- end
82
-
83
-
84
-
85
- #private
86
- def lut_keys
87
- lut_options.keys
88
- end
89
-
90
- # lut_set_proc(lut_name, block) / lut_proc(lut_name)
91
- def lut_set_proc(name, block)
92
- lut_proc[name.to_sym] = block
93
- end
94
-
95
- def lut_proc(name = nil)
96
- @@lut_proc ||= {}
97
- (name) ? @@lut_proc[name.to_sym] : @@lut_proc
98
- end
99
-
100
- # lut_set_options(lut_name, options) / lut_options(lut_name)
101
- def lut_set_options(name, options)
102
- lut_options[name.to_sym] = options
103
- end
104
-
105
- def lut_options(name = nil)#, option=nil)
106
- @@lut_options ||= {}
107
- (name) ? @@lut_options[name.to_sym] : @@lut_options
108
- end
109
-
110
- # Reads a single lut
111
- # HACK
112
- def lut_read(name)
113
- return nil unless options = lut_options(name)
114
-
115
- if options[:skip_memcached]
116
- return lut_read_without_cache(name)
117
- else
118
- return lut_read_from_cache(name)
119
- end
120
- end
121
-
122
-
123
-
124
- ################
125
- ### NO-CACHE ###
126
- ################
127
-
128
- # Reads a complete from given block or generic version
129
- # HACK: some duplicated methods from Class.lut_write_to_cache
130
- def lut_read_without_cache(name)
131
- if lut_options(name)[:sql_mode]
132
- return lut_read_without_cache_sql_mode(name)
133
- else
134
- return lut_read_without_cache_no_sql_mode(name)
135
- end
136
- end
137
-
138
- # HACK: somehow long method
139
- def lut_read_without_cache_sql_mode(name)
140
- lut = {}
141
- block = lut_proc(name)
142
-
143
- self.where(lut_options(name)[:where]).find_in_batches(:batch_size => lut_options(name)[:batch_size]) do |items| #FIXME not DRY here
144
- items.each do |item|
145
- if block
146
- block.call(lut, item)
147
- else
148
- lut[item.send(name)] = item.id
149
- end
150
- end
151
- end
152
-
153
- return lut
154
- end
10
+ # * Avoid @class_variables, issues if "subclassed"
155
11
 
156
- # HACK: ugly method_name
157
- def lut_read_without_cache_no_sql_mode(name)
158
- lut = {}
159
12
 
160
- block = lut_proc(name)
161
- block.call(lut)
13
+ require 'look_up_table'
162
14
 
163
- return lut
164
- end
165
-
166
-
167
-
168
- #############
169
- ### CACHE ###
170
- #############
171
-
172
- # Cache entry for given LUT exists?
173
- # TODO: just check if given key exists
174
- def lut_cache_exists?(name)
175
- !lut_read_cache_item(name, 0).nil?
176
- end
177
-
178
- # Reads a complete lut from cache
179
- # HACK: this still looks ugly somehow
180
- def lut_read_from_cache(name)
181
- lut_write_to_cache(name) unless lut_cache_exists?(name)
182
-
183
- i = 0
184
- lut = {}
185
-
186
- while item = lut_read_cache_item(name, i)
187
- # HACK: merge will override existing values
188
- lut.merge!(item) if item
189
- i += 1
190
- end
191
-
192
- return lut
193
- end
194
-
195
- # Reads a single item of a LookUpTable from Cache
196
- def lut_read_cache_item(name, item)
197
- prefix = lut_options(name)[:prefix]
198
- Rails.cache.read("#{prefix}#{name}/#{item}")
199
- end
200
-
201
- # Write a LookUpTable into Cache
202
- def lut_write_to_cache(name)
203
- if lut_options(name)[:sql_mode]
204
- count = lut_write_to_cache_sql_mode(name)
205
- else
206
- count = lut_write_to_cache_no_sql_mode(name)
207
- end
208
-
209
- # HACK: Writing a \0 to terminate batch_items
210
- lut_write_cache_item(name, count, nil)
211
- end
212
-
213
- # HACK: somehow long method
214
- def lut_write_to_cache_sql_mode(name)
215
- batch_count = 0
216
-
217
- self.where(lut_options(name)[:where]).find_in_batches(:batch_size => lut_options(name)[:batch_size]) do |items| #FIXME not DRY here
218
- lut = {}
219
- block = lut_proc(name)
220
-
221
- items.each do |item|
222
- if block
223
- block.call(lut, item)
224
- else
225
- lut[item.send(name)] = item.id
226
- end
227
- end
228
-
229
- self.lut_write_cache_item(name, batch_count, lut)
230
- batch_count += 1
231
- end
232
-
233
- batch_count
234
- end
235
-
236
- # HACK: somehow long method
237
- def lut_write_to_cache_no_sql_mode(name)
238
- lut = {}
239
- batch_count = 0
240
-
241
- block = lut_proc(name)
242
- block.call(lut)
243
-
244
- keys = lut.keys
245
-
246
- while
247
- key_block = keys.slice!(0, lut_options(name)[:batch_size])
248
- break if key_block.empty?
249
-
250
- lut_block = {}
251
- key_block.each{|key| lut_block[key] = lut[key]}
252
-
253
- self.lut_write_cache_item(name, batch_count, lut_block)
254
- batch_count += 1
255
- end
256
-
257
- batch_count
258
- end
259
-
260
- # Write a single Item into LookUpTable-Cache
261
- def lut_write_cache_item(name, lut_item, lut_data)
262
- prefix = lut_options(name)[:prefix]
263
- status = Rails.cache.write("#{prefix}#{name}/#{lut_item}", lut_data)
264
- raise "Cache::write failed - Try lower :batch_size" unless status
265
- end
266
-
267
-
268
-
269
- # Delegating <attribute>_lut(args) method calls
270
- # HACK
271
- def method_missing(m, *args, &block)
272
- method_name = m.to_s
273
- if method_name.end_with?("_lut")
274
- lut_name = method_name[0..-5]
275
- self.lut(lut_name, args.first)
276
- else
277
- super(m, *args, &block)
278
- end
279
- end
15
+ module LookUpTable
16
+ extend ActiveSupport::Autoload
17
+ extend ActiveSupport::Concern
280
18
 
281
- end
19
+ require 'look_up_table/base'
20
+ #autoload :Base # CHECK
282
21
  end
283
22
 
284
23
  ActiveRecord::Base.send :include, LookUpTable
@@ -0,0 +1,4 @@
1
+ class Bar < Foobar
2
+ #look_up_table :bar
3
+ end
4
+
@@ -0,0 +1,4 @@
1
+ class Foo < Foobar
2
+ #look_up_table :foo
3
+ end
4
+
@@ -1,4 +1,13 @@
1
1
  class Foobar < ActiveRecord::Base
2
+
3
+ look_up_table :id, :batch_size => 500000
2
4
  look_up_table :foo
5
+ look_up_table :bar, :sql_mode => false do |lut|
6
+ lut[1] = []
7
+ 500000.times { |key| lut[1] << Random.rand }
8
+ lut[1].sort!
9
+ end
10
+ #look_up_table :foobar
11
+
3
12
  end
4
13