pincushion 0.0.1 → 0.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8d51bc5faf8cad8112f822aa6b99d40473790720
4
- data.tar.gz: 32bc18b5c133c5862ed9a10b0f9f7e75e33287f3
3
+ metadata.gz: 8eab64e433918cb3f0881c57abb7d09a4dd1bfac
4
+ data.tar.gz: 4ca030d28f2cf4ef8b270d9a47e116cdf4a1e314
5
5
  SHA512:
6
- metadata.gz: 32db6265a455714d8e50303ca644129632d7e510d368fc0ab161770bc72f5eed14e47a95f4ae2c5b67f5f95fba51c9a01e5de4cf1b439dd540ba66f16c20df4b
7
- data.tar.gz: a5abddff7882701b6e681aa29b406dfad111450a4f24eacf2057215746ca766cdd06eb13867d87187c32c8ecfc682bbd4229478720e7c0d93ce3208acf480603
6
+ metadata.gz: cb77a2fd89d401c263b68ab334ee3a5eeeefa816d9578be94bb537b800c7d7abaad7169c30dbb7ce44a8dfd1637699c9d9d895e46324f0a851396382c29f6a61
7
+ data.tar.gz: 1b6df11775ecd620373e87767ae3e1c927541a0d9d0d145c0b56262ff4e2d357e09b151102563bf61866fdaaa4da8580a2e7d689526ec3ec40fc622f47e44c71
@@ -2,18 +2,42 @@ require 'set'
2
2
  require 'pincushion/plugins'
3
3
 
4
4
  module Pincushion
5
+ def self.from_rows(rows)
6
+ Module.new.module_eval do
7
+ include Pincushion
8
+
9
+ predicates = rows
10
+ .reduce(Set.new) { |a, e| a.merge(e.keys) }
11
+ .delete(:identifier)
12
+ predicates(*predicates)
13
+
14
+ rows.each do |row|
15
+ row = row.dup
16
+ identifier = row.delete(:identifier)
17
+ true_predicates = row.each_pair.select { |_, v| v }.map { |k, _| k }
18
+ that_is(*true_predicates).named(identifier)
19
+ end
20
+
21
+ self
22
+ end
23
+ end
24
+
25
+ def self.validate(mod, preds)
26
+ violations = Set.new(preds) - mod.predicates
27
+ return if violations.empty?
28
+ fail MissingPredicateError,
29
+ "Tried to set a value for unregistered predicate(s): "\
30
+ "#{violations.inspect}"
31
+ end
32
+
5
33
  module RootModuleMethods
6
34
  include Plugins
7
35
 
8
36
  attr_reader :identifiers
9
37
 
10
- def all_identifiers_predicates_hashes
11
- identifiers.map { |k, v| [k, v.all_predicates_hash] }.to_h
12
- end
13
-
14
- def all_identifiers_predicates_rows
15
- identifiers.flat_map do |id, cls|
16
- cls.new(id).all_predicates_hash.map { |k, v| [id, k, v] }
38
+ def rows
39
+ identifiers.map do |id, cls|
40
+ { identifier: id }.merge(cls.new(id).all_predicates_hash)
17
41
  end
18
42
  end
19
43
 
@@ -26,9 +50,9 @@ module Pincushion
26
50
  end
27
51
 
28
52
  def predicates(*preds)
29
- return @predicates if preds.empty?
53
+ return (@predicates || Set.new) if preds.empty?
30
54
  fail "Predicates can't be changed after initialization" if @predicates
31
- @predicates = Set.new preds
55
+ @predicates = Set.new(preds)
32
56
  is_not(*@predicates)
33
57
  @predicates.each do |predicate|
34
58
  alias_method(:"is_#{predicate}?", :"#{predicate}?")
@@ -53,11 +77,13 @@ module Pincushion
53
77
  end
54
78
 
55
79
  def is(*preds)
80
+ Pincushion.validate(predicate_pincushion_root, preds)
56
81
  preds.each { |pred| define_method(:"#{pred}?") { true } }
57
82
  self
58
83
  end
59
84
 
60
85
  def is_not(*preds)
86
+ Pincushion.validate(predicate_pincushion_root, preds)
61
87
  preds.each { |pred| define_method(:"#{pred}?") { false } }
62
88
  self
63
89
  end
@@ -76,6 +102,7 @@ module Pincushion
76
102
  end
77
103
 
78
104
  def that_are(*preds)
105
+ Pincushion.validate(self, preds)
79
106
  mod = Module.new
80
107
  mod.include self
81
108
  mod.is(*preds)
@@ -83,6 +110,7 @@ module Pincushion
83
110
  end
