piggly 1.2.1 → 2.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.
Files changed (112) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +163 -0
  3. data/Rakefile +29 -15
  4. data/bin/piggly +4 -244
  5. data/lib/piggly.rb +19 -17
  6. data/lib/piggly/command.rb +9 -0
  7. data/lib/piggly/command/base.rb +148 -0
  8. data/lib/piggly/command/report.rb +162 -0
  9. data/lib/piggly/command/test.rb +157 -0
  10. data/lib/piggly/command/trace.rb +90 -0
  11. data/lib/piggly/command/untrace.rb +78 -0
  12. data/lib/piggly/compiler.rb +7 -5
  13. data/lib/piggly/compiler/cache_dir.rb +119 -0
  14. data/lib/piggly/compiler/coverage_report.rb +63 -0
  15. data/lib/piggly/compiler/trace_compiler.rb +105 -0
  16. data/lib/piggly/config.rb +47 -22
  17. data/lib/piggly/dumper.rb +9 -0
  18. data/lib/piggly/dumper/index.rb +121 -0
  19. data/lib/piggly/dumper/qualified_name.rb +36 -0
  20. data/lib/piggly/dumper/qualified_type.rb +81 -0
  21. data/lib/piggly/dumper/reified_procedure.rb +142 -0
  22. data/lib/piggly/dumper/skeleton_procedure.rb +102 -0
  23. data/lib/piggly/installer.rb +84 -42
  24. data/lib/piggly/parser.rb +43 -49
  25. data/lib/piggly/parser/grammar.tt +289 -313
  26. data/lib/piggly/parser/nodes.rb +270 -211
  27. data/lib/piggly/parser/traversal.rb +35 -33
  28. data/lib/piggly/parser/treetop_ruby19_patch.rb +1 -1
  29. data/lib/piggly/profile.rb +81 -60
  30. data/lib/piggly/reporter.rb +5 -18
  31. data/lib/piggly/reporter/base.rb +103 -0
  32. data/lib/piggly/reporter/html_dsl.rb +63 -0
  33. data/lib/piggly/reporter/index.rb +108 -0
  34. data/lib/piggly/reporter/procedure.rb +104 -0
  35. data/lib/piggly/reporter/resources/highlight.js +21 -0
  36. data/lib/piggly/reporter/{piggly.css → resources/piggly.css} +52 -12
  37. data/lib/piggly/reporter/{sortable.js → resources/sortable.js} +0 -0
  38. data/lib/piggly/tags.rb +280 -0
  39. data/lib/piggly/task.rb +191 -40
  40. data/lib/piggly/util.rb +8 -27
  41. data/lib/piggly/util/blankslate.rb +114 -0
  42. data/lib/piggly/util/cacheable.rb +19 -0
  43. data/lib/piggly/util/enumerable.rb +44 -0
  44. data/lib/piggly/util/file.rb +17 -0
  45. data/lib/piggly/util/process_queue.rb +96 -0
  46. data/lib/piggly/util/thunk.rb +39 -0
  47. data/lib/piggly/version.rb +8 -8
  48. data/spec/examples/compiler/cacheable_spec.rb +190 -0
  49. data/spec/examples/compiler/report_spec.rb +25 -0
  50. data/spec/{compiler → examples/compiler}/trace_spec.rb +7 -57
  51. data/spec/examples/config_spec.rb +61 -0
  52. data/spec/examples/dumper/index_spec.rb +197 -0
  53. data/spec/examples/dumper/procedure_spec.rb +116 -0
  54. data/spec/{grammar → examples/grammar}/expression_spec.rb +60 -60
  55. data/spec/{grammar → examples/grammar}/statements/assignment_spec.rb +15 -15
  56. data/spec/examples/grammar/statements/declaration_spec.rb +21 -0
  57. data/spec/{grammar → examples/grammar}/statements/exception_spec.rb +10 -10
  58. data/spec/{grammar → examples/grammar}/statements/if_spec.rb +47 -34
  59. data/spec/{grammar → examples/grammar}/statements/loop_spec.rb +5 -5
  60. data/spec/{grammar → examples/grammar}/statements/sql_spec.rb +11 -11
  61. data/spec/{grammar → examples/grammar}/tokens/comment_spec.rb +11 -11
  62. data/spec/{grammar → examples/grammar}/tokens/datatype_spec.rb +14 -8
  63. data/spec/{grammar → examples/grammar}/tokens/identifier_spec.rb +26 -10
  64. data/spec/{grammar → examples/grammar}/tokens/keyword_spec.rb +5 -5
  65. data/spec/{grammar → examples/grammar}/tokens/label_spec.rb +7 -7
  66. data/spec/{grammar → examples/grammar}/tokens/literal_spec.rb +1 -1
  67. data/spec/examples/grammar/tokens/lval_spec.rb +50 -0
  68. data/spec/{grammar → examples/grammar}/tokens/number_spec.rb +1 -1
  69. data/spec/{grammar → examples/grammar}/tokens/sqlkeywords_spec.rb +1 -1
  70. data/spec/{grammar → examples/grammar}/tokens/string_spec.rb +9 -9
  71. data/spec/{grammar → examples/grammar}/tokens/whitespace_spec.rb +1 -1
  72. data/spec/examples/installer_spec.rb +59 -0
  73. data/spec/examples/parser/nodes_spec.rb +73 -0
  74. data/spec/examples/parser/traversal_spec.rb +14 -0
  75. data/spec/examples/parser_spec.rb +115 -0
  76. data/spec/examples/profile_spec.rb +153 -0
  77. data/spec/{reporter/html_spec.rb → examples/reporter/html/dsl_spec.rb} +0 -0
  78. data/spec/examples/reporter/html/index_spec.rb +0 -0
  79. data/spec/examples/reporter/html_spec.rb +1 -0
  80. data/spec/examples/reporter_spec.rb +0 -0
  81. data/spec/{compiler → examples}/tags_spec.rb +10 -10
  82. data/spec/examples/task_spec.rb +0 -0
  83. data/spec/examples/util/cacheable_spec.rb +41 -0
  84. data/spec/examples/util/enumerable_spec.rb +64 -0
  85. data/spec/examples/util/file_spec.rb +40 -0
  86. data/spec/examples/util/process_queue_spec.rb +16 -0
  87. data/spec/examples/util/thunk_spec.rb +58 -0
  88. data/spec/examples/version_spec.rb +0 -0
  89. data/spec/issues/007_spec.rb +25 -0
  90. data/spec/issues/008_spec.rb +73 -0
  91. data/spec/issues/018_spec.rb +25 -0
  92. data/spec/spec_helper.rb +253 -9
  93. metadata +136 -93
  94. data/README.markdown +0 -116
  95. data/lib/piggly/compiler/cache.rb +0 -151
  96. data/lib/piggly/compiler/pretty.rb +0 -67
  97. data/lib/piggly/compiler/queue.rb +0 -46
  98. data/lib/piggly/compiler/tags.rb +0 -244
  99. data/lib/piggly/compiler/trace.rb +0 -91
  100. data/lib/piggly/filecache.rb +0 -40
  101. data/lib/piggly/parser/parser.rb +0 -11794
  102. data/lib/piggly/reporter/html.rb +0 -207
  103. data/spec/compiler/cache_spec.rb +0 -9
  104. data/spec/compiler/pretty_spec.rb +0 -9
  105. data/spec/compiler/queue_spec.rb +0 -3
  106. data/spec/compiler/rewrite_spec.rb +0 -3
  107. data/spec/config_spec.rb +0 -58
  108. data/spec/filecache_spec.rb +0 -70
  109. data/spec/fixtures/snippets.sql +0 -158
  110. data/spec/grammar/tokens/lval_spec.rb +0 -50
  111. data/spec/parser_spec.rb +0 -8
  112. data/spec/profile_spec.rb +0 -5
