snippr 0.13.2 → 0.14.1

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.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YTAxMWYzNWFmOWYyMTc1OTM5MjAwYzM1Nzg1ZjQwNTIzZDNjNzU2Yg==
5
+ data.tar.gz: !binary |-
6
+ MzVkNWE2NjM5NzMyYjI2MWVhZjFmYjUzZDg1YjQ4Y2VkN2Q2NzJiYw==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ NGI2MGNmYjY2MGQzZmZkOGRhYTFiNDg0NjFmODRhZDMyMjQ4YzI3OWY4NWMx
10
+ NDI4YjE0YTFjMTkzZDZlNmFiYjk3NTIwNGI4Y2ZmMWUyMDA2YTc3NTU2ODYz
11
+ ODI0Mzg3YjU1NTZlN2JkOGIzNDc3NGMzZjg5MTBiYTc4OGRmZDc=
12
+ data.tar.gz: !binary |-
13
+ MDE4OTgyYzQ5MDAyOWUyMWU3MTU5YTE3MjliMzRmZWU0N2Y5ZTgyNGVlMjRl
14
+ Yjc3YmQzMmRiYjM5OGU4YWZiY2Q1Nzg1YTE0NzBiNDgxNDZmOWM3ZDI4ZmMz
15
+ NWNkMjlkM2Y5ZDViMmIyMTdhOWUxNjJhMjVjNzQ5NWVlYjlmODM=
data/README.md CHANGED
@@ -74,6 +74,22 @@ will yield:
74
74
 
75
75
  <p>Snippr says: HELLO SNIPPR</p>
76
76
 
77
+ The last parameter can also be passed in in 'block' form:
78
+
79
+ {a_variable.doitagain()}
80
+ SNIPPR
81
+ {/a_variable.doitagain}
82
+
83
+ Notice that you have to leave that last parameter out of the signature of the snippr call:
84
+
85
+ {two_parameters.signature("ONE","TWO")}
86
+
87
+ is equivalent to
88
+
89
+ {two_parameters.signature("ONE")}
90
+ TWO
91
+ {/two_parameters.signature}
92
+
77
93
  ### Meta Infos
78
94
 
79
95
  A snippet can not only hold content but also meta infos for this snippet.
@@ -15,6 +15,7 @@ require 'snippr/view_helper'
15
15
  require 'snippr/normalizer/camelizer'
16
16
  require 'snippr/normalizer/de_rester'
17
17
 
18
+ require 'snippr/processor/block'
18
19
  require 'snippr/processor/dynamics'
19
20
  require 'snippr/processor/functions'
20
21
  require 'snippr/processor/links'
@@ -24,6 +25,7 @@ Snippr::Normalizer.normalizers << Snippr::Normalizer::Camelizer.new
24
25
  # don't use DeRester this for all apps, but configure it as needed
25
26
  # Snippr::Normalizer.normalizers << Snippr::Normalizer::DeRester.new
26
27
 
28
+ Snippr::Processor.processors << Snippr::Processor::Block.new
27
29
  Snippr::Processor.processors << Snippr::Processor::Functions.new
28
30
  Snippr::Processor.processors << Snippr::Processor::Dynamics.new
29
31
  Snippr::Processor.processors << Snippr::Processor::Links.new
@@ -1,6 +1,3 @@
1
- # = Snippr::Normalizer::Camelizer
2
- #
3
- # When given a symbol, normalizes it to a lower camelized string, otherwise just returns the given string.
4
1
  module Snippr
5
2
 
6
3
  module Normalizer
@@ -35,7 +35,7 @@ module Snippr
35
35
  def self.list(*names)
36
36
  dir = path_from_name normalize_name *names
37
37
  Dir["#{dir}/*#{I18n.locale}.#{Snip::FILE_EXTENSION}"].map do |snip|
38
- snip.gsub(/^.*\/([^\.]+)?\.#{Snip::FILE_EXTENSION}$/, '\1').gsub(/_.*$/, '').underscore
38
+ snip.force_encoding("UTF-8").gsub(/^.*\/([^\.]+)?\.#{Snip::FILE_EXTENSION}$/, '\1').gsub(/_.*$/, '').underscore
39
39
  end.sort.map(&:to_sym)
40
40
  end
41
41
 
@@ -0,0 +1,31 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Snippr
3
+
4
+ module Processor
5
+
6
+ class Block
7
+
8
+ def process(content, opts = {})
9
+ opts.inject(content) do |c, pv|
10
+ placeholder, value = pv
11
+ c.gsub(/\{(.*)\((.*)\)\}(.*)\{\/\1\}/m) do |match|
12
+ # match[0] = {a.b("1","2")} INNEN {/a.b}
13
+ # match[1] = a.b
14
+ # match[2] = "1","2"
15
+ # match[3] = INNEN
16
+ message = $1
17
+ signature = $2
18
+ block_quoted = $3.gsub("\"","\"").strip
19
+ new_signature = []
20
+ new_signature << signature unless signature.empty?
21
+ new_signature << block_quoted unless block_quoted.empty?
22
+ "{#{message}(#{new_signature.map { |e| "\"#{e}\""}.join(',')})}"
23
+ end
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  # = Snippr::Processor::Dynamics
2
3
  #
3
4
  # Replaces {placeholder} placeholders in the content with values taken from the given opts.
@@ -9,6 +9,7 @@ module Snippr
9
9
  Snippr.i18n = app.config.snippr.i18n
10
10
  Snippr.path = app.config.snippr.path
11
11
  Snippr::Normalizer.add app.config.snippr.normalizers if app.config.snippr.normalizers
12
+ Snippr.adjust_urls_except += Array(app.config.snippr.adjust_urls_except) if app.config.snippr.adjust_urls_except
12
13
  end
13
14
 
14
15
  end
@@ -1,13 +1,14 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  Gem::Specification.new do |s|
3
3
  s.name = "snippr"
4
- s.version = "0.13.2"
4
+ s.version = "0.14.1"
5
5
  s.date = Time.now
6
6
  s.platform = Gem::Platform::RUBY
7
7
  s.authors = ["Daniel Harrington", "Thomas Jachmann"]
8
8
  s.email = ["me@rubiii.com", "self@thomasjachmann.com"]
9
9
  s.homepage = "http://github.com/blaulabs/snippr"
10
10
  s.summary = %q{File based content management}
11
+ s.license = "MIT"
11
12
  s.description = %q{This gem provides ways to access file based cms resources from a rails app.}
12
13
 
13
14
  s.rubyforge_project = "snippr"
@@ -17,11 +18,12 @@ Gem::Specification.new do |s|
17
18
 
18
19
  s.add_development_dependency "ci_reporter", "~> 1.6.5"
19
20
  s.add_development_dependency "rspec", "~> 2.8.0"
20
- s.add_development_dependency "mocha", "0.11.4"
21
- s.add_development_dependency "rake", "~> 0.9.2"
21
+ s.add_development_dependency "mocha", "= 0.11.4"
22
+ s.add_development_dependency "rake", "~> 0.9.0"
23
+ s.add_development_dependency "debugger"
22
24
 
23
- s.files = `git ls-files`.split("\n")
24
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
25
+ s.files = `git ls-files`.split("\n").reject{ |a| a.start_with?("\"") }
26
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n").reject{ |a| a.start_with?("\"") }
25
27
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
26
28
  s.require_paths = ["lib"]
27
29
  end
@@ -0,0 +1,4 @@
1
+ {view.with_block("PARAM")}
2
+ IN BLOCK-
3
+ LINE "TWO"
4
+ {/view.with_block}
@@ -65,6 +65,10 @@ describe Snippr::Path do
65
65
  subject.list(:doesnotexist).should == []
66
66
  end
67
67
 
68
+ it "can cope with umlauts in the name" do
69
+ subject.list(:mixed_encoding).first.encoding.to_s.should == "UTF-8"
70
+ end
71
+
68
72
  end
69
73
 
70
74
  context "with I18n" do
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  require "spec_helper"
2
3
 
3
4
  describe Snippr::Processor::Dynamics do
@@ -8,7 +9,7 @@ describe Snippr::Processor::Dynamics do
8
9
  def method3(param1, param2); "METHOD WITH #{param1} AND #{param2}"; end
9
10
  end
10
11
 
11
- it "should replace placeholders with dynamic values" do
12
+ it "replaces placeholders with dynamic values" do
12
13
  today = Date.today