84
111
 
85
112
  def that_is(*preds)
113
+ Pincushion.validate(self, preds)
86
114
  klass = Class.new
87
115
  klass.include self
88
116
  klass.is(*preds)
@@ -1,4 +1,20 @@
1
1
  module Pincushion
2
+ def self.plugins
3
+ @plugins || []
4
+ end
5
+
6
+ def self.plugin(name)
7
+ require "pincushion/plugins/#{name}"
8
+ mod = Plugins.const_get(Plugins.camelize(name))
9
+ extend mod::PincushionMethods if defined?(mod::PincushionMethods)
10
+ @plugins ||= []
11
+ @plugins << name
12
+ rescue LoadError
13
+ raise LoadError, "Plugin not found: #{name}"
14
+ rescue NameError => e
15
+ raise NameError, "#{e} for plugin: #{name}"
16
+ end
17
+
2
18
  module Plugins
3
19
  def self.camelize(string)
4
20
  string.to_s
@@ -6,12 +22,16 @@ module Pincushion
6
22
  .gsub(/(^|_)(.)/) { Regexp.last_match(2).upcase }
7
23
  end
8
24
 
25
+ attr_reader :plugins
26
+
9
27
  def plugin(name)
10
28
  require "pincushion/plugins/#{name}"
11
29
  mod = Plugins.const_get(Plugins.camelize(name))
12
30
  extend mod::ModuleMethods if defined?(mod::ModuleMethods)
13
31
  extend mod::RootModuleMethods if defined?(mod::RootModuleMethods)
14
32
  include mod::InstanceMethods if defined?(mod::InstanceMethods)
33
+ @plugins ||= []
34
+ @plugins << name
15
35
  rescue LoadError
16
36
  raise LoadError, "Plugin not found: #{name}"
17
37
  rescue NameError => e
@@ -1,12 +1,29 @@
1
1
  require 'csv'
2
2
 
3
3
  module Pincushion
4
+ def self.from_csv(csv, *args)
5
+ args << {} unless args.last.is_a?(Hash)
6
+ args.last.merge!(Plugins::CsvSerializer::OPTS)
7
+ from_rows(CSV.parse(csv, *args).map(&:to_h))
8
+ end
9
+
4
10
  module Plugins
5
11
  module CsvSerializer
12
+ OPTS = {
13
+ headers: true,
14
+ header_converters: :symbol,
15
+ converters: lambda { |string|
16
+ case string
17
+ when "true" then true
18
+ when "false" then false
19
+ else string
20
+ end
21
+ }
22
+ }.freeze
23
+
6
24
  module RootModuleMethods
7
25
  def to_csv(*args, **kwargs)
8
- rows = all_identifiers_predicates_rows
9
- kwargs[:headers] ||= [:identifier, :predicate, :value]
26
+ kwargs[:headers] ||= [:identifier, *predicates]
10
27
 
11
28
  CSV.generate(*args, **kwargs) do |csv|
12
29
  rows.each { |row| csv << row }
@@ -1,11 +1,19 @@
1
- require 'multi_json'
2
-
3
1
  module Pincushion
4
2
  module Plugins
5
3
  module JsonSerializer
4
+ OPTS = { symbolize_names: true }
5
+
6
+ module PincushionMethods
7
+ def from_json(json, *args)
8
+ args << {} unless args.last.is_a?(Hash)
9
+ args.last.merge!(OPTS)
10
+ from_rows(JSON.parse(json, *args))
11
+ end
12
+ end
13
+
6
14
  module RootModuleMethods
7
15
  def to_json(*args)
8
- MultiJson.dump(all_identifiers_predicates_hashes, *args)
16
+ JSON.dump(rows, *args)
9
17
  end
10
18
  end # module RootModuleMethods
11
19
  end # module JsonSerializer
@@ -1,5 +1,3 @@
1
1
  module Pincushion
2
- def self.version
3
- '0.0.1'
4
- end
2
+ VERSION = "0.1.0"
5
3
  end # module Pincushion