metadata CHANGED
@@ -1,121 +1,164 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: piggly
3
- version: !ruby/object:Gem::Version
4
- version: 1.2.1
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.0
5
5
  platform: ruby
6
- authors:
7
- - Kyle Putnam
6
+ authors:
7
+ - Kvle Putnam
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
-
12
- date: 2010-04-22 00:00:00 +00:00
13
- default_executable:
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
11
+ date: 2016-05-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
16
14
  name: treetop
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.4.14
17
20
  type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: "0"
24
- version:
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.4.14
27
+ - !ruby/object:Gem::Dependency
28
+ name: pg
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.18.4
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.18.4
25
41
  description: PostgreSQL PL/pgSQL stored procedure code coverage
26
- email: putnam.kyle@gmail.com
27
- executables:
42
+ email: putnam.kvle@gmail.com
43
+ executables:
28
44
  - piggly
29
45
  extensions: []
30
-
31
46
  extra_rdoc_files: []
32
-
33
- files:
34
- - README.markdown
47
+ files:
48
+ - README.md
35
49
  - Rakefile
36
- - spec/profile_spec.rb
37
- - spec/grammar/expression_spec.rb
38
- - spec/grammar/tokens/datatype_spec.rb
39
- - spec/grammar/tokens/identifier_spec.rb
40
- - spec/grammar/tokens/label_spec.rb
41
- - spec/grammar/tokens/lval_spec.rb
42
- - spec/grammar/tokens/keyword_spec.rb
43
- - spec/grammar/tokens/sqlkeywords_spec.rb
44
- - spec/grammar/tokens/whitespace_spec.rb
45
- - spec/grammar/tokens/number_spec.rb
46
- - spec/grammar/tokens/string_spec.rb
47
- - spec/grammar/tokens/literal_spec.rb
48
- - spec/grammar/tokens/comment_spec.rb
49
- - spec/grammar/statements/assignment_spec.rb
50
- - spec/grammar/statements/loop_spec.rb
51
- - spec/grammar/statements/if_spec.rb
52
- - spec/grammar/statements/sql_spec.rb
53
- - spec/grammar/statements/exception_spec.rb
54
- - spec/parser_spec.rb
55
- - spec/fixtures/snippets.sql
56
- - spec/filecache_spec.rb
57
- - spec/reporter/html_spec.rb
58
- - spec/config_spec.rb
59
- - spec/compiler/tags_spec.rb
60
- - spec/compiler/queue_spec.rb
61
- - spec/compiler/cache_spec.rb
62
- - spec/compiler/rewrite_spec.rb
63
- - spec/compiler/trace_spec.rb
64
- - spec/compiler/pretty_spec.rb
65
- - spec/spec_suite.rb
66
- - spec/spec_helper.rb
50
+ - bin/piggly
67
51
  - lib/piggly.rb