13
14
  subject.process('Your topup of {topup_amount} at {date_today} was successful.', {
14
15
  :topup_amount => "15,00 &euro;",
@@ -26,22 +27,22 @@ describe Snippr::Processor::Dynamics do
26
27
  subject.process(tpl, :var => Klass.new).should == "An instance METHOD WITH PART1 SPACE PART2"
27
28
  end
28
29
 
29
- it "should allow calling methods on placeholders" do
30
+ it "allows calling methods on placeholders" do
30
31
  tpl = "An instance {var.method()}"
31
32
  subject.process(tpl, :var => Klass.new).should == "An instance METHOD"
32
33
  end
33
34
 
34
- it "should allow calling methods with parameters on placeholders" do
35
+ it "allows calling methods with parameters on placeholders" do
35
36
  tpl = 'An instance {var.method2("PARAMETER")}'
36
37
  subject.process(tpl, :var => Klass.new).should == "An instance METHOD WITH PARAMETER"
37
38
  end
38
39
 
39
- it "should allow calling methods with multiple parameters on placeholders" do
40
+ it "allows calling methods with multiple parameters on placeholders" do
40
41
  tpl = 'An instance {var.method3("PARAMETER1","PARAMETER2")}'
41
42
  subject.process(tpl, :var => Klass.new).should == "An instance METHOD WITH PARAMETER1 AND PARAMETER2"
42
43
  end
43
44
 
44
- it "should keep the {snip} if calling a method but the method is not defined" do
45
+ it "keeps the {snip} if calling a method but the method is not defined" do
45
46
  tpl = "An instance {var.method_not_exist()}"
46
47
  subject.process(tpl, :var => Klass.new).should == tpl
47
48
  end
@@ -1,27 +1,29 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  require "spec_helper"
2
3
 
3
4
  describe Snippr::Processor do
4
5
 
5
6
  describe ".processors" do
6
7
 
7
- it "should be an array" do
8
+ it "is an array" do
8
9
  subject.processors.should be_an(Array)
9
10
  end
10
11
 
11
- it "should have a set of default processors" do
12
+ it "has a set of default processors" do
12
13
  processors = subject.processors
13
- processors.size.should == 4
14
- processors[0].should be_a(Snippr::Processor::Functions)
15
- processors[1].should be_a(Snippr::Processor::Dynamics)
16
- processors[2].should be_a(Snippr::Processor::Links)
17
- processors[3].should be_a(Snippr::Processor::Wikilinks)
14
+ processors.size.should == 5
15
+ processors[0].should be_a(Snippr::Processor::Block)
16
+ processors[1].should be_a(Snippr::Processor::Functions)
17
+ processors[2].should be_a(Snippr::Processor::Dynamics)
18
+ processors[3].should be_a(Snippr::Processor::Links)
19
+ processors[4].should be_a(Snippr::Processor::Wikilinks)
18
20
  end
19
21
 
20
22
  end
21
23
 
22
24
  describe ".process" do
23
25
 
24
- it "should call process on all processors, passing the content between them and returning the last result" do
26
+ it "calls process on all processors, passing the content between them and returning the last result" do
25
27
  seq = sequence 'processors'
26
28
  subject.processors.each_with_index do |processor, i|
27
29
  processor.should respond_to(:process)
@@ -10,10 +10,20 @@ describe Snippr::ViewHelper do
10
10
  "wrapppppp #{param.upcase} ppppppparw"
11
11
  end
12
12
 
13
- it "should allow calling of methods on a Rails view" do
13
+ def with_block(param1, block_content)
14
+ "block result: #{block_content}"
15
+ end
16
+
17
+ it "allows calling of methods on a Rails view" do
14
18
  snippr(:with_view_helper_method).should == "<!-- starting snippr: withViewHelperMethod -->\nwith helper *wrapppppp TEST ppppppparw*\n<!-- closing snippr: withViewHelperMethod -->"
15
19
  end
16
20
 
21
+ context "block given in snippet" do
22
+ it "returns a correct snippet" do
23
+ snippr(:with_block).should == "<!-- starting snippr: withBlock -->\nblock result: IN BLOCK-LINE \"TWO\"\n<!-- closing snippr: withBlock -->"
24
+ end
25
+ end
26
+
17
27
  context "existance check on string returned by snippr" do
18
28
 
19
29
  context "existing snippet" do
@@ -98,13 +108,13 @@ describe Snippr::ViewHelper do
98
108
 
99
109
  context "existing snippr" do
100
110
 
101
- it "should call html_safe and return html safe value" do
111
+ it "calls html_safe and return html safe value" do
102
112
  content = snippr(:home)
103
113
  content.should == "<!-- starting snippr: home -->\n<p>Home</p>\n<!-- closing snippr: home -->"
104
114
  content.should be_html_safe
105
115
  end
106
116
 
107
- it "should pass html_safe snippr to block" do
117
+ it "passes html_safe snippr to block" do
108
118
  lambda {
109
119
  snippr(:home) do |snippr|
110
120
  snippr.should be_html_safe
@@ -113,7 +123,7 @@ describe Snippr::ViewHelper do
113
123
  }.should raise_error StandardError, 'block should be called'
114
124
  end
115
125
 
116
- it "should return 0 with block" do
126
+ it "returns 0 with block" do
117
127
  snippr(:home) do; end.should == 0
118
128
  end
119
129
 
@@ -121,13 +131,13 @@ describe Snippr::ViewHelper do
121
131
 
122
132
  context "empty snippr" do
123
133
 
124
- it "should call html_safe return html save value" do
134
+ it "calls html_safe return html save value" do
125
135
  content = snippr(:empty)
126
136
  content.should == "<!-- starting snippr: empty -->\n\n<!-- closing snippr: empty -->"
127
137
  content.should be_html_safe
128
138
  end
129
139
 
130
- it "should not pass snippr to block but concat snippr and return 0" do
140
+ it "doesn't pass snippr to block but concat snippr and return 0" do
131
141
  expects(:concat).with("<!-- starting snippr: empty -->\n\n<!-- closing snippr: empty -->")
132
142
  lambda {
133
143
  snippr(:empty) do
@@ -140,13 +150,13 @@ describe Snippr::ViewHelper do
140
150
 
141
151
  context "missing snippr" do
142
152
 
143
- it "should call html_safe return html save value" do
153
+ it "calls html_safe return html save value" do
144
154
  content = snippr(:doesnotexist)
145
155
  content.should == '<!-- missing snippr: doesnotexist -->'
146
156
  content.should be_html_safe
147
157
  end
148
158
 
149
- it "should not pass snippr to block but concat snippr and return 0" do
159
+ it "doesn't pass snippr to block but concat snippr and return 0" do
150
160
  expects(:concat).with('<!-- missing snippr: doesnotexist -->')
151
161
  lambda {
152
162
  snippr(:doesnotexist) do
@@ -194,7 +204,7 @@ describe Snippr::ViewHelper do
194
204
  stubs(:params).returns(:action => :and_underscore)
195
205
  end
196
206
 
197
- it "should camelize controller and action names" do
207
+ it "camelizes controller and action names" do
198
208
  snippr_with_path(:a_snippet).should == "<!-- starting snippr: withUnderscore/andUnderscore/aSnippet -->\nan underscored snippet with param {param}\n<!-- closing snippr: withUnderscore/andUnderscore/aSnippet -->"
199
209
  end
200
210
 
@@ -211,7 +221,7 @@ describe Snippr::ViewHelper do
211
221
  snippr_with_path(:a_snippet, :param => "value").should == "<!-- starting snippr: withUnderscore/andUnderscore/aSnippet -->\nan underscored snippet with param value\n<!-- closing snippr: withUnderscore/andUnderscore/aSnippet -->"
212
222
  end
213
223
 
214
- it "should allow multiple arguments" do
224
+ it "allows multiple arguments" do
215
225
  snippr_with_path(:deeper, :nested, :snippet).should == "<!-- missing snippr: withUnderscore/andUnderscore/deeper/nested/snippet -->"
216
226
  end
217
227
  end
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: snippr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.2
5
- prerelease:
4
+ version: 0.14.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Daniel Harrington
@@ -10,74 +9,106 @@ authors:
10
9
  autorequire:
11
10
  bindir: bin
12
11
  cert_chain: []
13
- date: 2012-06-15 00:00:00.000000000 Z
12
+ date: 2013-04-29 00:00:00.000000000 Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: i18n
17
- requirement: &2183725920 !ruby/object:Gem::Requirement
18
- none: false
16
+ requirement: !ruby/object:Gem::Requirement
19
17
  requirements:
20
18
  - - ! '>='
21
19
  - !ruby/object:Gem::Version
22
20
  version: '0'
23
21
  type: :runtime
24
22
  prerelease: false
25
- version_requirements: *2183725920
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ! '>='
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
26
28
  - !ruby/object:Gem::Dependency
27
29
  name: activesupport
28
- requirement: &2183725480 !ruby/object:Gem::Requirement
29
- none: false
30
+ requirement: !ruby/object:Gem::Requirement
30
31
  requirements:
31
32
  - - ! '>='
32
33
  - !ruby/object:Gem::Version
33
34
  version: '0'
34
35
  type: :runtime
35
36
  prerelease: false
36
- version_requirements: *2183725480
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ! '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
37
42
  - !ruby/object:Gem::Dependency
38
43
  name: ci_reporter
39
- requirement: &2183774080 !ruby/object:Gem::Requirement
40
- none: false
44
+ requirement: !ruby/object:Gem::Requirement
41
45
  requirements:
42
46
  - - ~>
43
47
  - !ruby/object:Gem::Version
44
48
  version: 1.6.5
45
49
  type: :development
46
50
  prerelease: false
47
- version_requirements: *2183774080
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ version: 1.6.5
48
56
  - !ruby/object:Gem::Dependency
49
57
  name: rspec
50
- requirement: &2183773580 !ruby/object:Gem::Requirement
51
- none: false
58
+ requirement: !ruby/object:Gem::Requirement
52
59
  requirements:
53
60
  - - ~>
54
61
  - !ruby/object:Gem::Version
55
62
  version: 2.8.0
56
63
  type: :development
57
64
  prerelease: false
58
- version_requirements: *2183773580
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 2.8.0
59
70
  - !ruby/object:Gem::Dependency
60
71
  name: mocha
61
- requirement: &2183773120 !ruby/object:Gem::Requirement
62
- none: false
72
+ requirement: !ruby/object:Gem::Requirement
63
73
  requirements:
64
- - - =
74
+ - - '='
65
75
  - !ruby/object:Gem::Version
66
76
  version: 0.11.4
67
77
  type: :development
68
78
  prerelease: false
69
- version_requirements: *2183773120
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - '='
82
+ - !ruby/object:Gem::Version
83
+ version: 0.11.4
70
84
  - !ruby/object:Gem::Dependency
71
85
  name: rake
72
- requirement: &2183772660 !ruby/object:Gem::Requirement
73
- none: false
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ~>
89
+ - !ruby/object:Gem::Version
90
+ version: 0.9.0
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
74
94
  requirements:
75
95
  - - ~>
76
96
  - !ruby/object:Gem::Version
77
- version: 0.9.2
97
+ version: 0.9.0
98
+ - !ruby/object:Gem::Dependency
99
+ name: debugger
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
78
105
  type: :development
79
106
  prerelease: false
80
- version_requirements: *2183772660
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ! '>='
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
81
112
  description: This gem provides ways to access file based cms resources from a rails
82
113
  app.
83
114
  email:
@@ -103,6 +134,7 @@ files:
103
134
  - lib/snippr/normalizer/de_rester.rb
104
135
  - lib/snippr/path.rb
105
136
  - lib/snippr/processor.rb
137
+ - lib/snippr/processor/block.rb
106
138
  - lib/snippr/processor/dynamics.rb
107
139
  - lib/snippr/processor/functions.rb
108
140
  - lib/snippr/processor/links.rb
@@ -126,6 +158,7 @@ files:
126
158
  - spec/fixtures/meta/withNoContent.snip
127
159
  - spec/fixtures/topup/someError.snip
128
160
  - spec/fixtures/topup/success.snip
161
+ - spec/fixtures/withBlock.snip
129
162
  - spec/fixtures/withUnderscore/andUnderscore/aSnippet.snip
130
163
  - spec/fixtures/withViewHelperMethod.snip
131
164
  - spec/snippr/i18n_spec.rb
@@ -144,28 +177,28 @@ files:
144
177
  - spec/snippr/view_helper_spec.rb
145
178
  - spec/spec_helper.rb
146
179
  homepage: http://github.com/blaulabs/snippr
147
- licenses: []
180
+ licenses:
181
+ - MIT
182
+ metadata: {}
148
183
  post_install_message:
149
184
  rdoc_options: []
150
185
  require_paths:
151
186
  - lib
152
187
  required_ruby_version: !ruby/object:Gem::Requirement
153
- none: false
154
188
  requirements:
155
189
  - - ! '>='
156
190
  - !ruby/object:Gem::Version
157
191
  version: '0'
158
192
  required_rubygems_version: !ruby/object:Gem::Requirement
159
- none: false
160
193
  requirements:
161
194
  - - ! '>='
162
195
  - !ruby/object:Gem::Version
163
196
  version: '0'
164
197
  requirements: []
165
198
  rubyforge_project: snippr
166
- rubygems_version: 1.8.11
199
+ rubygems_version: 2.0.3
167
200
  signing_key:
168
- specification_version: 3
201
+ specification_version: 4
169
202
  summary: File based content management
170
203
  test_files:
171
204
  - spec/fixtures/a/path/aSnippet.snip
@@ -182,6 +215,7 @@ test_files:
182
215
  - spec/fixtures/meta/withNoContent.snip
183
216
  - spec/fixtures/topup/someError.snip
184
217
  - spec/fixtures/topup/success.snip
218
+ - spec/fixtures/withBlock.snip
185
219
  - spec/fixtures/withUnderscore/andUnderscore/aSnippet.snip
186
220
  - spec/fixtures/withViewHelperMethod.snip
187
221
  - spec/snippr/i18n_spec.rb