action_widget 0.5.1 → 0.6.0.pre

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b0064d71b6d2394b297e64a6fa7dcbe74de2c8bb
4
- data.tar.gz: 84ced9363a9d26829c241ec189d18d318609be25
3
+ metadata.gz: 65f04e0efe22c4a80632c8bb2788b48f60c0d1d9
4
+ data.tar.gz: 2319ca299e05f2611cef8a1f420f886dcaebadfa
5
5
  SHA512:
6
- metadata.gz: 72f994bbf840c022f8c6c0038e26b78c9454a7e816806e9b6a7826e72de5ed187bddf9fa6597dfc0f66af6c619e57d6fbf025cf442bcb5b31382e7911fdb0025
7
- data.tar.gz: 58a3598ec1cfef5167831f3657a44db4c6c3f01080e6a6579b7f7f576ab93ec49041cf03a6a086fd23d558f4418127ad4c146fd6cefb5d71b2f7fba96c29cb83
6
+ metadata.gz: 90bda59277298cee6d43f1e98580fd812def0f5a6de6a1066ac762a470cf04d0ef2b4dac78798e264f5cd197dc51ea13f448ba7b1371fb44e9cde66024c19efb
7
+ data.tar.gz: d715ab381e066c58275390dda86e5f1156bfba030519c8292db06fcd0ecf017a61ca3624ea00f199e97b1e6996d1f908e0891be776d0e9a3988a2ca804dc985f
data/.gitignore CHANGED
@@ -16,3 +16,4 @@ test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
18
  .rvmrc
19
+ .rspec
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ rvm:
2
+ - 2.2.2
3
+ - 1.9.3
4
+
5
+ bundler_args: --without documentation
data/README.md CHANGED
@@ -1,6 +1,23 @@
1
1
  # ActionWidget
2
2
 