68
- - lib/piggly/util.rb
69
- - lib/piggly/version.rb
70
- - lib/piggly/parser/treetop_ruby19_patch.rb
71
- - lib/piggly/parser/traversal.rb
72
- - lib/piggly/parser/grammar.tt
73
- - lib/piggly/parser/nodes.rb
74
- - lib/piggly/parser/parser.rb
75
- - lib/piggly/reporter.rb
52
+ - lib/piggly/command.rb
53
+ - lib/piggly/command/base.rb
54
+ - lib/piggly/command/report.rb
55
+ - lib/piggly/command/test.rb
56
+ - lib/piggly/command/trace.rb
57
+ - lib/piggly/command/untrace.rb
76
58
  - lib/piggly/compiler.rb
77
- - lib/piggly/reporter/html.rb
78
- - lib/piggly/reporter/piggly.css
79
- - lib/piggly/reporter/sortable.js
80
- - lib/piggly/compiler/pretty.rb
81
- - lib/piggly/compiler/trace.rb
82
- - lib/piggly/compiler/cache.rb
83
- - lib/piggly/compiler/tags.rb
84
- - lib/piggly/compiler/queue.rb
85
- - lib/piggly/filecache.rb
86
- - lib/piggly/task.rb
59
+ - lib/piggly/compiler/cache_dir.rb
60
+ - lib/piggly/compiler/coverage_report.rb
61
+ - lib/piggly/compiler/trace_compiler.rb
87
62
  - lib/piggly/config.rb