@@ -0,0 +1,95 @@
1
+ require "spec_helper"
2
+ require "json"
3
+ require "pincushion"
4
+ require "pincushion/version"
5
+
6
+ describe Pincushion do
7
+ let(:klass) { Pincushion }
8
+
9
+ let(:animal_data) do
10
+ [
11
+ { identifier: "Mee-mow", carnivore: true, herbivore: false },
12
+ { identifier: "Jake", carnivore: true, herbivore: true },
13
+ { identifier: "B-Mo", carnivore: false, herbivore: false }
14
+ ]
15
+ end
16
+
17
+ let(:animal_data_csv) do
18
+ "identifier,carnivore,herbivore\n"\
19
+ "Mee-mow,true,false\n"\
20
+ "Jake,true,true\n"\
21
+ "B-Mo,false,false\n"
22
+ end
23
+
24
+ let(:animal_data_json) do
25
+ "[{\"identifier\":\"Mee-mow\",\"carnivore\":true,\"herbivore\":false},"\
26
+ "{\"identifier\":\"Jake\",\"carnivore\":true,\"herbivore\":true},"\
27
+ "{\"identifier\":\"B-Mo\",\"carnivore\":false,\"herbivore\":false}]"
28
+ end
29
+
30
+ it "has a version number" do
31
+ refute_nil ::Pincushion::VERSION
32
+ end
33
+
34
+ it "can be used to define a category with properties" do
35
+ animals = Module.new
36
+ animals.include Pincushion
37
+ animals.predicates :carnivore, :herbivore
38
+ cat = animals.that_is(:carnivore).named("Mee-mow")
39
+ mittens = animals.find("Mee-mow")
40
+ assert_equal mittens.class, cat
41
+ assert mittens.carnivore?
42
+ refute mittens.herbivore?
43
+ end
44
+
45
+ it "doesn't allow unregistered predicates" do
46
+ animals = Module.new
47
+ animals.include Pincushion
48
+ animals.predicates :herbivore
49
+
50
+ assert_raises Pincushion::MissingPredicateError do
51
+ animals.that_is(:carnivore)
52
+ end
53
+
54
+ assert_raises Pincushion::MissingPredicateError do
55
+ cat = animals.that_is
56
+ cat.is(:carnivore)
57
+ end
58
+
59
+ assert_raises Pincushion::MissingPredicateError do
60
+ cat = animals.that_is
61
+ cat.is_not(:carnivore)
62
+ end
63
+
64
+ assert_raises Pincushion::MissingPredicateError do
65
+ animals.that_are(:carnivore)
66
+ end
67
+ end
68
+
69
+ it "doesn't allow unregistered predicates (empty set)" do
70
+ animals = Module.new
71
+ animals.include Pincushion
72
+ assert_raises Pincushion::MissingPredicateError do
73
+ animals.that_is(:carnivore)
74
+ end
75
+ end
76
+
77
+ it "round-trips to and from arrays of hashes" do
78
+ animals = Pincushion.from_rows(animal_data)
79
+ assert_equal animals.rows, animal_data
80
+ end
81
+
82
+ it "round-trips CSV with the csv_serializer plugin" do
83
+ Pincushion.plugin :csv_serializer
84
+ animals = Pincushion.from_csv(animal_data_csv)
85
+ animals.plugin :csv_serializer
86
+ assert_equal animals.to_csv(write_headers: true), animal_data_csv
87
+ end
88
+
89
+ it "round-trips JSON with the json_serializer plugin" do
90
+ Pincushion.plugin :json_serializer
91
+ animals = Pincushion.from_json(animal_data_json)
92
+ animals.plugin :json_serializer
93
+ assert_equal animals.to_json, animal_data_json
94
+ end
95
+ end
@@ -0,0 +1,4 @@
1
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
2
+
3
+ require "minitest/spec"
4
+ require "minitest/autorun"
metadata CHANGED
@@ -1,29 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pincushion
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Miller
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-04 00:00:00.000000000 Z
11
+ date: 2016-02-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rspec
14
+ name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '3.0'
19
+ version: '1.0'
20
20
  type: :development
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: '3.0'
26
+ version: '1.0'
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: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
27
55
  description:
28
56
  email: bmiller@rackspace.com
29
57
  executables: []
@@ -35,6 +63,8 @@ files:
35
63
  - lib/pincushion/plugins/csv_serializer.rb
36
64
  - lib/pincushion/plugins/json_serializer.rb
37
65
  - lib/pincushion/version.rb
66
+ - spec/pincushion_spec.rb
67
+ - spec/spec_helper.rb
38
68
  homepage: https://github.com/bjmllr/pincushion
39
69
  licenses:
40
70
  - MIT
@@ -55,8 +85,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
55
85
  version: '0'
56
86
  requirements: []
57
87
  rubyforge_project:
58
- rubygems_version: 2.4.5.1
88
+ rubygems_version: 2.5.1
59
89
  signing_key:
60
90
  specification_version: 4
61
91
  summary: Predicate-centric classification toolkit
62
- test_files: []
92
+ test_files:
93
+ - spec/pincushion_spec.rb
94
+ - spec/spec_helper.rb
95
+ has_rdoc: