hydramata-core 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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.hound.yml +818 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +20 -0
  6. data/CONTRIBUTING.md +220 -0
  7. data/Gemfile +34 -0
  8. data/Guardfile +36 -0
  9. data/LICENSE +15 -0
  10. data/README.md +13 -0
  11. data/Rakefile +40 -0
  12. data/bin/rails +12 -0
  13. data/fs +3 -0
  14. data/gemfiles/rails4.1.gemfile +12 -0
  15. data/gemfiles/rails4.gemfile +13 -0
  16. data/hydramata-core.gemspec +35 -0
  17. data/lib/hydramata-core.rb +1 -0
  18. data/lib/hydramata/configuration.rb +17 -0
  19. data/lib/hydramata/core.rb +42 -0
  20. data/lib/hydramata/core/named_callbacks.rb +24 -0
  21. data/lib/hydramata/core/railtie.rb +14 -0
  22. data/lib/hydramata/core/runner.rb +58 -0
  23. data/lib/hydramata/core/translator.rb +72 -0
  24. data/lib/hydramata/core/version.rb +5 -0
  25. data/lib/hydramata/exceptions.rb +4 -0
  26. data/lib/hydramata/services.rb +21 -0
  27. data/lib/hydramata_core.rb +2 -0
  28. data/lib/tasks/hydramata_mecha_tasks.rake +4 -0
  29. data/run_each_spec_in_isolation +11 -0
  30. data/script/analyzer.rb +124 -0
  31. data/script/fast_specs +22 -0
  32. data/spec/README.md +10 -0
  33. data/spec/features/translation_services_spec.rb +35 -0
  34. data/spec/fixtures/core.translations.yml +8 -0
  35. data/spec/lib/hydramata-translations_spec.rb +7 -0
  36. data/spec/lib/hydramata/configuration_spec.rb +38 -0
  37. data/spec/lib/hydramata/core/named_callbacks_spec.rb +30 -0
  38. data/spec/lib/hydramata/core/runner_spec.rb +45 -0
  39. data/spec/lib/hydramata/core/translator_spec.rb +94 -0
  40. data/spec/lib/hydramata/services_spec.rb +27 -0
  41. data/spec/spec_fast_helper.rb +33 -0
  42. metadata +164 -0
@@ -0,0 +1 @@
1
+ require 'hydramata/core'
@@ -0,0 +1,17 @@
1
+ require 'active_support/lazy_load_hooks'
2
+ require 'hydramata/exceptions'
3
+ module Hydramata
4
+ class Configuration
5
+ def translator
6
+ @translator ||= begin
7
+ require 'hydramata/core/translator'
8
+ Core::Translator.new(base_scope: ['hydramata', 'core'])
9
+ end
10
+ end
11
+ def translator=(object)
12
+ raise ConfigurationError.new("Expected #{self.class}#translator to respond_to #translate. #{object.inspect} does not respond to #translate") unless object.respond_to?(:translate)
13
+ @translator = object
14
+ end
15
+ end
16
+ ActiveSupport.run_load_hooks(:hydramata_configuration, Configuration)
17
+ end
@@ -0,0 +1,42 @@
1
+ require 'hydramata/core/railtie' if defined?(Rails)
2
+
3
+ module Hydramata
4
+ if defined?(ActiveSupport::Autoload)
5
+ extend ActiveSupport::Autoload
6
+ autoload :Services
7
+ autoload :Configuration
8
+ end
9
+ class << self
10
+ attr_writer :configuration
11
+
12
+ # @see Configuration
13
+ def configuration
14
+ require 'hydramata/configuration'
15
+ @configuration ||= Configuration.new
16
+ end
17
+ end
18
+
19
+ module_function
20
+
21
+ # @see Configuration
22
+ # @see .configuration
23
+ def configure(&block)
24
+ @configuration_block = block
25
+
26
+ # The Rails load sequence means that some of the configured Targets may
27
+ # not be loaded; As such I am not calling configure! instead relying on
28
+ # Hydra::RemoteIdentifier::Railtie to handle the configure! call
29
+ configure! unless defined?(Rails)
30
+ end
31
+
32
+ def configure!
33
+ if @configuration_block.respond_to?(:call)
34
+ @configuration_block.call(configuration)
35
+ @configuration_block = nil
36
+ end
37
+ end
38
+ private :configure!
39
+
40
+ module Core
41
+ end
42
+ end
@@ -0,0 +1,24 @@
1
+ module Hydramata
2
+ module Core
3
+ # Responsible for registering blocks used for callbacks.
4
+ class NamedCallbacks
5
+ def initialize
6
+ @callbacks = {}
7
+ end
8
+
9
+ def method_missing(callback_name, *args, &block)
10
+ @callbacks[callback_name] = block
11
+ end
12
+
13
+ def respond_to_missing?(callback_name)
14
+ @callbacks[callback_name]
15
+ end
16
+
17
+ def call(callback_name, *args)
18
+ callback_name = callback_name.to_sym
19
+ callback = @callbacks[callback_name]
20
+ callback ? callback.call(*args) : true
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,14 @@
1
+ require 'rails/railtie'
2
+
3
+ module Hydramata
4
+ module Core
5
+ class Railtie < Rails::Railtie
6
+ config.eager_load_namespaces << Hydramata::Core
7
+ config.to_prepare do
8
+ # Because I want allow for other components to plug into Hydramata's
9
+ # configuration. Maybe there is a better way to do this.
10
+ Hydramata.send(:configure!)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,58 @@
1
+ module Hydramata
2
+ module Core
3
+ # A Runner is responsible for performing the guts of what might traditionally
4
+ # be a controller's action.
5
+ #
6
+ # It exists for two reasons:
7
+ # * Controllers are extremely over-worked (parameter negotiation,
8
+ # authentication, authorization, marshalling an object, user messages via
9
+ # flash, and http response determination)
10
+ # * The inner method content of a controller's action is subject to change
11
+ # especially as other implementors look to change the behavior.
12
+ #
13
+ # So, the Runner provides a seem in the code in which you can more readily
14
+ # make changes to the "inner method" of a route. In some ways, I see this as
15
+ # a separation of state change and response; a somewhat analogous separation
16
+ # to the Command/Query separation principle.
17
+ #
18
+ # @See Services
19
+ class Runner
20
+ attr_reader :context
21
+
22
+ def self.run(context,*args,&block)
23
+ new(context, &block).run(*args)
24
+ end
25
+
26
+ def initialize(context, collaborators = {})
27
+ @callbacks = collaborators.fetch(:callbacks) { default_callbacks }
28
+ self.context = context
29
+ yield(@callbacks) if block_given?
30
+ end
31
+
32
+ def callback(name, *args)
33
+ @callbacks.call(name, *args)
34
+ args
35
+ end
36
+
37
+ def services
38
+ context.services
39
+ end
40
+
41
+ def run(*args)
42
+ raise NotImplementedError.new("You must define #{self.class}#run")
43
+ end
44
+ protected
45
+
46
+ def context=(object)
47
+ raise RuntimeError.new("Expected #{object.inspect} to implement #services") unless object.respond_to?(:services)
48
+ @context = object
49
+ end
50
+
51
+ private
52
+ def default_callbacks
53
+ require 'hydramata/core/named_callbacks'
54
+ NamedCallbacks.new
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,72 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+
3
+ module Hydramata
4
+ module Core
5
+ # Responsible for handling the translation via diminishing specificity.
6
+ class Translator
7
+
8
+ class << self
9
+ def translate(key, options = {})
10
+ new(options).translate(key, options)
11
+ end
12
+ alias_method :t, :translate
13
+ end
14
+
15
+ attr_reader :base_scope, :translation_service, :translation_service_error
16
+ def initialize(options = {})
17
+ self.base_scope = options.fetch(:base_scope) { default_base_scope }
18
+ @translation_service = options.fetch(:translation_service) { default_translation_service }
19
+ @translation_service_error = options.fetch(:translation_service_error) { default_translation_service_error }
20
+ end
21
+
22
+ def translate(key, options = {})
23
+ translate_key_for_specific_scopes(key, options) || translate_key_for_general_case(key, options)
24
+ end
25
+ alias_method :t, :translate
26
+
27
+ private
28
+
29
+ def translate_key_for_specific_scopes(key, options = {})
30
+ return nil unless scopes = options[:scopes]
31
+ returning_value = nil
32
+ Array.wrap(scopes).each do |scope|
33
+ begin
34
+ options_to_use = options.merge(scope: base_scope + Array.wrap(scope), raise: true)
35
+ options_to_use.delete(:scopes)
36
+ options_to_use.delete(:default)
37
+ returning_value = translation_service.translate(key, options_to_use)
38
+ break
39
+ rescue *translation_service_error => e
40
+ STDOUT.puts(e) if ENV['DEBUG']
41
+ next
42
+ end
43
+ end
44
+ returning_value
45
+ end
46
+
47
+ def translate_key_for_general_case(key, options = {})
48
+ options_to_use = options.merge(scope: base_scope)
49
+ options_to_use.delete(:scopes)
50
+ translation_service.translate(key, options_to_use)
51
+ end
52
+
53
+ def base_scope=(values)
54
+ @base_scope = Array.wrap(values)
55
+ end
56
+
57
+ def default_base_scope
58
+ ['hydramata', 'core']
59
+ end
60
+
61
+ def default_translation_service_error
62
+ require 'i18n/exceptions'
63
+ I18n::ArgumentError
64
+ end
65
+
66
+ def default_translation_service
67
+ require 'i18n'
68
+ I18n
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,5 @@
1
+ module Hydramata
2
+ module Core
3
+ VERSION = '0.1.0'
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ module Hydramata
2
+ class ConfigurationError < RuntimeError
3
+ end
4
+ end
@@ -0,0 +1,21 @@
1
+ require 'active_support/lazy_load_hooks'
2
+ require 'active_support/dependencies/autoload'
3
+ module Hydramata
4
+ # The Services class is a dumping ground for methods. These methods are
5
+ # exposed to an application's context (i.e. a controller).
6
+ #
7
+ # It is the application's interface between the logic of the controller/
8
+ # request handling and the construction of objects.
9
+ #
10
+ # This class has been constructed such that other engines can inject
11
+ # behavior into Services using the ActiveSupport.on_load method.
12
+ #
13
+ # @see Runner
14
+ class Services
15
+ def initialize(context)
16
+ @context = context
17
+ end
18
+ attr_reader :context
19
+ end
20
+ ActiveSupport.run_load_hooks(:hydramata_services, Services)
21
+ end
@@ -0,0 +1,2 @@
1
+ # A convenience/courtesy file for adopters of this gem.
2
+ require 'hydramata/core'
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :hydramata/work do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby -w
2
+ require 'time'
3
+ require 'rake'
4
+ require 'benchmark'
5
+
6
+ FileList['spec/**/*_spec.rb'].sort.select do |fn|
7
+ puts "rspec #{fn}"
8
+ system "rspec #{fn}"
9
+ end
10
+ system('rspec')
11
+ end
@@ -0,0 +1,124 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ require 'pp'
4
+ require 'sexp_processor'
5
+ require 'ruby_parser'
6
+ require 'byebug'
7
+ require 'rake'
8
+ require 'set'
9
+
10
+ class Collector
11
+ attr_reader :namespace
12
+ def initialize(namespace)
13
+ @namespace = namespace
14
+ @storage = {}
15
+ end
16
+
17
+ def push(class_stack, collaborator)
18
+ klass = register(class_stack)
19
+ @storage[klass] << collaborator
20
+ end
21
+
22
+ def register(class_stack)
23
+ klass = class_stack.reverse[0..-1].collect(&:to_s).join('::')
24
+ @storage[klass] ||= Set.new
25
+ klass
26
+ end
27
+
28
+ def render
29
+ puts %(digraph "G" {)
30
+ puts %( label="#{namespace}") if namespace
31
+ @storage.each do |klass, collaborators|
32
+ if klass.strip != '' || collaborators.any? {|c| c =~ /\A#{namespace}/ }
33
+ puts render_node(klass)
34
+ collaborators.each do |collaborator|
35
+ if @storage.detect do |some_class, _|
36
+ if some_class =~ /\A#{namespace}(::\w+)*::#{collaborator}\Z/
37
+ render_node(some_class)
38
+ render_edge(klass, some_class)
39
+ true
40
+ else
41
+ false
42
+ end
43
+ end
44
+ else
45
+ if collaborator =~ /\A#{namespace}/
46
+ render_node(collaborator)
47
+ render_edge(klass, collaborator)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ puts %(})
54
+ end
55
+
56
+ def render_node(klass)
57
+ puts %( #{to_dot_name(klass)}[label="#{klass}"])
58
+ end
59
+
60
+ def render_edge(from, to)
61
+ puts %( #{to_dot_name(from)} -> #{to_dot_name(to)})
62
+ end
63
+
64
+ def to_dot_name(str)
65
+ str.gsub(/[\W:]+/,'_')
66
+ end
67
+ end
68
+
69
+ class DependencyAnalyzer < MethodBasedSexpProcessor
70
+
71
+ attr_reader :collector
72
+
73
+ def initialize(collector = Collector.new)
74
+ super()
75
+ @collector = collector
76
+ end
77
+
78
+ def process_colon2(exp)
79
+ klass = classify_colon2(exp).join("::")
80
+ collector.push(class_stack, klass)
81
+ return exp
82
+ end
83
+
84
+ def process_module(exp)
85
+ super(exp)
86
+ collector.register(class_stack)
87
+ return exp
88
+ end
89
+
90
+ def process_dfn(exp)
91
+ collector.register(class_stack)
92
+ return exp
93
+ end
94
+
95
+ def process_const(exp)
96
+ collaborator = exp[1]
97
+ collector.push(class_stack, collaborator.to_s)
98
+ return s(:const, collaborator)
99
+ end
100
+
101
+ def classify_colon2(exp, returning = [])
102
+ returning.unshift(exp[2]) if exp[2]
103
+ if exp[1].is_a?(Sexp)
104
+ classify_colon2(exp[1], returning)
105
+ else
106
+ returning.unshift(exp[1])
107
+ end
108
+ return returning
109
+ end
110
+
111
+ end
112
+
113
+ if __FILE__ == $0
114
+ collaborator = Collector.new('Hydramata::Core')
115
+ analyzer = DependencyAnalyzer.new(collaborator)
116
+ FileList['app/**/*.rb', 'lib/**/*.rb'].each do |filename|
117
+ # filename = '/Users/jfriesen/Repositories/hydramata-work/app/parsers/hydramata/core/predicate_parsers/simple_parser.rb'
118
+ file_content = File.read(filename)
119
+ parsed_file = RubyParser.for_current_ruby.parse(file_content)
120
+ # break
121
+ analyzer.process(parsed_file)
122
+ end
123
+ collaborator.render
124
+ end
data/script/fast_specs ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby -w
2
+
3
+ require 'rake'
4
+
5
+ # A helper function for answering: Does this spec run fast?
6
+ module Fast
7
+ module_function
8
+ def spec?(fn)
9
+ open(fn) { |f| f.gets =~ /spec_fast_helper/ }
10
+ end
11
+ end
12
+
13
+ fast_specs = FileList['spec/**/*_spec.rb'].select do |fn|
14
+ Fast.spec?(fn)
15
+ end
16
+
17
+ if fast_specs.any?
18
+ system "rspec #{fast_specs}"
19
+ else
20
+ puts 'Unable to find any fast specs'
21
+ exit(-1)
22
+ end