88
- - lib/piggly/parser.rb
63
+ - lib/piggly/dumper.rb
64
+ - lib/piggly/dumper/index.rb
65
+ - lib/piggly/dumper/qualified_name.rb
66
+ - lib/piggly/dumper/qualified_type.rb
67
+ - lib/piggly/dumper/reified_procedure.rb
68
+ - lib/piggly/dumper/skeleton_procedure.rb
89
69
  - lib/piggly/installer.rb
70
+ - lib/piggly/parser.rb
71
+ - lib/piggly/parser/grammar.tt
72
+ - lib/piggly/parser/nodes.rb
73
+ - lib/piggly/parser/traversal.rb
74
+ - lib/piggly/parser/treetop_ruby19_patch.rb
90
75
  - lib/piggly/profile.rb
91
- - bin/piggly
92
- has_rdoc: true
76
+ - lib/piggly/reporter.rb
77
+ - lib/piggly/reporter/base.rb
78
+ - lib/piggly/reporter/html_dsl.rb
79
+ - lib/piggly/reporter/index.rb
80
+ - lib/piggly/reporter/procedure.rb
81
+ - lib/piggly/reporter/resources/highlight.js
82
+ - lib/piggly/reporter/resources/piggly.css
83
+ - lib/piggly/reporter/resources/sortable.js
84
+ - lib/piggly/tags.rb
85
+ - lib/piggly/task.rb
86
+ - lib/piggly/util.rb
87
+ - lib/piggly/util/blankslate.rb
88
+ - lib/piggly/util/cacheable.rb
89
+ - lib/piggly/util/enumerable.rb
90
+ - lib/piggly/util/file.rb
91
+ - lib/piggly/util/process_queue.rb
92
+ - lib/piggly/util/thunk.rb
93
+ - lib/piggly/version.rb
94
+ - spec/examples/compiler/cacheable_spec.rb
95
+ - spec/examples/compiler/report_spec.rb
96
+ - spec/examples/compiler/trace_spec.rb
97
+ - spec/examples/config_spec.rb
98
+ - spec/examples/dumper/index_spec.rb
99
+ - spec/examples/dumper/procedure_spec.rb
100
+ - spec/examples/grammar/expression_spec.rb
101
+ - spec/examples/grammar/statements/assignment_spec.rb
102
+ - spec/examples/grammar/statements/declaration_spec.rb
103
+ - spec/examples/grammar/statements/exception_spec.rb
104
+ - spec/examples/grammar/statements/if_spec.rb
105
+ - spec/examples/grammar/statements/loop_spec.rb
106
+ - spec/examples/grammar/statements/sql_spec.rb
107
+ - spec/examples/grammar/tokens/comment_spec.rb
108
+ - spec/examples/grammar/tokens/datatype_spec.rb
109
+ - spec/examples/grammar/tokens/identifier_spec.rb
110
+ - spec/examples/grammar/tokens/keyword_spec.rb
111
+ - spec/examples/grammar/tokens/label_spec.rb
112
+ - spec/examples/grammar/tokens/literal_spec.rb
113
+ - spec/examples/grammar/tokens/lval_spec.rb
114
+ - spec/examples/grammar/tokens/number_spec.rb
115
+ - spec/examples/grammar/tokens/sqlkeywords_spec.rb
116
+ - spec/examples/grammar/tokens/string_spec.rb
117
+ - spec/examples/grammar/tokens/whitespace_spec.rb
118
+ - spec/examples/installer_spec.rb
119
+ - spec/examples/parser/nodes_spec.rb
120
+ - spec/examples/parser/traversal_spec.rb
121
+ - spec/examples/parser_spec.rb
122
+ - spec/examples/profile_spec.rb
123
+ - spec/examples/reporter/html/dsl_spec.rb
124
+ - spec/examples/reporter/html/index_spec.rb
125
+ - spec/examples/reporter/html_spec.rb
126
+ - spec/examples/reporter_spec.rb
127
+ - spec/examples/tags_spec.rb
128
+ - spec/examples/task_spec.rb
129
+ - spec/examples/util/cacheable_spec.rb
130
+ - spec/examples/util/enumerable_spec.rb
131
+ - spec/examples/util/file_spec.rb
132
+ - spec/examples/util/process_queue_spec.rb
133
+ - spec/examples/util/thunk_spec.rb
134
+ - spec/examples/version_spec.rb
135
+ - spec/issues/007_spec.rb
136
+ - spec/issues/008_spec.rb
137
+ - spec/issues/018_spec.rb
138
+ - spec/spec_helper.rb
139
+ - spec/spec_suite.rb
93
140
  homepage: http://github.com/kputnam/piggly
