merb 0.4.2 → 0.5.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 +21 -14
- data/Rakefile +157 -108
- data/SVN_REVISION +1 -0
- data/app_generators/merb/templates/Rakefile +20 -4
- data/app_generators/merb/templates/app/views/exceptions/internal_server_error.html.erb +1 -1
- data/app_generators/merb/templates/config/boot.rb +1 -1
- data/app_generators/merb/templates/config/dependencies.rb +3 -3
- data/app_generators/merb/templates/config/merb.yml +5 -0
- data/app_generators/merb/templates/config/merb_init.rb +3 -3
- data/app_generators/merb/templates/script/destroy +3 -0
- data/app_generators/merb/templates/script/generate +1 -1
- data/app_generators/merb/templates/spec/spec_helper.rb +2 -2
- data/app_generators/merb/templates/test/test_helper.rb +1 -1
- data/app_generators/merb_plugin/merb_plugin_generator.rb +4 -0
- data/bin/merb +1 -3
- data/lib/merb.rb +144 -76
- data/lib/merb/abstract_controller.rb +6 -5
- data/lib/merb/assets.rb +119 -0
- data/lib/merb/boot_loader.rb +217 -0
- data/lib/merb/caching.rb +1 -1
- data/lib/merb/caching/action_cache.rb +1 -1
- data/lib/merb/caching/fragment_cache.rb +1 -1
- data/lib/merb/caching/store/file_cache.rb +1 -1
- data/lib/merb/config.rb +290 -0
- data/lib/merb/controller.rb +5 -5
- data/lib/merb/core_ext/get_args.rb +1 -0
- data/lib/merb/core_ext/hash.rb +182 -169
- data/lib/merb/core_ext/kernel.rb +57 -26
- data/lib/merb/dispatcher.rb +6 -6
- data/lib/merb/drb_server.rb +1 -1
- data/lib/merb/generators/merb_generator_helpers.rb +7 -6
- data/lib/merb/logger.rb +1 -1
- data/lib/merb/mail_controller.rb +3 -4
- data/lib/merb/mailer.rb +2 -2
- data/lib/merb/mixins/basic_authentication.rb +2 -2
- data/lib/merb/mixins/controller.rb +1 -1
- data/lib/merb/mixins/general_controller.rb +13 -20
- data/lib/merb/mixins/inline_partial.rb +32 -0
- data/lib/merb/mixins/render.rb +3 -3
- data/lib/merb/mixins/responder.rb +1 -1
- data/lib/merb/mixins/view_context.rb +159 -33
- data/lib/merb/mongrel_handler.rb +9 -9
- data/lib/merb/plugins.rb +1 -1
- data/lib/merb/request.rb +25 -1
- data/lib/merb/router.rb +264 -226
- data/lib/merb/server.rb +66 -560
- data/lib/merb/session/cookie_store.rb +14 -13
- data/lib/merb/session/mem_cache_session.rb +20 -10
- data/lib/merb/session/memory_session.rb +21 -11
- data/lib/merb/template.rb +2 -2
- data/lib/merb/template/erubis.rb +3 -33
- data/lib/merb/template/haml.rb +8 -3
- data/lib/merb/test/fake_request.rb +8 -3
- data/lib/merb/test/helper.rb +66 -22
- data/lib/merb/test/rspec.rb +9 -155
- data/lib/merb/test/rspec_matchers/controller_matchers.rb +117 -0
- data/lib/merb/test/rspec_matchers/markup_matchers.rb +98 -0
- data/lib/merb/upload_handler.rb +2 -1
- data/lib/merb/version.rb +38 -3
- data/lib/merb/view_context.rb +1 -2
- data/lib/tasks/merb.rake +11 -11
- data/merb_generators/part_controller/USAGE +5 -0
- data/merb_generators/part_controller/part_controller_generator.rb +27 -0
- data/merb_generators/part_controller/templates/controller.rb +8 -0
- data/merb_generators/part_controller/templates/helper.rb +5 -0
- data/merb_generators/part_controller/templates/index.html.erb +3 -0
- data/rspec_generators/merb_controller_test/merb_controller_test_generator.rb +1 -1
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/fixtures/controllers/dispatch_spec_controllers.rb +9 -1
- data/spec/fixtures/controllers/render_spec_controllers.rb +5 -5
- data/spec/fixtures/models/router_spec_models.rb +10 -0
- data/spec/merb/abstract_controller_spec.rb +2 -2
- data/spec/merb/assets_spec.rb +207 -0
- data/spec/merb/caching_spec.rb +2 -2
- data/spec/merb/controller_spec.rb +7 -2
- data/spec/merb/cookie_store_spec.rb +1 -1
- data/spec/merb/core_ext/class_spec.rb +97 -0
- data/spec/merb/core_ext/enumerable_spec.rb +27 -0
- data/spec/merb/core_ext/hash_spec.rb +251 -0
- data/spec/merb/core_ext/inflector_spec.rb +34 -0
- data/spec/merb/core_ext/kernel_spec.rb +25 -0
- data/spec/merb/core_ext/numeric_spec.rb +26 -0
- data/spec/merb/core_ext/object_spec.rb +47 -0
- data/spec/merb/core_ext/string_spec.rb +22 -0
- data/spec/merb/core_ext/symbol_spec.rb +7 -0
- data/spec/merb/dependency_spec.rb +22 -0
- data/spec/merb/dispatch_spec.rb +23 -12
- data/spec/merb/fake_request_spec.rb +8 -0
- data/spec/merb/generator_spec.rb +140 -21
- data/spec/merb/handler_spec.rb +5 -5
- data/spec/merb/mail_controller_spec.rb +3 -3
- data/spec/merb/render_spec.rb +1 -1
- data/spec/merb/responder_spec.rb +3 -3
- data/spec/merb/router_spec.rb +260 -191
- data/spec/merb/server_spec.rb +5 -5
- data/spec/merb/upload_handler_spec.rb +7 -0
- data/spec/merb/version_spec.rb +33 -0
- data/spec/merb/view_context_spec.rb +217 -59
- data/spec/spec_generator_helper.rb +15 -0
- data/spec/spec_helper.rb +5 -3
- data/spec/spec_helpers/url_shared_behaviour.rb +5 -7
- metadata +32 -7
- data/lib/merb/caching/store/memcache.rb +0 -20
- data/lib/merb/mixins/form_control.rb +0 -332
- data/lib/patch +0 -69
- data/spec/merb/core_ext_spec.rb +0 -464
- data/spec/merb/form_control_mixin_spec.rb +0 -431
|
@@ -38,8 +38,8 @@ class FakeModelWithArguments
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def to_xml(*args)
|
|
41
|
-
options = args.last.is_a?(Hash) ? args.pop
|
|
42
|
-
options.keys.
|
|
41
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
|
42
|
+
options.keys.sort_by{|s| s.to_s}.inject('') do |str, tag|
|
|
43
43
|
str << "<#{tag}>#{options[tag]}</#{tag}>"
|
|
44
44
|
end
|
|
45
45
|
end
|
|
@@ -169,6 +169,6 @@ class ExtensionTemplateController < Merb::Controller
|
|
|
169
169
|
|
|
170
170
|
end
|
|
171
171
|
|
|
172
|
-
Merb::
|
|
173
|
-
Merb::
|
|
174
|
-
Merb::
|
|
172
|
+
Merb::BootLoader.load_action_arguments
|
|
173
|
+
Merb::BootLoader.load_controller_template_path_cache
|
|
174
|
+
Merb::BootLoader.load_inline_helpers
|
|
@@ -13,8 +13,8 @@ describe Merb::AbstractController do
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
after(:all) do
|
|
16
|
-
Merb::
|
|
17
|
-
Merb::
|
|
16
|
+
Merb::BootLoader.load_controller_template_path_cache
|
|
17
|
+
Merb::BootLoader.load_inline_helpers
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
it "should add a template path" do
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
5
|
+
describe Merb::Assets, "in the production environment" do
|
|
6
|
+
|
|
7
|
+
before(:each) do
|
|
8
|
+
@old_env = Merb::Config[:environment]
|
|
9
|
+
Merb::Config[:environment] = :production
|
|
10
|
+
Merb::Config.delete(:bundle_assets)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
after(:each) do
|
|
14
|
+
Merb::Config[:environment] = @old_env
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "should bundle assets" do
|
|
18
|
+
Merb::Assets.bundle?.should == true
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
describe Merb::Assets, "in the development environment" do
|
|
24
|
+
|
|
25
|
+
before(:each) do
|
|
26
|
+
@old_env = Merb::Config[:environment]
|
|
27
|
+
Merb::Config[:environment] = :development
|
|
28
|
+
Merb::Config.delete(:bundle_assets)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
after(:each) do
|
|
32
|
+
Merb::Config[:environment] = @old_env
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "should not bundle assets" do
|
|
36
|
+
Merb::Assets.bundle?.should == false
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe Merb::Assets, "in the development environment with asset bundling enabled" do
|
|
42
|
+
|
|
43
|
+
before(:each) do
|
|
44
|
+
@old_env = Merb::Config[:environment]
|
|
45
|
+
Merb::Config[:environment] = :development
|
|
46
|
+
Merb::Config[:bundle_assets] = true
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
after(:each) do
|
|
50
|
+
Merb::Config[:environment] = @old_env
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "should bundle assets" do
|
|
54
|
+
Merb::Assets.bundle?.should == true
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
describe Merb::Assets::AssetHelpers, "asset_path" do
|
|
60
|
+
|
|
61
|
+
include Merb::Assets::AssetHelpers
|
|
62
|
+
|
|
63
|
+
it "should turn a symbol shorthand for a javascript asset into a URI path" do
|
|
64
|
+
asset_path(:javascript, :dingo).should == "/javascripts/dingo.js"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "should turn a string shorthand for a javascript asset into a URI path" do
|
|
68
|
+
asset_path(:javascript, "dingo").should == "/javascripts/dingo.js"
|
|
69
|
+
asset_path(:javascript, "dingo.r7337").should == "/javascripts/dingo.r7337.js"
|
|
70
|
+
asset_path(:javascript, "dingo/wakka").should == "/javascripts/dingo/wakka.js"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "should turn the filename of a javascript asset into a URI path" do
|
|
74
|
+
asset_path(:javascript, "dingo.js").should == "/javascripts/dingo.js"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "should turn a symbol shorthand for a stylesheet asset into a URI path" do
|
|
78
|
+
asset_path(:stylesheet, :dingo).should == "/stylesheets/dingo.css"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "should turn a string shorthand for a stylesheet asset into a URI path" do
|
|
82
|
+
asset_path(:stylesheet, "dingo").should == "/stylesheets/dingo.css"
|
|
83
|
+
asset_path(:stylesheet, "dingo.r7337").should == "/stylesheets/dingo.r7337.css"
|
|
84
|
+
asset_path(:stylesheet, "dingo/wakka").should == "/stylesheets/dingo/wakka.css"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "should turn the filename of a stylesheet asset into a URI path" do
|
|
88
|
+
asset_path(:stylesheet, "dingo.css").should == "/stylesheets/dingo.css"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "should turn an asset name into a file path" do
|
|
92
|
+
asset_path(:stylesheet, :dingo, true).should == "public/stylesheets/dingo.css"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "should prepend any path prefix to a URI path" do
|
|
96
|
+
begin
|
|
97
|
+
Merb::Config[:path_prefix] = '/inky'
|
|
98
|
+
asset_path(:stylesheet, "dingo.css").should == "/inky/stylesheets/dingo.css"
|
|
99
|
+
ensure
|
|
100
|
+
Merb::Config.delete(:path_prefix)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it "should prepend any path prefix to a local path" do
|
|
105
|
+
begin
|
|
106
|
+
Merb::Config[:path_prefix] = '/inky'
|
|
107
|
+
asset_path(:stylesheet, "dingo.css", true).should == "public/stylesheets/dingo.css"
|
|
108
|
+
ensure
|
|
109
|
+
Merb::Config.delete(:path_prefix)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
describe "an asset bundler with callbacks", :shared => true do
|
|
115
|
+
it "should collect callbacks for execution after bundling" do
|
|
116
|
+
@bundler_klass.add_callback { |filename| "yay #{filename}" }
|
|
117
|
+
@bundler_klass.callbacks.should have(1).procs
|
|
118
|
+
@bundler_klass.callbacks.first.call("me").should == "yay me"
|
|
119
|
+
@bundler_klass.callbacks.clear
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
describe "an asset bundler", :shared => true do
|
|
124
|
+
|
|
125
|
+
include Merb::Assets::AssetHelpers
|
|
126
|
+
|
|
127
|
+
before(:each) do
|
|
128
|
+
FileUtils.mkdir_p("./public/#{@bundler_klass.asset_type}s")
|
|
129
|
+
create_fixture_files
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
after(:each) do
|
|
133
|
+
FileUtils.rm_rf("./public")
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def create_fixture_files
|
|
137
|
+
@asset1 = :asset1
|
|
138
|
+
@asset1filename = asset_path(@bundler_klass.asset_type, @asset1, true)
|
|
139
|
+
File.open(@asset1filename, "w") { |f| f << "one" }
|
|
140
|
+
|
|
141
|
+
@asset2 = :asset2
|
|
142
|
+
@asset2filename = asset_path(@bundler_klass.asset_type, @asset2, true)
|
|
143
|
+
File.open(@asset2filename, "w") { |f| f << "two" }
|
|
144
|
+
|
|
145
|
+
@asset3 = :asset3
|
|
146
|
+
@asset3filename = asset_path(@bundler_klass.asset_type, @asset3, true)
|
|
147
|
+
File.open(@asset3filename, "w") { |f| f << "three" }
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it "should combine files into a single file" do
|
|
151
|
+
bundler = @bundler_klass.new(true, @asset1, @asset2, @asset3)
|
|
152
|
+
bundle_name = bundler.bundle!
|
|
153
|
+
File.read(asset_path(@bundler_klass.asset_type, bundle_name, true)).should == "one\ntwo\nthree\n"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it "should not overwrite bundles" do
|
|
157
|
+
File.open(asset_path(@bundler_klass.asset_type, :all, true), "w") { |f| f << "exists" }
|
|
158
|
+
bundler = @bundler_klass.new(true, @asset1, @asset2, @asset3)
|
|
159
|
+
bundle_name = bundler.bundle!
|
|
160
|
+
File.read(asset_path(@bundler_klass.asset_type, bundle_name, true)).should == "exists"
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
it "should execute all callbacks, passing them the bundled file's path" do
|
|
164
|
+
begin
|
|
165
|
+
@bundler_klass.add_callback do |filename|
|
|
166
|
+
File.open(filename, "a") do |f|
|
|
167
|
+
f.puts "yay"
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
bundler = @bundler_klass.new(true, @asset1, @asset2, @asset3)
|
|
172
|
+
bundle_name = bundler.bundle!
|
|
173
|
+
File.read(asset_path(@bundler_klass.asset_type, bundle_name, true)).should == "one\ntwo\nthree\nyay\n"
|
|
174
|
+
ensure
|
|
175
|
+
@bundler_klass.callbacks.clear
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
describe Merb::Assets::JavascriptAssetBundler do
|
|
182
|
+
|
|
183
|
+
before(:each) do
|
|
184
|
+
@bundler_klass = Merb::Assets::JavascriptAssetBundler
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
it "should handle javascript assets" do
|
|
188
|
+
@bundler_klass.asset_type.should == :javascript
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
it_should_behave_like "an asset bundler"
|
|
192
|
+
it_should_behave_like "an asset bundler with callbacks"
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
describe Merb::Assets::StylesheetAssetBundler do
|
|
196
|
+
|
|
197
|
+
before(:each) do
|
|
198
|
+
@bundler_klass = Merb::Assets::StylesheetAssetBundler
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
it "should handle stylesheet assets" do
|
|
202
|
+
@bundler_klass.asset_type.should == :stylesheet
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
it_should_behave_like "an asset bundler"
|
|
206
|
+
it_should_behave_like "an asset bundler with callbacks"
|
|
207
|
+
end
|
data/spec/merb/caching_spec.rb
CHANGED
|
@@ -83,7 +83,7 @@ end
|
|
|
83
83
|
|
|
84
84
|
describe "fragment caching in memory" do
|
|
85
85
|
before do
|
|
86
|
-
Merb::
|
|
86
|
+
Merb::Config['cache_store'] = 'memory'
|
|
87
87
|
@c = Merb::Caching::Fragment
|
|
88
88
|
@c.clear
|
|
89
89
|
end
|
|
@@ -93,7 +93,7 @@ end
|
|
|
93
93
|
|
|
94
94
|
describe "fragment caching to a file" do
|
|
95
95
|
before do
|
|
96
|
-
Merb::
|
|
96
|
+
Merb::Config[:cache_store] = 'file'
|
|
97
97
|
@c = Merb::Caching::Fragment
|
|
98
98
|
@c.clear
|
|
99
99
|
end
|
|
@@ -13,11 +13,16 @@ describe "Merb::Controller" do
|
|
|
13
13
|
c = new_controller
|
|
14
14
|
c._layout.should == :application
|
|
15
15
|
end
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
it "should have a spec helper to dispatch that skips the router" do
|
|
18
18
|
Merb::Router.should_not_receive(:match)
|
|
19
19
|
dispatch_to(Bar, :foo, :id => "1") do |controller|
|
|
20
|
-
|
|
20
|
+
if defined? ParseTreeArray # We have parameterized actions
|
|
21
|
+
controller.should_receive(:foo).with("1")
|
|
22
|
+
else
|
|
23
|
+
controller.should_receive(:foo) # No parameterized actions
|
|
24
|
+
controller.params[:id].should == "1"
|
|
25
|
+
end
|
|
21
26
|
end
|
|
22
27
|
end
|
|
23
28
|
|
|
@@ -18,7 +18,7 @@ class TestCookieSessionController < Merb::Controller
|
|
|
18
18
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
Merb::
|
|
21
|
+
Merb::Config[:session_secret_key] = 'Secret!'
|
|
22
22
|
|
|
23
23
|
describe Merb::SessionMixin do
|
|
24
24
|
it "should set the cookie if the cookie is changed" do
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
|
2
|
+
|
|
3
|
+
# Class cattr_reader
|
|
4
|
+
|
|
5
|
+
class ClassWithCAttrReader
|
|
6
|
+
cattr_reader :bacon
|
|
7
|
+
def initialize; @@bacon = "chunky"; end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
describe "Core Class with cattr_reader", :shared => true do
|
|
11
|
+
|
|
12
|
+
it "should read value from attribute" do
|
|
13
|
+
@klass.bacon.should == "chunky"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "should not write to attribute" do
|
|
17
|
+
lambda {
|
|
18
|
+
@klass.bacon = "soggy"
|
|
19
|
+
}.should raise_error(NoMethodError)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe Class, "with cattr_reader" do
|
|
25
|
+
|
|
26
|
+
before do
|
|
27
|
+
@klass = ClassWithCAttrReader.new.class
|
|
28
|
+
end
|
|
29
|
+
it_should_behave_like "Core Class with cattr_reader"
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe Class, "with cattr_reader (instantiated)" do
|
|
34
|
+
|
|
35
|
+
before do
|
|
36
|
+
@klass = ClassWithCAttrReader.new
|
|
37
|
+
end
|
|
38
|
+
it_should_behave_like "Core Class with cattr_reader"
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Class cattr_writer
|
|
43
|
+
|
|
44
|
+
class ClassWithCAttrWriter
|
|
45
|
+
cattr_writer :bacon
|
|
46
|
+
def self.chunky?; @@bacon == "chunky"; end
|
|
47
|
+
def chunky?; self.class.chunky?; end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
describe "Core Class with cattr_writer", :shared => true do
|
|
51
|
+
|
|
52
|
+
it "should write value to attribute" do
|
|
53
|
+
@klass.should be_chunky
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "should not read attribute" do
|
|
57
|
+
lambda {
|
|
58
|
+
@klass.bacon
|
|
59
|
+
}.should raise_error(NoMethodError)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
describe Class, "with cattr_writer" do
|
|
65
|
+
|
|
66
|
+
before do
|
|
67
|
+
@klass = ClassWithCAttrWriter.new.class
|
|
68
|
+
@klass.bacon = "chunky"
|
|
69
|
+
end
|
|
70
|
+
it_should_behave_like "Core Class with cattr_writer"
|
|
71
|
+
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
describe Class, "with cattr_writer (instantiated)" do
|
|
75
|
+
|
|
76
|
+
before do
|
|
77
|
+
@klass = ClassWithCAttrWriter.new
|
|
78
|
+
@klass.bacon = "chunky"
|
|
79
|
+
end
|
|
80
|
+
it_should_behave_like "Core Class with cattr_writer"
|
|
81
|
+
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
class ClassWithAttrInitialize
|
|
85
|
+
attr_initialize :dog, :muppet
|
|
86
|
+
attr_accessor :dog, :muppet
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
describe ClassWithAttrInitialize do
|
|
90
|
+
|
|
91
|
+
it "should initialize with values" do
|
|
92
|
+
c = ClassWithAttrInitialize.new("louie", "bert")
|
|
93
|
+
c.dog.should == "louie"
|
|
94
|
+
c.muppet.should == "bert"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Enumerable do
|
|
4
|
+
before do
|
|
5
|
+
@mascots = ['louie', 'bert', 'ernie']
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it "should perform injecting" do
|
|
9
|
+
@mascots.injecting({}) { |m,i| m[i] = i.size }.should ==
|
|
10
|
+
{ 'louie' => 5, 'bert' => 4, 'ernie' => 5 }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "should find arrays of things inside other arrays" do
|
|
14
|
+
@mascots.include_any?('louie', 'sasquatch').should be_true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "should recognize absence of arrays of things inside other arrays" do
|
|
18
|
+
@mascots.include_any?('chicken', 'sasquatch').should be_false
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should group by" do
|
|
22
|
+
groups = (1..6).group_by{ |i| i % 3 }
|
|
23
|
+
groups[0].should == [3,6]
|
|
24
|
+
groups[1].should == [1,4]
|
|
25
|
+
groups[2].should == [2,5]
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Hash, "environmentize_keys!" do
|
|
4
|
+
it "should transform keys to uppercase text" do
|
|
5
|
+
{ :test_1 => 'test', 'test_2' => 'test', 1 => 'test' }.environmentize_keys!.should ==
|
|
6
|
+
{ 'TEST_1' => 'test', 'TEST_2' => 'test', '1' => 'test' }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "should only transform one level of keys" do
|
|
10
|
+
{ :test_1 => { :test2 => 'test'} }.environmentize_keys!.should ==
|
|
11
|
+
{ 'TEST_1' => { :test2 => 'test'} }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe Hash, "only" do
|
|
16
|
+
before do
|
|
17
|
+
@hash = { :one => 'ONE', 'two' => 'TWO', 3 => 'THREE' }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "should return a hash with only the given key(s)" do
|
|
21
|
+
@hash.only(:one).should == { :one => 'ONE' }
|
|
22
|
+
@hash.only(:one, 3).should == { :one => 'ONE', 3 => 'THREE' }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe Hash, "except" do
|
|
27
|
+
before do
|
|
28
|
+
@hash = { :one => 'ONE', 'two' => 'TWO', 3 => 'THREE' }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "should return a hash without only the given key(s)" do
|
|
32
|
+
@hash.except(:one).should == { 'two' => 'TWO', 3 => 'THREE' }
|
|
33
|
+
@hash.except(:one, 3).should == { 'two' => 'TWO' }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe Hash, "symbolize_keys!" do
|
|
38
|
+
before do
|
|
39
|
+
@hash = { 'one' => 1, 'two' => 2 }
|
|
40
|
+
@hash_with_another_hash = { 'a' => 'A', 'prefs' => { 'private' => true, 'sex' => 'yes please' } }
|
|
41
|
+
@hash_with_non_string_keys = { 'one' => 1, 2 => 'TWO', '' => 'blank' }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "should convert all keys to symbols" do
|
|
45
|
+
@hash.symbolize_keys!
|
|
46
|
+
@hash.should == { :one => 1, :two => 2 }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "should recursively convert all keys to symbols" do
|
|
50
|
+
@hash_with_another_hash.symbolize_keys!
|
|
51
|
+
@hash_with_another_hash.should == { :a => 'A', :prefs => { :private => true, :sex => 'yes please' } }
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe Hash, "to_xml_attributes" do
|
|
56
|
+
before do
|
|
57
|
+
@hash = { :one => "ONE", "two" => "TWO" }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "should turn the hash into xml attributes" do
|
|
61
|
+
attrs = @hash.to_xml_attributes
|
|
62
|
+
attrs.should match(/one="ONE"/m)
|
|
63
|
+
attrs.should match(/two="TWO"/m)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe Hash, "from_xml" do
|
|
68
|
+
it "should transform a simple tag with content" do
|
|
69
|
+
xml = "<tag>This is the contents</tag>"
|
|
70
|
+
Hash.from_xml(xml).should == { 'tag' => 'This is the contents' }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "should work with cdata tags" do
|
|
74
|
+
xml = <<-END
|
|
75
|
+
<tag>
|
|
76
|
+
<![CDATA[
|
|
77
|
+
text inside cdata
|
|
78
|
+
]]>
|
|
79
|
+
</tag>
|
|
80
|
+
END
|
|
81
|
+
Hash.from_xml(xml)["tag"].strip.should == "text inside cdata"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "should transform a simple tag with attributes" do
|
|
85
|
+
xml = "<tag attr1='1' attr2='2'></tag>"
|
|
86
|
+
hash = { 'tag' => { 'attr1' => '1', 'attr2' => '2' } }
|
|
87
|
+
Hash.from_xml(xml).should == hash
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it "should transform repeating siblings into an array" do
|
|
91
|
+
xml =<<-XML
|
|
92
|
+
<opt>
|
|
93
|
+
<user login="grep" fullname="Gary R Epstein" />
|
|
94
|
+
<user login="stty" fullname="Simon T Tyson" />
|
|
95
|
+
</opt>
|
|
96
|
+
XML
|
|
97
|
+
|
|
98
|
+
Hash.from_xml(xml)['opt']['user'].should be_an_instance_of(Array)
|
|
99
|
+
|
|
100
|
+
hash = {
|
|
101
|
+
'opt' => {
|
|
102
|
+
'user' => [{
|
|
103
|
+
'login' => 'grep',
|
|
104
|
+
'fullname' => 'Gary R Epstein'
|
|
105
|
+
},{
|
|
106
|
+
'login' => 'stty',
|
|
107
|
+
'fullname' => 'Simon T Tyson'
|
|
108
|
+
}]
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
Hash.from_xml(xml).should == hash
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "should not transform non-repeating siblings into an array" do
|
|
116
|
+
xml =<<-XML
|
|
117
|
+
<opt>
|
|
118
|
+
<user login="grep" fullname="Gary R Epstein" />
|
|
119
|
+
</opt>
|
|
120
|
+
XML
|
|
121
|
+
|
|
122
|
+
Hash.from_xml(xml)['opt']['user'].should be_an_instance_of(Hash)
|
|
123
|
+
|
|
124
|
+
hash = {
|
|
125
|
+
'opt' => {
|
|
126
|
+
'user' => {
|
|
127
|
+
'login' => 'grep',
|
|
128
|
+
'fullname' => 'Gary R Epstein'
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
Hash.from_xml(xml).should == hash
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it "should typecast an integer" do
|
|
137
|
+
xml = "<tag type='integer'>10</tag>"
|
|
138
|
+
Hash.from_xml(xml)['tag'].should == 10
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it "should typecast a true boolean" do
|
|
142
|
+
xml = "<tag type='boolean'>true</tag>"
|
|
143
|
+
Hash.from_xml(xml)['tag'].should be_true
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it "should typecast a false boolean" do
|
|
147
|
+
["false", "1", "0", "some word" ].each do |w|
|
|
148
|
+
Hash.from_xml("<tag type='boolean'>#{w}</tag>")['tag'].should be_false
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
it "should typecast a datetime" do
|
|
153
|
+
xml = "<tag type='datetime'>2007-12-31 10:32</tag>"
|
|
154
|
+
Hash.from_xml(xml)['tag'].should == Time.parse( '2007-12-31 10:32' ).utc
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it "should typecast a date" do
|
|
158
|
+
xml = "<tag type='date'>2007-12-31</tag>"
|
|
159
|
+
Hash.from_xml(xml)['tag'].should == Date.parse('2007-12-31')
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it "should unescape html entities" do
|
|
163
|
+
values = {
|
|
164
|
+
"<" => "<",
|
|
165
|
+
">" => ">",
|
|
166
|
+
'"' => """,
|
|
167
|
+
"'" => "'",
|
|
168
|
+
"&" => "&"
|
|
169
|
+
}
|
|
170
|
+
values.each do |k,v|
|
|
171
|
+
xml = "<tag>Some content #{v}</tag>"
|
|
172
|
+
Hash.from_xml(xml)['tag'].should match(Regexp.new(k))
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it "should undasherize keys as tags" do
|
|
177
|
+
xml = "<tag-1>Stuff</tag-1>"
|
|
178
|
+
Hash.from_xml(xml).keys.should include( 'tag_1' )
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
it "should undasherize keys as attributes" do
|
|
182
|
+
xml = "<tag1 attr-1='1'></tag1>"
|
|
183
|
+
Hash.from_xml(xml)['tag1'].keys.should include( 'attr_1')
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
it "should undasherize keys as tags and attributes" do
|
|
187
|
+
xml = "<tag-1 attr-1='1'></tag-1>"
|
|
188
|
+
Hash.from_xml(xml).keys.should include( 'tag_1' )
|
|
189
|
+
Hash.from_xml(xml)['tag_1'].keys.should include( 'attr_1')
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
it "should render nested content correctly" do
|
|
193
|
+
xml = "<root><tag1>Tag1 Content <em><strong>This is strong</strong></em></tag1></root>"
|
|
194
|
+
Hash.from_xml(xml)['root']['tag1'].should == "Tag1 Content <em><strong>This is strong</strong></em>"
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
it "should render nested content with split text nodes correctly" do
|
|
198
|
+
xml = "<root>Tag1 Content<em>Stuff</em> Hi There</root>"
|
|
199
|
+
Hash.from_xml(xml)['root'].should == "Tag1 Content<em>Stuff</em> Hi There"
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
it "should ignore attributes when a child is a text node" do
|
|
203
|
+
xml = "<root attr1='1'>Stuff</root>"
|
|
204
|
+
Hash.from_xml(xml).should == { "root" => "Stuff" }
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
it "should ignore attributes when any child is a text node" do
|
|
208
|
+
xml = "<root attr1='1'>Stuff <em>in italics</em></root>"
|
|
209
|
+
Hash.from_xml(xml).should == { "root" => "Stuff <em>in italics</em>" }
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
it "should correctly transform multiple children" do
|
|
213
|
+
xml = <<-XML
|
|
214
|
+
<user gender='m'>
|
|
215
|
+
<age type='integer'>35</age>
|
|
216
|
+
<name>Home Simpson</name>
|
|
217
|
+
<dob type='date'>1988-01-01</dob>
|
|
218
|
+
<joined-at type='datetime'>2000-04-28 23:01</joined-at>
|
|
219
|
+
<is-cool type='boolean'>true</is-cool>
|
|
220
|
+
</user>
|
|
221
|
+
XML
|
|
222
|
+
|
|
223
|
+
hash = {
|
|
224
|
+
"user" => {
|
|
225
|
+
"gender" => "m",
|
|
226
|
+
"age" => 35,
|
|
227
|
+
"name" => "Home Simpson",
|
|
228
|
+
"dob" => Date.parse('1988-01-01'),
|
|
229
|
+
"joined_at" => Time.parse("2000-04-28 23:01"),
|
|
230
|
+
"is_cool" => true
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
Hash.from_xml(xml).should == hash
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
describe Hash, 'to_params' do
|
|
239
|
+
before do
|
|
240
|
+
@hash = { :name => 'Bob', :address => { :street => '111 Ruby Ave.', :city => 'Ruby Central', :phones => ['111-111-1111', '222-222-2222'] } }
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
it 'should convert correctly into query parameters' do
|
|
244
|
+
@hash.to_params.split('&').sort.should ==
|
|
245
|
+
'name=Bob&address[city]=Ruby Central&address[phones]=111-111-1111222-222-2222&address[street]=111 Ruby Ave.'.split('&').sort
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
it 'should not leave a trailing &' do
|
|
249
|
+
@hash.to_params.should_not match(/&$/)
|
|
250
|
+
end
|
|
251
|
+
end
|