material 0.2.2 → 0.2.3
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 +4 -4
- data/LICENSE.txt +21 -0
- data/README.md +49 -0
- data/lib/material/base.rb +16 -0
- data/lib/material/concerns/components.rb +79 -0
- data/lib/material/concerns/core.rb +31 -0
- data/lib/material/concerns/for.rb +16 -0
- data/lib/material/concerns/icon.rb +12 -0
- data/lib/material/concerns/page.rb +13 -0
- data/lib/material/concerns/text.rb +55 -0
- data/lib/material/list.rb +23 -0
- data/lib/material/rspec/custom_matchers/have_material_component.rb +49 -0
- data/lib/material/rspec/custom_matchers.rb +3 -0
- data/lib/material/spec_helper.rb +3 -0
- data/lib/material/version.rb +5 -0
- data/lib/material.rb +19 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '021836dc0d87cd597826b893a04c6a95640fbccfc2f289c518624e19a962fb38'
|
4
|
+
data.tar.gz: d6afc3531a65a9d63e6570f9cc24d63219c23ed0c8154a44062e0924df6f0509
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a13e04ba8415a4c8e48ab3adf31e935c2de502a967cd7f9c616668e58719ba597eea81b4f5f2ff27813704ebc0bfd86bdfc4e3a99d8dc44528ec46d5f814eb3a
|
7
|
+
data.tar.gz: 5b67cf7d691fd845a8dd704eb3bb55f7c39400d35d5d566fcfd7523232dd386fec217e2938d59407bd237a5488fde655008846f8a08d21cb7d3e51fd03565743
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2019 Eric Garside
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Material
|
2
|
+
|
3
|
+
An extensible and lightweight object which defines how your objects are viewed.
|
4
|
+
|
5
|
+
[](https://badge.fury.io/rb/material)
|
6
|
+
[](https://semaphoreci.com/freshly/material)
|
7
|
+
[](https://codeclimate.com/github/Freshly/material/maintainability)
|
8
|
+
[](https://codeclimate.com/github/Freshly/material/test_coverage)
|
9
|
+
|
10
|
+
* [Material](#material)
|
11
|
+
* [Installation](#installation)
|
12
|
+
* [Usage](#usage)
|
13
|
+
* [Development](#development)
|
14
|
+
* [Contributing](#contributing)
|
15
|
+
* [License](#license)
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Add this line to your application's Gemfile:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
gem 'material'
|
23
|
+
```
|
24
|
+
|
25
|
+
And then execute:
|
26
|
+
|
27
|
+
$ bundle
|
28
|
+
|
29
|
+
Or install it yourself as:
|
30
|
+
|
31
|
+
$ gem install material
|
32
|
+
|
33
|
+
## Usage
|
34
|
+
|
35
|
+
TODO: Write usage instructions here
|
36
|
+
|
37
|
+
## Development
|
38
|
+
|
39
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
40
|
+
|
41
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
42
|
+
|
43
|
+
## Contributing
|
44
|
+
|
45
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/Freshly/material.
|
46
|
+
|
47
|
+
## License
|
48
|
+
|
49
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Material
|
4
|
+
class Base < Spicerack::AttributeObject
|
5
|
+
include Material::Components
|
6
|
+
include Material::Core
|
7
|
+
include Material::Icon
|
8
|
+
include Material::Text
|
9
|
+
include Material::Page
|
10
|
+
include Material::For
|
11
|
+
|
12
|
+
def self.for(object)
|
13
|
+
material_class_for(object, "Material")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Components are value objects which abstract the storage of configuration data for various parts of a material.
|
4
|
+
module Material
|
5
|
+
module Components
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
class_attribute :_components, instance_writer: false, default: {}
|
10
|
+
end
|
11
|
+
|
12
|
+
class_methods do
|
13
|
+
def inherited(base)
|
14
|
+
dup = _components.dup
|
15
|
+
base._components = dup.each { |k, v| dup[k] = v.dup }
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def register_component(key, **options, &block)
|
22
|
+
_components[key] = Component.new(**options, &block)
|
23
|
+
define_component_configurator(key)
|
24
|
+
define_component_reader(key)
|
25
|
+
define_component_value_reader(key)
|
26
|
+
define_component_predicate(key)
|
27
|
+
end
|
28
|
+
|
29
|
+
def define_component_configurator(key)
|
30
|
+
define_singleton_method("define_#{key}".to_sym) do |**options, &block|
|
31
|
+
_components[key].configure(**options, &block)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def define_component_reader(key)
|
36
|
+
method_name = "#{key}_component".to_sym
|
37
|
+
define_singleton_method(method_name) { _components[key] }
|
38
|
+
delegate method_name, to: :class
|
39
|
+
end
|
40
|
+
|
41
|
+
def define_component_value_reader(key)
|
42
|
+
method_name = "#{key}_value".to_sym
|
43
|
+
define_method(method_name) { _components[key].value_for(self) }
|
44
|
+
memoize method_name
|
45
|
+
alias_method key, method_name unless method_defined?(key)
|
46
|
+
end
|
47
|
+
|
48
|
+
def define_component_predicate(key)
|
49
|
+
method_name = "#{key}_value?".to_sym
|
50
|
+
define_method(method_name) { public_send("#{key}_value".to_sym).present? }
|
51
|
+
alias_method "#{key}?".to_sym, method_name
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Component
|
56
|
+
attr_reader :options
|
57
|
+
|
58
|
+
def initialize(**options, &block)
|
59
|
+
@options = {}
|
60
|
+
configure(**options, &block)
|
61
|
+
end
|
62
|
+
|
63
|
+
def value_for(object)
|
64
|
+
object.instance_eval(&@value).dup if @value.respond_to?(:call)
|
65
|
+
end
|
66
|
+
|
67
|
+
def configure(**opts, &block)
|
68
|
+
@value = block if block_given?
|
69
|
+
options.merge!(**opts)
|
70
|
+
end
|
71
|
+
|
72
|
+
def initialize_copy(other)
|
73
|
+
super
|
74
|
+
@options = @options.dup
|
75
|
+
@value = @value.dup if @value.present?
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Material is a component framework for defining the presentation layer of an application
|
4
|
+
module Material
|
5
|
+
module Core
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
attr_reader :source
|
10
|
+
|
11
|
+
delegate :name, to: :class, prefix: true
|
12
|
+
|
13
|
+
delegate_missing_to :source
|
14
|
+
end
|
15
|
+
|
16
|
+
class_methods do
|
17
|
+
private
|
18
|
+
|
19
|
+
def references(field, as:, from: :source)
|
20
|
+
delegate field, to: from
|
21
|
+
alias_method as, field
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(object)
|
26
|
+
raise ArgumentError, "source is required" if object.nil?
|
27
|
+
|
28
|
+
@source = object
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Build materials from objects based either an explicit definition or an optimistic lookup
|
4
|
+
module Material
|
5
|
+
module For
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
LOOKUP_NAME = :material_class
|
9
|
+
|
10
|
+
class_methods do
|
11
|
+
def material_class_for(object, subtype)
|
12
|
+
object.try(LOOKUP_NAME) || object.class.try(LOOKUP_NAME) || "#{object.class.name}#{subtype}".safe_constantize
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Material
|
4
|
+
module Text
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
DEFAULT_TRUNCATE_LENGTH = 50
|
8
|
+
|
9
|
+
class_methods do
|
10
|
+
private
|
11
|
+
|
12
|
+
def register_title_truncator(key)
|
13
|
+
register_truncator(key) { title }
|
14
|
+
end
|
15
|
+
|
16
|
+
def register_truncator(key, max_length = DEFAULT_TRUNCATE_LENGTH, &block)
|
17
|
+
register_component(key, max_length: max_length, &block)
|
18
|
+
define_truncation_formatter(key)
|
19
|
+
define_truncation_predicate(key)
|
20
|
+
end
|
21
|
+
|
22
|
+
def define_truncation_formatter(key)
|
23
|
+
define_method(key) do
|
24
|
+
public_send("#{key}_value".to_sym).truncate(public_send("#{key}_component".to_sym).options[:max_length])
|
25
|
+
end
|
26
|
+
memoize key
|
27
|
+
end
|
28
|
+
|
29
|
+
def define_truncation_predicate(key)
|
30
|
+
method_name = "#{key}_truncated?".to_sym
|
31
|
+
define_method(method_name) do
|
32
|
+
public_send("#{key}_value".to_sym).length > public_send("#{key}_component".to_sym).options[:max_length]
|
33
|
+
end
|
34
|
+
memoize method_name
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
included do
|
39
|
+
register_truncator(:title) { default_title }
|
40
|
+
|
41
|
+
register_component(:parameterized_title) { title_value.underscore.parameterize }
|
42
|
+
|
43
|
+
register_title_truncator :list_title
|
44
|
+
register_title_truncator :header_title
|
45
|
+
register_title_truncator :reference_title
|
46
|
+
register_title_truncator :breadcrumb_title
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def default_title
|
51
|
+
source.try(:name) || self.class.try(:model_name)&.human || self.class.name
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "concerns/components"
|
4
|
+
require_relative "concerns/icon"
|
5
|
+
require_relative "concerns/text"
|
6
|
+
|
7
|
+
module Material
|
8
|
+
class List < Collectible::CollectionBase
|
9
|
+
include Material::Components
|
10
|
+
include Material::Icon
|
11
|
+
include Material::Text
|
12
|
+
include Material::Page
|
13
|
+
include Material::For
|
14
|
+
|
15
|
+
def self.for(object)
|
16
|
+
material_class_for(object, "List")
|
17
|
+
end
|
18
|
+
|
19
|
+
def default_title
|
20
|
+
(item_class.try(:model_name)&.human || item_class&.name).pluralize || self.class.name
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# RSpec matcher that tests usages of `.register_component`
|
4
|
+
#
|
5
|
+
# class ExampleMaterial < ApplicationMaterial
|
6
|
+
# register_component :foo
|
7
|
+
# register_component :bar, max_length: 10
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# RSpec.describe ExampleMaterial do
|
11
|
+
# it { is_expected.to have_material_component :foo }
|
12
|
+
# it { is_expected.to have_material_component(:bar).with_options(max_length: 10) }
|
13
|
+
# end
|
14
|
+
|
15
|
+
RSpec::Matchers.define :have_material_component do |component_name|
|
16
|
+
match do
|
17
|
+
@component = test_subject._components[component_name]
|
18
|
+
|
19
|
+
@component.present? && @component.instance_of?(Material::Components::Component) && options_matching?
|
20
|
+
end
|
21
|
+
|
22
|
+
def options_matching?
|
23
|
+
if @options.present?
|
24
|
+
@options.keys.all? { |option| @component.options[option] == @options[option] }
|
25
|
+
else
|
26
|
+
true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
chain :with_options do |options|
|
31
|
+
@options = options
|
32
|
+
end
|
33
|
+
|
34
|
+
description do
|
35
|
+
"have material component #{component_name}#{" with options #{@options}" if @options.present?}"
|
36
|
+
end
|
37
|
+
|
38
|
+
failure_message do
|
39
|
+
"expected to have material component #{component_name}#{" with options #{@options}" if @options.present?}"
|
40
|
+
end
|
41
|
+
|
42
|
+
failure_message_when_negated do
|
43
|
+
"not expected to have material component #{component_name}#{" with options #{@options}" if @options.present?}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_subject
|
47
|
+
subject.is_a?(Class) ? subject : subject.class
|
48
|
+
end
|
49
|
+
end
|
data/lib/material.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support"
|
4
|
+
|
5
|
+
require "spicerack"
|
6
|
+
|
7
|
+
require_relative "material/concerns/components"
|
8
|
+
require_relative "material/concerns/core"
|
9
|
+
require_relative "material/concerns/icon"
|
10
|
+
require_relative "material/concerns/text"
|
11
|
+
require_relative "material/concerns/page"
|
12
|
+
require_relative "material/concerns/for"
|
13
|
+
|
14
|
+
require "material/version"
|
15
|
+
|
16
|
+
require "material/base"
|
17
|
+
require "material/list"
|
18
|
+
|
19
|
+
module Material; end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: material
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Garside
|
@@ -203,7 +203,22 @@ email:
|
|
203
203
|
executables: []
|
204
204
|
extensions: []
|
205
205
|
extra_rdoc_files: []
|
206
|
-
files:
|
206
|
+
files:
|
207
|
+
- LICENSE.txt
|
208
|
+
- README.md
|
209
|
+
- lib/material.rb
|
210
|
+
- lib/material/base.rb
|
211
|
+
- lib/material/concerns/components.rb
|
212
|
+
- lib/material/concerns/core.rb
|
213
|
+
- lib/material/concerns/for.rb
|
214
|
+
- lib/material/concerns/icon.rb
|
215
|
+
- lib/material/concerns/page.rb
|
216
|
+
- lib/material/concerns/text.rb
|
217
|
+
- lib/material/list.rb
|
218
|
+
- lib/material/rspec/custom_matchers.rb
|
219
|
+
- lib/material/rspec/custom_matchers/have_material_component.rb
|
220
|
+
- lib/material/spec_helper.rb
|
221
|
+
- lib/material/version.rb
|
207
222
|
homepage: https://github.com/Freshly/material
|
208
223
|
licenses:
|
209
224
|
- MIT
|