94
- licenses: []
95
-
141
+ licenses:
142
+ - BSD-2-Clause
143
+ metadata: {}
96
144
  post_install_message:
97
145
  rdoc_options: []
98
-
99
- require_paths:
146
+ require_paths:
100
147
  - lib
101
- required_ruby_version: !ruby/object:Gem::Requirement
102
- requirements:
148
+ required_ruby_version: !ruby/object:Gem::Requirement
149
+ requirements:
103
150
  - - ">="
104
- - !ruby/object:Gem::Version
105
- version: "0"
106
- version:
107
- required_rubygems_version: !ruby/object:Gem::Requirement
108
- requirements:
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ required_rubygems_version: !ruby/object:Gem::Requirement
154
+ requirements:
109
155
  - - ">="
110
- - !ruby/object:Gem::Version
111
- version: "0"
112
- version:
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
113
158
  requirements: []
114
-
115
159
  rubyforge_project:
116
- rubygems_version: 1.3.5
160
+ rubygems_version: 2.5.1
117
161
  signing_key:
118
- specification_version: 3
162
+ specification_version: 4
119
163
  summary: PL/pgSQL code coverage tool
120
164
  test_files: []
121
-
@@ -1,116 +0,0 @@
1
- # Piggly - PostgreSQL PL/pgSQL stored-procedure code coverage for Ruby
2
-
3
- ## Purpose
4
-
5
- PL/pgSQL doesn't have much in the way of developer tools, and writing automated tests for
6
- stored procedures can go much better when you know what you haven't tested. Code coverage
7
- allows you to see which parts of your code haven't been executed.
8
-
9
- ## What's Piggly?
10
-
11
- Piggly is a tool written in Ruby to track code coverage of PostgreSQL's PL/pgSQL stored
12
- procedures. It reports on code coverage to help you identify untested parts of your code. You
13
- write tests in Ruby against your stored procedures and run them with piggly.
14
-
15
- ## How Does It Work?
16
-
17
- Piggly tracks the execution of PostgreSQL's PL/pgSQL stored procedures by recompiling
18
- the stored procedure with instrumentation code that uses RAISE WARNING to notify the
19
- client of an execution event (e.g., a branch condition evaluating to true or false). It records
20
- these events and generates prettified source code that is annotated with coverage details.
21
-
22
- ## Features
23
-
24
- * Branch, block, and loop coverage analysis
25
- * Instrumenting source-to-source compiler
26
- * Low test execution overhead
27
- * Reduced compilation times by use of disk caching
28
- * Readable and easily-navigable reports (see example/piggly/reports/index.html)
29
- * Possible to aggregate coverage across multiple runs
30
- * Test::Unit and RSpec compatible
31
-
32
- ## Limitations
33
-
34
- * Cannot parse aggregate definitions (but helper functions are fine)
35
- * Cannot parse nested dollar-quoted strings, eg $A$ ... $B$ ... $B$ ... $A$
36
- * Report generation is resource intensive and slow
37
- * SQL statements are not instrumented, so their branches (COALESCE, WHERE-clauses, etc) aren't tracked
38
- * Not all PL/pgSQL grammar is currently supported, but this is easily addressed
39
-
40
- ## Requirements
41
-
42
- * [Treetop] [2]
43
- * Stored procedures stored on the filesystem, defined as `CREATE OR REPLACE FUNCTION`
44
- * The [ruby-pg driver] [3], and for the time being, ActiveRecord (some workaround should be possible)
45
-
46
- ## How to Install
47
-
48
- $ gem install piggly
49
-
50
- ## Usage
51
-
52
- Assume your stored procedures are in proc/, and the tests that should be exercising your
53
- stored procedures are in spec/.
54
-
55
- $ cd piggly/example/
56
- $ ../bin/piggly -s 'proc/*.sql' 'spec/**/*_spec.rb'
57
- Loading 1 test files
58
- > Completed in 0.30 seconds
59
- Compiling 1 files
60
- Compiling /home/kputnam/wd/piggly/example/proc/iterate.sql
61
- > Completed in 0.09 seconds
62
- Installing 1 proc files
63
- > Completed in 0.02 seconds
64
- Clearing previous run's profile
65
- > Completed in 0.00 seconds
66
- ...........
67
-
68
- Finished in 0.025061 seconds
69
-
70
- 11 examples, 0 failures
71
- Storing coverage profile
72
- > Completed in 0.00 seconds
73
- Removing trace code
74
- > Completed in 0.00 seconds
75
- Creating index
76
- > Completed in 0.00 seconds
77
- Creating reports
78
- > Completed in 0.02 seconds
79
- > Completed in 0.65 seconds
80
-
81
- $ ls -alh piggly/reports/index.html
82
- -rw-r--r-- 1 kputnam kputnam 1.3K 2010-04-19 14:25 piggly/reports/index.html
83
-
84
- Note the compilation can be slow on the first run, but on subsequent runs it shouldn't need
85
- to compile everything again. If a file is added or changed (based on mtime), it will be recompiled.
86
-
87
- Piggly can also be run from Rake, with a task like:
88
- require 'piggly/task'
89
-
90
- namespace :spec do
91
- Piggly::Task.new(:piggly => 'db:test:prepare') do |t|
92
- t.libs.push 'spec'
93
-
94
- t.test_files = FileList['spec/**/*_spec.rb']
95
- t.proc_files = FileList['proc/*.sql']
96
-
97
- # this can be used if piggly is frozen in a Rails application
98
- t.libs.concat Dir['vendor/gems/*/lib/'].sort.reverse
99
- t.piggly_path = Dir['vendor/gems/piggly-*/bin/piggly'].sort.last
100
- end
101
- end
102
-
103
- $ rake spec:piggly
104
-
105
- ## Bugs & Issues
106
-
107
- Please report any issues on the [github tracker] [4]
108
-
109
- ## Author
110
-
111
- * Kyle Putnam <putnam.kyle@gmail.com>
112
-
113
- [1]: http://github.com/relevance/rcov/
114
- [2]: http://github.com/nathansobo/treetop
115
- [3]: http://bitbucket.org/ged/ruby-pg/
116
- [4]: http://github.com/kputnam/piggly/issues
@@ -1,151 +0,0 @@
1
- module Piggly
2
- module CompilerCache
3
- def self.included(subclass)
4
- subclass.extend(ClassMethods)
5
- end
6
-
7
- #
8
- # Each cache unit (any group of data that should be expired and created
9
- # together) can be broken apart, to prevent unmarshaling a huge block of
10
- # data all at once.
11
- #
12
- # The interface works like a Hash, so the compile method should return a
13
- # hash of objects. Each object is writen to a different file (named by the
14
- # hash key) within the same directory. String objects are (usually) read
15
- # and written directly to disk, while all other objects are (un-)Marshal'd
16
- #
17
- # Cache invalidation is done by comparing mtime timestamps on the cached
18
- # object's file to all the "source" files (ruby libs, input files, etc)
19
- # required to regenerate the data.
20
- #
21
- class FileCache
22
- # Non-printable ASCII char indicates data should be Marshal'd
23
- HINT = /[\000-\010\016-\037\177-\300]/
24
-
25
- class << self
26
- def lookup(cachedir, data={})
27
- store[cachedir] ||= new(cachedir, data)
28
- end
29
-
30
- def dump(cachedir, hash)
31
- FileUtils.mkdir(cachedir) unless File.exists?(cachedir)
32
- FileUtils.touch(cachedir)
33
-
34
- for key, data in hash
35
- File.open(File.join(cachedir, key.to_s), 'wb') do |f|
36
- if data.is_a?(String) and data[0,2] !~ HINT
37
- # even Strings will be Marshal'd if the first two bytes contain non-ASCII
38
- f.write data
39
- else
40
- Marshal.dump(data, f)
41
- end
42
- end
43
- end
44
-
45
- return hash
46
- end
47
-
48
- def load(cachedir, key)
49
- File.open(File.join(cachedir, key.to_s)) do |io|
50
- # detect Marshal'd data
51
- if io.read(2) !~ HINT
52
- io.rewind
53
- io.read
54
- else
55
- io.rewind
56
- Marshal.load(io)
57
- end
58
- end
59
- end
60
-
61
- # Creates cachedir (if missing) and destroys its contents
62
- def clean(cachedir)
63
- FileUtils.mkdir(cachedir) unless File.exists?(cachedir)
64
- FileUtils.touch(cachedir)
65
- FileUtils.rm(Dir["#{cachedir}/*"])
66
- end
67
-
68
- def store
69
- @store ||= {}
70
- end
71
- end
72
-
73
- # Destroys any existing cached data and write the given data to disk if data is not empty
74
- def initialize(cachedir, data={})
75
- @dir = cachedir
76
- @data = {}
77
- replace(data) unless data.empty?
78
- end
79
-
80
- # Load +key+ from file system if needed
81
- def [](key)
82
- @data[key.to_s] ||= self.class.load(@dir, key)
83
- end
84
-
85
- # Writes through to file system
86
- def []=(key, value)
87
- @data[key.to_s] = value
88
- self.class.dump(@dir, key.to_s => value)
89
- end
90
-
91
- # Writes through to file system
92
- def update(hash)
93
- hash.each do |key,value|
94
- self[key] = value
95
- end
96
- end
97
-
98
- def keys
99
- Dir[@dir + '/*'].map{|e| File.basename(e) } | @data.keys
100
- end
101
-
102
- private
103
-
104
- # Clears entire cache and replaces contents
105
- def replace(data)
106
- self.class.clean(@dir)
107
- self.class.dump(@dir, data)
108
-
109
- for key, value in data
110
- # stringify keys
111
- @data[key.to_s] = data
112
- end
113
- end
114
- end
115
-
116
- #
117
- # Base class should define self.compiler_path and self.compile(tree, ...)
118
- #
119
- module ClassMethods
120
- def cache_sources
121
- [compiler_path, Parser.grammar_path, Parser.parser_path, Parser.nodes_path]
122
- end
123
-
124
- def stale?(source)
125
- File.stale?(cache_path(source), source, *cache_sources)
126
- end
127
-
128
- # returns FileCache instance
129
- def cache(source, args={}, &block)
130
- Parser.parser # load libraries
131
- cachedir = cache_path(source)
132
-
133
- if stale?(source)
134
- begin
135
- tree = Parser.cache(source)
136
- data = compile(tree, args.update(:path => source), &block)
137
-
138
- # replaces old cached data with new data
139
- FileCache.lookup(cachedir, data)
140
- rescue Piggly::Parser::Failure
141
- FileCache.clean(cachedir)
142
- raise
143
- end
144
- else
145
- FileCache.lookup(cachedir)
146
- end
147
- end
148
- end
149
-
150
- end
151
- end