3
- TODO: Write a gem description
3
+ `ActionWidget` is a light-weight widget system for [Ruby on
4
+ Rails](http://rubyonrails.com) and [Middleman](http://middlemanapp.com). It is
5
+ essentially a minimal tool set for building desktop-like UI components. The
6
+ main idea behind `ActionWidget` is the separation of the concept of an UI
7
+ component and its representation. While the representation of component might
8
+ easily change over time, the concept of a component is unlikely to change
9
+ significantly. Think of a button for instance: Most developers will agree that
10
+ a button is conceptually something that has a caption and when clicked triggers
11
+ an action. When we think about the visual representation of a button, however,
12
+ things get more complicated. While most people will agree to a certain degree
13
+ on what can visually be considered a button and what is something else, people
14
+ tend to have different ideas about a button's exact representation. There are
15
+ buttons with icons, without icons, round ones, rectangular ones, and so on.
16
+ Despite their different appearances, the functionality and in this sense the
17
+ component's concept stays the same: when clicked an action is triggered.
18
+ ActionWidget provides developers with a tool set that helps them to strictly
19
+ decouple a component's concept from its representation to support future
20
+ change.
4
21
 
5
22
  ## Installation
6
23
 
@@ -18,7 +35,274 @@ Or install it yourself as:
18
35
 
19
36
  ## Usage
20
37
 
21
- TODO: Write usage instructions here
38
+ `ActionWidget` can be used to build arbitrarily complex view components. To
39
+ illustrate the basic usage of `ActionWidget`, however, we start with a simple
40
+ example, a widget for representing a button. We then continue with widgets that
41
+ except blocks. We will use a widget representing panels as an example. Finally,
42
+ we see discuss how to build widgets that utilize widgets themselves for
43
+ constructing navigation components.
44
+
45
+ ### Simple Widgets
46
+
47
+ The goal of this section is to build a widget that represents a button. The
48
+ button we are designing must have a `caption` and a `type`. The type can either
49
+ be `regular`, `accept`, or `cancel`. The button further must have a specified
50
+ `size`, which can be `small`, `medium`, or `large`. Finally, the button
51
+ requires a `target` that defines the resource it links to. `ActionWidget`
52
+ compentens utilize [SmartProperties](http://github.com/t6d/smart_properties) to
53
+ define attributes that can be configured to automatically enforce these
54
+ constraints and provide sensible defaults.
55
+
56
+ In the example below, we simple use an `<a>` tag to represent a button. The
57
+ attributes `size` and `type` are simply translated into CSS classes. The
58
+ `caption` will be used as the text encolsed by the `<a>` tag and the `target`
59
+ will be used as the value the the `<a>` tag's `href` attribute.
60
+
61
+ ```ruby
62
+ class ButtonWidget < ActionWidget::Base
63
+ property :caption,
64
+ converts: :to_s,
65
+ required: true
66
+
67
+ property :target,
68
+ converts: :to_s,
69
+ accepts: lambda { |uri| URI.parse(uri) rescue false },
70
+ required: true
71
+
72
+ property :type,
73
+ converts: :to_sym,
74
+ accepts: [:regular, :accept, :cancel],
75
+ default: :regular
76
+
77
+ property :size,
78
+ converts: :to_sym,
79
+ accepts: [:small, :medium, :large],
80
+ default: :medium
81
+
82
+ def render
83
+ content_tag(:a, caption, href: target, class: css_classes)
84
+ end
85
+
86
+ protected
87
+
88
+ def css_classes
89
+ css_classes = ['btn']
90
+ css_classes << "btn-#{size}" unless size == :regular
91
+ css_classes << "btn-#{type}" unless type == :medium
92
+ css_classes
93
+ end
94
+ end
95
+ ```
96
+
97
+ By convention, a widget's class name should end in "Widget". This way,
98
+ `ActionWidget` automatically generates `ActionView` helper methods for more
99
+ convenient instantiation and rendering of a widget.
100
+
101
+ In our example, the widget can be instantiated by simply calling the helper
102
+ method `button_widget` and providing it with all necessary attributes:
103
+
104
+ ```erb
105
+ <%= button_widget caption: 'Go to Admin Area', size: :small, target: '/admin' %>
106
+ ```
107
+
108
+ Instead of using the provided helper method, a widget can always be instantiated
109
+ manually:
110
+
111
+ ```erb
112
+ <%= ButtonWidget.new(self, caption: 'Go to Admin Area', size: :small, target: '/admin').render %>
113
+ ```
114
+
115
+ In both cases, the resulting HTML looks as follows:
116
+
117
+ ```html
118
+ <a class="btn btn-small" href="/admin">Go to Admin Area</a>
119
+ ```
120
+
121
+ ### Widgets that Accept Blocks
122
+
123
+ The panel widget we are building requires a `title` and a block that defines the
124
+ widgets content.
125
+
126
+ ```ruby
127
+ require 'action_widget'
128
+
129
+ class PanelWidget < ActionWidget::Base
130
+ property :title, required: true, converts: :to_s
131
+
132
+ def render(&block)
133
+ content_tag(:div, class: 'panel') do
134
+ content_tag(:h2, title, class: 'title') +
135
+ content_tag(:div, class: 'content', &block)
136
+ end
137
+ end
138
+ end
139
+ ```
140
+
141
+ Again, the automatically generated helper method, `#panel_widget` in this case,
142
+ can be used to instantiate and render the widget:
143
+
144
+ ```erb
145
+ <%= panel_widget title: "Important Notice" do %>
146
+ The system will be down for maintanence today.
147
+ <% end %>
148
+ ```
149
+
150
+ Executing the code above results in the follwing HTML:
151
+
152
+ ```html
153
+ <div class="panel">
154
+ <h2 class="title">Important Notice</h2>
155
+ <div class="content">
156
+ The system will be down for maintanence today.
157
+ </div>
158
+ </div>
159
+ ```
160
+
161
+ Since widgets are simple Ruby classes, they naturally support inheritance.
162
+ Let's assume we require a special panel widget for sidebars that renders a
163
+ different header. There are two options:
164
+
165
+ 1. we can provide the tag that is chosen for the header as a property, or
166
+ 2. we restructure the `PanelWidget` class and then subclass it.
167
+
168
+ Let's take the second approach and extract the header rendering and the content
169
+ rendering into their own methods so we can overwrite either one of them in a
170
+ potential subclass.
171
+
172
+ ```ruby
173
+ class PanelWidget < ActionWidget::Base
174
+ property :title, required: true, converts: :to_s
175
+
176
+ def render(&block)
177
+ header + content(&block)
178
+ end
179
+
180
+ protected
181
+
182
+ def header
183
+ content_tag(:h2, title)
184
+ end
185
+
186
+ def content(&block)
187
+ content_tag(:div, &block)
188
+ end
189
+ end
190
+ ```
191
+
192
+ After this refactoring, we are able to subclass `PanelWidget` and customize the
193
+ `header` method:
194
+
195
+ ```ruby
196
+ class SidebarPanelWidget < PanelWidget
197
+ protected
198
+
199
+ def header
200
+ content_tag(:h3, title)
201
+ end
202
+ end
203
+ ```
204
+
205
+ ### Nested Widgets
206
+
207
+ Let's assume we want to implement a widget that simplifies the rendering of
208
+ navigational menus. The widget only exposes one property, `orientation`, which
209
+ can either be `horizontal` or `vertical`.
210
+
211
+ ```ruby
212
+ class MenuWidget < ActionWidget::Base
213
+ property :orientation,
214
+ accepts: [:horizontal, :vertical],
215
+ converts: :to_sym,
216
+ default: :horizontal,
217
+ required: true
218
+
219
+ def render(&block)
220
+ content_tag(:nav, class: orientation) do
221
+ content_tag(:ul) do
222
+ capture(self, &block)
223
+ end
224
+ end
225
+ end
226
+
227
+ def item(caption, target)
228
+ content_tag(:li) do
229
+ content_tag(:a, caption, href: target)
230
+ end
231
+ end
232
+
233
+ def submenu(caption, &block)
234
+ content_tag(:li) do
235
+ content_tag(:span, caption) +
236
+ self.class.new(view, orientation: orientation).render(&block)
237
+ end
238
+ end
239
+ end
240
+ ```
241
+
242
+ The following example demonstrates how to use this widget:
243
+
244
+ ```erb
245
+ <%= menu_widget do |m| %>
246
+ <%= m.item "Dashboard", "/" %>
247
+ <%= m.submenu "Admin" do |m| %>
248
+ <%= m.item "Manage Users", "/admin/users" %>
249
+ <%= m.item "Manage Groups", "/admin/groups" %>
250
+ <% end %>
251
+ <% end %>
252
+ ```
253
+
254
+ Executing the code above, will result in the following HTML being generated:
255
+
256
+ ```html
257
+ <nav class="horizontal">
258
+ <ul>
259
+ <li> <a href="/">Dashboard</a> </li>
260
+ <li>
261
+ <span>Admin</span>
262
+ <nav class="horizontal">
263
+ <ul>
264
+ <li><a href="/admin/users">Manage Users</a></li>
265
+ <li><a href="/admin/groups">Manage Groups</a></li>
266
+ </ul>
267
+ </nav>
268
+ </li>
269
+ </ul>
270
+ </nav>
271
+ ```
272
+
273
+ ### Option Capturing and Positional Argument Forwarding
274
+
275
+ `ActionWidget` instances capture all initializer keyword
276
+ arguments that do not correspond to a property in a general `options` hash.
277
+ All positional arguments supplied to an autogenerated helper are
278
+ forwarded to the `render` method.
279
+
280
+ ```ruby
281
+ class ParagraphWidget < ActionWidget::Base
282
+ def render(content, &block)
283
+ content = capture(&block) if block
284
+ content_tag(:p, content, class: options[:class])
285
+ end
286
+ end
287
+ ```
288
+
289
+ This widget can be used in the following two ways:
290
+
291
+ ```erb
292
+ <%= paragraph_widget("Some content", class: 'important') %>
293
+
294
+ <%= paragraph_widget(class: 'important') do %>
295
+ Some content
296
+ <% end %>
297
+ ```
298
+
299
+ In both cases, the resulting HTML reads as follows:
300
+
301
+ ```html
302
+ <p class="important">
303
+ Some content
304
+ </p>
305
+ ```
22
306
 
23
307
  ## Contributing
24
308
 
data/Rakefile CHANGED
@@ -1,2 +1,6 @@
1
1
  #!/usr/bin/env rake
2
2
  require "bundler/gem_tasks"
3
+
4
+ require "rspec/core/rake_task"
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ task :default => :spec
@@ -10,7 +10,11 @@ Gem::Specification.new do |gem|
10
10
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
11
11
  gem.name = "action_widget"
12
12
  gem.require_paths = ["lib"]
13
- gem.version = "0.5.1"
13
+ gem.version = "0.6.0.pre"
14
14
 
15
- gem.add_dependency 'smart_properties', '~> 1.1'
15
+ gem.add_dependency 'smart_properties', '~> 1.10'
16
+
17
+ gem.add_development_dependency 'rake', '~> 10.0'
18
+ gem.add_development_dependency 'rspec', '~> 3.3'
19
+ gem.add_development_dependency 'actionview', '~> 4.0'
16
20
  end
@@ -1,31 +1,21 @@
1
- require 'smart_properties'
2
-
3
1
  module ActionWidget
4
- class Base
2
+ class Base < SimpleDelegator
3
+ alias view __getobj__
5
4
  include SmartProperties
6
5
 
7
- def initialize(view, *args)
8
- @view = view
9
- super(*args)
6
+ attr_reader :options
7
+
8
+ def initialize(view, attributes = {})
9
+ properties = self.class.properties
10
+ attributes, options = attributes.partition { |name, value| properties.key?(name) }
11
+
12
+ @options = Hash[options]
13
+ super(view, Hash[attributes])
10
14
  end
11
15
 
12
16
  def render
13
17
  raise NotImplementedError, "#{self.class} must implement #render"
14
18
  end
15
-
16
- protected
17
-
18
- attr_reader :view
19
-
20
- undef :capture if method_defined?(:capture)
21
-
22
- def method_missing(method, *args, &block)
23
- view.send(method, *args, &block)
24
- rescue NoMethodError
25
- # Double check - the NoMethodError might have occurred somewhere else.
26
- view.respond_to?(method) ? raise : super
27
- end
28
-
29
19
  end
30
20
  end
31
21
 
@@ -2,18 +2,19 @@ module ActionWidget
2
2
  module ViewHelper
3
3
 
4
4
  def method_missing(name, *args, &block)
5
- return super unless name =~ /_widget$/
5
+ return super unless ActionWidget.helper?(name)
6
6
 
7
7
  klass = begin
8
- "#{name.to_s.camelcase}".constantize
8
+ ActionWidget.class_for(name)
9
9
  rescue NameError, LoadError
10
10
  return super
11
11
  end
12
12
 
13
13
  ActionWidget::ViewHelper.module_eval <<-RUBY
14
- def #{name}(*args, &block) # def example_widget(*args, &block)
15
- #{klass}.new(self, *args).render(&block) # ExampleWidget.new(self, *args).render(&block)
16
- end # end
14
+ def #{name}(*args, &block) # def example_widget(*args, &block)
15
+ attrs = args.last.kind_of?(Hash) ? args.pop : {}
16
+ #{klass}.new(self, attrs).render(*args, &block) # ExampleWidget.new(self, attrs).render(*args, &block)
17
+ end # end
17
18
  RUBY
18
19
 
19
20
  send(name, *args, &block)
data/lib/action_widget.rb CHANGED
@@ -1,3 +1,45 @@
1
+ require 'smart_properties'
2
+
3
+ module ActionWidget
4
+ class Configuration
5
+ include SmartProperties
6
+ property :prefix
7
+ property :suffix
8
+
9
+ attr_reader :pattern
10
+
11
+ def initialize(*)
12
+ super
13
+
14
+ @pattern = Regexp.new([
15
+ (prefix.underscore if prefix.presence),
16
+ "(.*)",
17
+ (suffix.underscore if suffix.presence)
18
+ ].compact.join("_"))
19
+ end
20
+ end
21
+
22
+ class << self
23
+ def configuration
24
+ @configuration ||= Configuration.new(suffix: "Widget")
25
+ end
26
+
27
+ def configure(&block)
28
+ @configuration = Configuration.new(&block)
29
+ end
30
+
31
+ def helper?(name)
32
+ !!configuration.pattern.match(name)
33
+ end
34
+
35
+ def class_for(helper_name)
36
+ basename = configuration.pattern.match(helper_name)[1]
37
+ classname = [configuration.prefix, basename.camelcase, configuration.suffix].join("")
38
+ classname.constantize
39
+ end
40
+ end
41
+ end
42
+
1
43
  require 'action_widget/base'
2
44
  require 'action_widget/view_helper'
3
- require 'action_widget/extensions'
45
+ require 'action_widget/extensions'
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ class DummyWidget < ActionWidget::Base
4
+ property :type
5
+
6
+ def render(content = nil)
7
+ classes = [options[:class], type].compact
8
+ if classes.empty?
9
+ content_tag(:p, content)
10
+ else
11
+ content_tag(:p, content, class: classes)
12
+ end
13
+ end
14
+ end
15
+
16
+ class ViewContext < ActionView::Base
17
+ include ActionWidget::ViewHelper
18
+ end
19
+
20
+ RSpec.describe DummyWidget do
21
+ let(:view) { ViewContext.new }
22
+
23
+ it "should delegate unknown method calls to the view context" do
24
+ expect(described_class.new(view).render).to eq("<p></p>")
25
+ end
26
+
27
+ context "option capturing and positional argument forwarding" do
28
+ let(:expected_html) { '<p class="wide teaser">Hello World</p>' }
29
+
30
+ it "should be possible to reference a property using a symbol" do
31
+ attributes = {type: 'teaser', class: 'wide'}
32
+ expect(view.dummy_widget("Hello World", attributes)).to eq(expected_html)
33
+ end
34
+
35
+ it "should be possible to reference a property using a string" do
36
+ attributes = {"type" => 'teaser', class: 'wide'}
37
+ expect(view.dummy_widget("Hello World", attributes)).to eq(expected_html)
38
+ end
39
+
40
+ specify "keyword arguments that do not correspond to a property should be captured as options" do
41
+ instance = described_class.new(view, class: 'wide')
42
+ expect(instance.options).to eq({class: 'wide'})
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,82 @@
1
+ require 'bundler/setup'
2
+ require 'rspec'
3
+ require 'action_view'
4
+
5
+ require 'action_widget'
6
+
7
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
8
+ RSpec.configure do |config|
9
+ # rspec-expectations config goes here. You can use an alternate
10
+ # assertion/expectation library such as wrong or the stdlib/minitest
11
+ # assertions if you prefer.
12
+ config.expect_with :rspec do |expectations|
13
+ # This option will default to `true` in RSpec 4. It makes the `description`
14
+ # and `failure_message` of custom matchers include text for helper methods
15
+ # defined using `chain`, e.g.:
16
+ # be_bigger_than(2).and_smaller_than(4).description
17
+ # # => "be bigger than 2 and smaller than 4"
18
+ # ...rather than:
19
+ # # => "be bigger than 2"
20
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
21
+ end
22
+
23
+ # rspec-mocks config goes here. You can use an alternate test double
24
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
25
+ config.mock_with :rspec do |mocks|
26
+ # Prevents you from mocking or stubbing a method that does not exist on
27
+ # a real object. This is generally recommended, and will default to
28
+ # `true` in RSpec 4.
29
+ mocks.verify_partial_doubles = true
30
+ end
31
+
32
+ # These two settings work together to allow you to limit a spec run
33
+ # to individual examples or groups you care about by tagging them with
34
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
35
+ # get run.
36
+ config.filter_run :focus
37
+ config.run_all_when_everything_filtered = true
38
+
39
+ # Allows RSpec to persist some state between runs in order to support
40
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
41
+ # you configure your source control system to ignore this file.
42
+ #
43
+ # config.example_status_persistence_file_path = "spec/examples.txt"
44
+
45
+ # Limits the available syntax to the non-monkey patched syntax that is
46
+ # recommended. For more details, see:
47
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
48
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
49
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
50
+ config.disable_monkey_patching!
51
+
52
+ # This setting enables warnings. It's recommended, but in some cases may
53
+ # be too noisy due to issues in dependencies.
54
+ config.warnings = true
55
+
56
+ # Many RSpec users commonly either run the entire suite or an individual
57
+ # file, and it's useful to allow more verbose output when running an
58
+ # individual spec file.
59
+ if config.files_to_run.one?
60
+ # Use the documentation formatter for detailed output,
61
+ # unless a formatter has already been configured
62
+ # (e.g. via a command-line flag).
63
+ config.default_formatter = 'doc'
64
+ end
65
+
66
+ # Print the 10 slowest examples and example groups at the
67
+ # end of the spec run, to help surface which specs are running
68
+ # particularly slow.
69
+ config.profile_examples = 10
70
+
71
+ # Run specs in random order to surface order dependencies. If you find an
72
+ # order dependency and want to debug it, you can fix the order by providing
73
+ # the seed, which is printed after each run.
74
+ # --seed 1234
75
+ config.order = :random
76
+
77
+ # Seed global randomization in this process using the `--seed` CLI option.
78
+ # Setting this allows you to use `--seed` to deterministically reproduce
79
+ # test failures related to randomization by passing the same `--seed` value
80
+ # as the one that triggered the failure.
81
+ Kernel.srand config.seed
82
+ end
metadata CHANGED
@@ -1,29 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: action_widget
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.0.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Tennhard
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-22 00:00:00.000000000 Z
11
+ date: 2015-10-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: smart_properties
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.1'
19
+ version: '1.10'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.1'
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: actionview
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '4.0'
27
69
  description:
28
70
  email:
29
71
  - me@t6d.de
@@ -31,7 +73,8 @@ executables: []
31
73
  extensions: []
32
74
  extra_rdoc_files: []
33
75
  files:
34
- - .gitignore
76
+ - ".gitignore"
77
+ - ".travis.yml"
35
78
  - Gemfile
36
79
  - LICENSE
37
80
  - README.md
@@ -45,6 +88,8 @@ files:
45
88
  - lib/action_widget/extensions/rails/generators.rb
46
89
  - lib/action_widget/extensions/rails/railtie.rb
47
90
  - lib/action_widget/view_helper.rb
91
+ - spec/action_widget_spec.rb
92
+ - spec/spec_helper.rb
48
93
  - support/templates/widget.rb.erb
49
94
  - support/templates/widget_spec.rb.erb
50
95
  homepage: http://github.com/t6d/action_widget
@@ -56,19 +101,21 @@ require_paths:
56
101
  - lib
57
102
  required_ruby_version: !ruby/object:Gem::Requirement
58
103
  requirements:
59
- - - '>='
104
+ - - ">="
60
105
  - !ruby/object:Gem::Version
61
106
  version: '0'
62
107
  required_rubygems_version: !ruby/object:Gem::Requirement
63
108
  requirements:
64
- - - '>='
109
+ - - ">"
65
110
  - !ruby/object:Gem::Version
66
- version: '0'
111
+ version: 1.3.1
67
112
  requirements: []
68
113
  rubyforge_project:
69
- rubygems_version: 2.0.6
114
+ rubygems_version: 2.4.5
70
115
  signing_key:
71
116
  specification_version: 4
72
117
  summary: Reusable view components for your Ruby web application
73
- test_files: []
118
+ test_files:
119
+ - spec/action_widget_spec.rb
120
+ - spec/spec_helper.rb
74
121
  has_rdoc: