rubiks 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. data/CHANGELOG.md +12 -12
  2. data/Gemfile +2 -0
  3. data/README.md +90 -72
  4. data/lib/rubiks/calculated_measure.rb +26 -0
  5. data/lib/rubiks/cube.rb +80 -0
  6. data/lib/rubiks/dimension.rb +48 -0
  7. data/lib/rubiks/hierarchy.rb +46 -0
  8. data/lib/rubiks/jars/commons-collections.jar +0 -0
  9. data/lib/rubiks/jars/commons-dbcp.jar +0 -0
  10. data/lib/rubiks/jars/commons-logging.jar +0 -0
  11. data/lib/rubiks/jars/commons-math.jar +0 -0
  12. data/lib/rubiks/jars/commons-pool.jar +0 -0
  13. data/lib/rubiks/jars/commons-vfs.jar +0 -0
  14. data/lib/rubiks/jars/eigenbase-properties.jar +0 -0
  15. data/lib/rubiks/jars/eigenbase-resgen.jar +0 -0
  16. data/lib/rubiks/jars/eigenbase-xom.jar +0 -0
  17. data/lib/rubiks/jars/log4j.jar +0 -0
  18. data/lib/rubiks/jars/mondrian.jar +0 -0
  19. data/lib/rubiks/jars/olap4j-1.2.0-SNAPSHOT.jar +0 -0
  20. data/lib/rubiks/jars/olap4j-xmla-1.2.0-SNAPSHOT.jar +0 -0
  21. data/lib/rubiks/jars/olap4j-xmla.jar.orig +0 -0
  22. data/lib/rubiks/jars/olap4j-xmlaserver-0.0.1-SNAPSHOT.jar +0 -0
  23. data/lib/rubiks/jars/olap4j.jar.orig +0 -0
  24. data/lib/rubiks/jars/postgresql-9.2-1002.jdbc3.jar +0 -0
  25. data/lib/rubiks/jars/postgresql-9.2-1002.jdbc4.jar +0 -0
  26. data/lib/rubiks/level.rb +57 -0
  27. data/lib/rubiks/measure.rb +28 -0
  28. data/lib/rubiks/mondrian/cell_set.rb +148 -0
  29. data/lib/rubiks/mondrian/connection.rb +131 -0
  30. data/lib/rubiks/mondrian/errors.rb +56 -0
  31. data/lib/rubiks/mondrian/jars/commons-collections-3.2.jar +0 -0
  32. data/lib/rubiks/mondrian/jars/commons-dbcp-1.2.2.jar +0 -0
  33. data/lib/rubiks/mondrian/jars/commons-logging-1.1.1.jar +0 -0
  34. data/lib/rubiks/mondrian/jars/commons-math-1.2.jar +0 -0
  35. data/lib/rubiks/mondrian/jars/commons-pool-1.4.jar +0 -0
  36. data/lib/rubiks/mondrian/jars/eigenbase-properties-1.1.2.jar +0 -0
  37. data/lib/rubiks/mondrian/jars/eigenbase-resgen-1.3.1.jar +0 -0
  38. data/lib/rubiks/mondrian/jars/eigenbase-xom-1.3.1.jar +0 -0
  39. data/lib/rubiks/mondrian/jars/log4j-1.2.17.jar +0 -0
  40. data/lib/rubiks/mondrian/jars/mondrian-4.0.0-SNAPSHOT.jar +0 -0
  41. data/lib/rubiks/mondrian/jars/olap4j-1.2.0-SNAPSHOT.jar +0 -0
  42. data/lib/rubiks/mondrian/jars/olap4j-xmla-1.2.0-SNAPSHOT.jar +0 -0
  43. data/lib/rubiks/mondrian/jars/olap4j-xmlaserver-0.0.1-SNAPSHOT.jar +0 -0
  44. data/lib/rubiks/mondrian/member.rb +107 -0
  45. data/lib/rubiks/mondrian.rb +44 -0
  46. data/lib/rubiks/named_object.rb +121 -0
  47. data/lib/rubiks/schema.rb +28 -0
  48. data/lib/rubiks/version.rb +1 -1
  49. data/lib/rubiks.rb +28 -4
  50. data/rubiks.gemspec +2 -1
  51. data/spec/examples/simple_mondrian_schema_spec.rb +56 -0
  52. data/spec/rubiks/calculated_measure_spec.rb +12 -0
  53. data/spec/rubiks/cube_spec.rb +29 -0
  54. data/spec/rubiks/dimension_spec.rb +12 -0
  55. data/spec/rubiks/hierarchy_spec.rb +15 -0
  56. data/spec/rubiks/level_spec.rb +20 -0
  57. data/spec/rubiks/measure_spec.rb +11 -0
  58. data/spec/rubiks/schema_spec.rb +32 -0
  59. data/spec/spec_helper.rb +10 -3
  60. data/spec/support/shared_examples.rb +12 -0
  61. metadata +85 -47
  62. data/lib/rubiks/examples.rb +0 -61
  63. data/lib/rubiks/nodes/annotated_node.rb +0 -26
  64. data/lib/rubiks/nodes/calculated_member.rb +0 -81
  65. data/lib/rubiks/nodes/cube.rb +0 -149
  66. data/lib/rubiks/nodes/dimension.rb +0 -72
  67. data/lib/rubiks/nodes/hierarchy.rb +0 -100
  68. data/lib/rubiks/nodes/level.rb +0 -133
  69. data/lib/rubiks/nodes/measure.rb +0 -75
  70. data/lib/rubiks/nodes/schema.rb +0 -72
  71. data/lib/rubiks/nodes/validated_node.rb +0 -79
  72. data/spec/examples/mondrian_docs_example_spec.rb +0 -108
  73. data/spec/examples/simple_mondrian_example_spec.rb +0 -84
  74. data/spec/rubiks/nodes/annotated_node_spec.rb +0 -24
  75. data/spec/rubiks/nodes/calculated_member_spec.rb +0 -50
  76. data/spec/rubiks/nodes/cube_spec.rb +0 -63
  77. data/spec/rubiks/nodes/dimension_spec.rb +0 -41
  78. data/spec/rubiks/nodes/hierarchy_spec.rb +0 -48
  79. data/spec/rubiks/nodes/level_spec.rb +0 -93
  80. data/spec/rubiks/nodes/measure_spec.rb +0 -55
  81. data/spec/rubiks/nodes/schema_spec.rb +0 -38
  82. data/spec/rubiks/nodes/validated_node_spec.rb +0 -49
  83. data/spec/support/examples/mondrian_docs.yml +0 -36
  84. data/spec/support/examples/shared_dimensions.yml +0 -45
  85. data/spec/support/matchers/be_like.rb +0 -24
  86. data/spec/support/schema_context.rb +0 -59
@@ -0,0 +1,148 @@
1
+ require 'rubiks/mondrian/member'
2
+
3
+ module ::Rubiks
4
+ module Mondrian
5
+
6
+ class CellSet
7
+ def initialize(raw_cell_set)
8
+ @raw_cell_set = raw_cell_set
9
+ end
10
+
11
+ def to_s
12
+ "#<#{self.class}>"
13
+ end
14
+
15
+ def inspect
16
+ axes_strings = []
17
+ axes_strings << "column_axis=#{axis_string(column_axis)}"
18
+ axes_strings << "row_axis=#{axis_string(row_axis)}" if row_axis
19
+ axes_strings << "filter_axis=#{axis_string(filter_axis)}" if filter_axis
20
+
21
+ "#<#{self.class} cube=#{cube_name} #{axes_strings.join(' ')}>"
22
+ end
23
+
24
+ def to_hash
25
+ {
26
+ :cube_name => cube_name,
27
+ :cells => cells,
28
+ :axes => result_axes,
29
+ :filter_axis => filter_axis
30
+ }.delete_if{ |k,v| v.blank? }
31
+ end
32
+
33
+ def cells
34
+ raw_cells.flatten
35
+ end
36
+
37
+ def cube_name
38
+ @raw_cell_set.meta_data.cube.name
39
+ end
40
+
41
+ def filter_axis
42
+ generate_axis(@raw_cell_set.filter_axis)
43
+ end
44
+
45
+ def column_axis
46
+ result_axes.first
47
+ end
48
+
49
+ def row_axis
50
+ result_axes[1]
51
+ end
52
+
53
+ def axes
54
+ @axes ||= @raw_cell_set.getAxes
55
+ end
56
+
57
+ def result_axes
58
+ axes.map{ |raw_axis| generate_axis(raw_axis) }
59
+ end
60
+
61
+ def axis_string(axis)
62
+ axis[:tuples].map do |tuple|
63
+ members = tuple[:members].map{ |member| member[:path] }.join(',')
64
+ end.join('|')
65
+ end
66
+
67
+ def generate_axis(axis)
68
+ tuples = axis.getPositions.map do |position|
69
+
70
+ members = position.getMembers.map do |raw_member|
71
+ member = ::Rubiks::Mondrian::Member.new(raw_member)
72
+
73
+ names = [raw_member.level.name]
74
+ names << raw_member.level.hierarchy.name.split('.').last
75
+ names << raw_member.level.hierarchy.dimension.name
76
+
77
+ level_unique_name = names.reverse.flatten.map{ |str| "[#{str}]" }.join('.')
78
+ member_unique_name = "#{level_unique_name}.[#{raw_member.name}]"
79
+
80
+ path_name = '/' + raw_member.unique_name.gsub(/\]\.\[/, '/')[1...-1]
81
+
82
+ {
83
+ :name => raw_member.name,
84
+ :unique_name => generate_unique_name(member_unique_name),
85
+ :level_name => generate_unique_name(level_unique_name),
86
+ :path => path_name,
87
+ :level_depth => raw_member.depth,
88
+ :child_count => member.children.length,
89
+ :is_all_member => member.all_member?,
90
+ :is_drillable => member.drillable?
91
+ }
92
+ end
93
+
94
+ members.compact!
95
+
96
+ if members.present?
97
+ {:members => members}
98
+ end
99
+ end
100
+
101
+ tuples.compact!
102
+
103
+ if tuples.present?
104
+ {
105
+ :name => axis.axis_ordinal.name,
106
+ :tuples => tuples
107
+ }
108
+ else
109
+ nil
110
+ end
111
+ end
112
+
113
+ # [Date.YQMD] - Mondrian's hierarchy
114
+ # [Logins].[Date].[YQMD] - needed output
115
+ def generate_unique_name(string)
116
+ cube_name = @raw_cell_set.meta_data.cube.name
117
+ dimension_hierarchy_regexp = /\[([^\]]+)\.([^\]]+)\]/
118
+ formatted_string = string.
119
+ gsub(dimension_hierarchy_regexp, '[\1].[\2]').
120
+ gsub(/\[Measures\]\.\[Measures\]/, '[Measures]').
121
+ gsub(/\.\[MeasuresLevel\]/, '')
122
+
123
+ "[#{cube_name}].#{formatted_string}"
124
+ end
125
+
126
+ def raw_cells
127
+ axes_sequence = (0...axes.size).to_a.reverse
128
+ recursive_values(axes_sequence, 0)
129
+ end
130
+
131
+ def recursive_values(axes_sequence, current_index, cell_params=[])
132
+ if axis_number = axes_sequence[current_index]
133
+ (0...axes[axis_number].getPositions.size).map do |i|
134
+ cell_params[axis_number] = Java::JavaLang::Integer.new(i)
135
+ recursive_values(axes_sequence, current_index + 1, cell_params)
136
+ end
137
+ else
138
+ cell = @raw_cell_set.getCell(cell_params)
139
+ {
140
+ :value => cell.value,
141
+ :formatted_value => cell.formatted_value
142
+ }
143
+ end
144
+ end
145
+ end
146
+
147
+ end
148
+ end
@@ -0,0 +1,131 @@
1
+ # Taken from mondrian-olap: https://github.com/rsim/mondrian-olap/blob/master/lib/mondrian/olap/connection.rb
2
+ require 'rubiks/mondrian/cell_set'
3
+ require 'rubiks/mondrian/errors'
4
+
5
+ module ::Rubiks
6
+ module Mondrian
7
+
8
+ class Connection
9
+ def self.create(params)
10
+ connection = new(params)
11
+ connection.connect
12
+ connection
13
+ end
14
+
15
+ attr_reader :raw_connection, :raw_catalog, :raw_schema
16
+
17
+ def initialize(params={})
18
+ @params = params
19
+ @driver = params[:driver]
20
+ @connected = false
21
+ @raw_connection = nil
22
+ end
23
+
24
+ def connect
25
+ ::Rubiks::MondrianError.wrap_native_exception do
26
+ # hack to call private constructor of MondrianOlap4jDriver
27
+ # to avoid using DriverManager which fails to load JDBC drivers
28
+ # because of not seeing JRuby required jar files
29
+ cons = Java::MondrianOlap4j::MondrianOlap4jDriver.java_class.declared_constructor
30
+ cons.accessible = true
31
+ driver = cons.new_instance.to_java
32
+
33
+ props = java.util.Properties.new
34
+ props.setProperty('JdbcUser', @params[:username]) if @params[:username]
35
+ props.setProperty('JdbcPassword', @params[:password]) if @params[:password]
36
+
37
+ conn_string = connection_string
38
+ @raw_jdbc_connection = driver.connect(conn_string, props)
39
+
40
+ @raw_connection = @raw_jdbc_connection.unwrap(Java::OrgOlap4j::OlapConnection.java_class)
41
+ @raw_catalog = @raw_connection.getOlapCatalog
42
+ # currently it is assumed that there is just one schema per connection catalog
43
+ @raw_schema = @raw_catalog.getSchemas.first
44
+ @connected = true
45
+ true
46
+
47
+ # latest Mondrian version added ClassResolver which uses current thread class loader to load some classes
48
+ # therefore need to set it to JRuby class loader to ensure that Mondrian classes are found
49
+ # (e.g. when running mondrian-olap inside OSGi container)
50
+ current_thread = Java::JavaLang::Thread.currentThread
51
+ class_loader = current_thread.getContextClassLoader
52
+ begin
53
+ current_thread.setContextClassLoader JRuby.runtime.jruby_class_loader
54
+ @raw_jdbc_connection = driver.connect(conn_string, props)
55
+ ensure
56
+ current_thread.setContextClassLoader(class_loader)
57
+ end
58
+
59
+ @raw_connection = @raw_jdbc_connection.unwrap(Java::OrgOlap4j::OlapConnection.java_class)
60
+ @raw_catalog = @raw_connection.getOlapCatalog
61
+ # currently it is assumed that there is just one schema per connection catalog
62
+ @raw_schema = @raw_catalog.getSchemas.first
63
+ @connected = true
64
+ true
65
+ end
66
+ end
67
+
68
+ def connected?
69
+ @connected
70
+ end
71
+
72
+ def close
73
+ @raw_connection.close
74
+ @connected = false
75
+ @raw_connection = @raw_jdbc_connection = nil
76
+ true
77
+ end
78
+
79
+ def execute(query_string)
80
+ ::Rubiks::MondrianError.wrap_native_exception do
81
+ statement = @raw_connection.prepareOlapStatement(query_string)
82
+ ::Rubiks::Mondrian::CellSet.new(statement.executeQuery())
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ def connection_string
89
+ string = "jdbc:mondrian:Jdbc=#{quote_string(jdbc_uri)};JdbcDrivers=#{jdbc_driver};"
90
+ # by default use content checksum to reload schema when catalog has changed
91
+ string << "UseContentChecksum=true;" unless @params[:use_content_checksum] == false
92
+ if role = @params[:role] || @params[:roles]
93
+ roles = Array(role).map{|r| r && r.to_s.gsub(',', ',,')}.compact
94
+ string << "Role=#{quote_string(roles.join(','))};" unless roles.empty?
95
+ end
96
+ string << (@params[:catalog] ? "Catalog=#{catalog_uri}" : "CatalogContent=#{quote_string(catalog_content)}")
97
+ end
98
+
99
+ def jdbc_uri
100
+ "jdbc:#{@driver}://#{@params[:host]}#{@params[:port] && ":#{@params[:port]}"}/#{@params[:database]}"
101
+ end
102
+
103
+ def jdbc_driver
104
+ 'org.postgresql.Driver'
105
+ end
106
+
107
+ def catalog_uri
108
+ if @params[:catalog]
109
+ "file://#{File.expand_path(@params[:catalog])}"
110
+ else
111
+ raise ArgumentError, 'missing catalog source'
112
+ end
113
+ end
114
+
115
+ def catalog_content
116
+ if @params[:catalog_content]
117
+ @params[:catalog_content]
118
+ elsif @params[:schema]
119
+ @params[:schema].to_xml(:driver => @driver)
120
+ else
121
+ raise ArgumentError, "Specify catalog with :catalog, :catalog_content or :schema option"
122
+ end
123
+ end
124
+
125
+ def quote_string(string)
126
+ "'#{string.gsub("'","''")}'"
127
+ end
128
+ end
129
+
130
+ end
131
+ end
@@ -0,0 +1,56 @@
1
+ # Taken from mondrian-olap: https://github.com/rsim/mondrian-olap/blob/master/lib/mondrian/olap/error.rb
2
+ module ::Rubiks
3
+
4
+ class MondrianError < StandardError
5
+ NATIVE_MONDRIAN_ERROR_REGEXP = /^(org\.olap4j\.|mondrian\.|java\.lang\.reflect\.UndeclaredThrowableException\: Mondrian Error\:)/
6
+
7
+ # root_cause will be nil if there is no cause for wrapped native error
8
+ # root_cause_message will have either root_cause message or wrapped native error message
9
+ attr_reader :native_error, :root_cause_message, :root_cause
10
+
11
+ def initialize(native_error)
12
+ @native_error = native_error
13
+ get_root_cause
14
+ super(native_error.message)
15
+ add_root_cause_to_backtrace
16
+ end
17
+
18
+ def self.wrap_native_exception
19
+ yield
20
+ rescue NativeException => e
21
+ if e.message =~ NATIVE_MONDRIAN_ERROR_REGEXP
22
+ raise ::Rubiks::MondrianError.new(e)
23
+ else
24
+ raise
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def get_root_cause
31
+ @root_cause = nil
32
+ e = @native_error
33
+ while e.respond_to?(:cause) && (cause = e.cause)
34
+ @root_cause = e = cause
35
+ end
36
+ message = e.message
37
+ if message =~ /\AMondrian Error:(.*)\Z/m
38
+ message = $1
39
+ end
40
+ @root_cause_message = message
41
+ end
42
+
43
+ def add_root_cause_to_backtrace
44
+ bt = @native_error.backtrace
45
+ if @root_cause
46
+ root_cause_bt = Array(@root_cause.backtrace)
47
+ root_cause_bt[0,5].reverse.each do |bt_line|
48
+ bt.unshift "root cause: #{bt_line}"
49
+ end
50
+ bt.unshift "root cause: #{@root_cause.java_class.name}: #{@root_cause.message.chomp}"
51
+ end
52
+ set_backtrace bt
53
+ end
54
+ end
55
+
56
+ end
@@ -0,0 +1,107 @@
1
+ # Taken from mondrian-olap: https://github.com/rsim/mondrian-olap/blob/master/lib/mondrian/olap/cube.rb
2
+ # require 'rubiks/mondrian/cell_set'
3
+ # require 'rubiks/mondrian/errors'
4
+
5
+ module ::Rubiks
6
+ module Mondrian
7
+
8
+ class Member
9
+ def initialize(raw_member)
10
+ @raw_member = raw_member
11
+ end
12
+
13
+ attr_reader :raw_member
14
+
15
+ def name
16
+ @raw_member.getName
17
+ end
18
+
19
+ def full_name
20
+ @raw_member.getUniqueName
21
+ end
22
+
23
+ def caption
24
+ @raw_member.getCaption
25
+ end
26
+
27
+ def calculated?
28
+ @raw_member.isCalculated
29
+ end
30
+
31
+ def calculated_in_query?
32
+ @raw_member.isCalculatedInQuery
33
+ end
34
+
35
+ def visible?
36
+ @raw_member.isVisible
37
+ end
38
+
39
+ def all_member?
40
+ @raw_member.isAll
41
+ end
42
+
43
+ def drillable?
44
+ return false if calculated?
45
+ # @raw_member.getChildMemberCount > 0
46
+ # This hopefully is faster than counting actual child members
47
+ raw_level = @raw_member.getLevel
48
+ raw_levels = raw_level.getHierarchy.getLevels
49
+ raw_levels.indexOf(raw_level) < raw_levels.size - 1
50
+ end
51
+
52
+ def depth
53
+ @raw_member.getDepth
54
+ end
55
+
56
+ def dimension_type
57
+ case @raw_member.getDimension.getDimensionType
58
+ when Java::OrgOlap4jMetadata::Dimension::Type::TIME
59
+ :time
60
+ when Java::OrgOlap4jMetadata::Dimension::Type::MEASURE
61
+ :measures
62
+ else
63
+ :standard
64
+ end
65
+ end
66
+
67
+ def children
68
+ ::Rubiks::MondrianError.wrap_native_exception do
69
+ @raw_member.getChildMembers.map{|m| ::Rubiks::Mondrian::Member.new(m)}
70
+ end
71
+ end
72
+
73
+ def descendants_at_level(level)
74
+ ::Rubiks::MondrianError.wrap_native_exception do
75
+ raw_level = @raw_member.getLevel
76
+ raw_levels = raw_level.getHierarchy.getLevels
77
+ current_level_index = raw_levels.indexOf(raw_level)
78
+ descendants_level_index = raw_levels.indexOfName(level)
79
+
80
+ return nil unless descendants_level_index > current_level_index
81
+
82
+ members = [self]
83
+ (descendants_level_index - current_level_index).times do
84
+ members = members.map do |member|
85
+ member.children
86
+ end.flatten
87
+ end
88
+ members
89
+ end
90
+ end
91
+
92
+ def property_value(name)
93
+ if property = @raw_member.getProperties.get(name)
94
+ @raw_member.getPropertyValue(property)
95
+ end
96
+ end
97
+
98
+ def property_formatted_value(name)
99
+ if property = @raw_member.getProperties.get(name)
100
+ @raw_member.getPropertyFormattedValue(property)
101
+ end
102
+ end
103
+
104
+ end
105
+
106
+ end
107
+ end
@@ -0,0 +1,44 @@
1
+ require 'java'
2
+ require 'rubiks/mondrian/connection'
3
+ require 'rubiks/mondrian/errors'
4
+
5
+ Dir[File.expand_path('../mondrian/jars/*.jar', __FILE__)].each{ |jar| require jar }
6
+
7
+ # register Mondrian olap4j driver
8
+ Java::mondrian.olap4j.MondrianOlap4jDriver
9
+
10
+ module ::Rubiks
11
+ def self.connection
12
+ @connection ||= ::Rubiks::Mondrian.connection
13
+ end
14
+
15
+ module Mondrian
16
+ def self.connection
17
+ @connection ||= ::Rubiks::Mondrian::Connection.create(
18
+ config.merge(:catalog_content => ::Rubiks.schema.to_xml)
19
+ )
20
+ end
21
+
22
+ def self.config=(new_config)
23
+ @config = new_config
24
+ end
25
+
26
+ def self.config
27
+ @config ||= begin
28
+ if defined?(ActiveRecord)
29
+ ar_config = ActiveRecord::Base.connection.config
30
+
31
+ {
32
+ :driver => ar_config[:adapter],
33
+ :host => ar_config[:host],
34
+ :database => ar_config[:database],
35
+ :username => ar_config[:username],
36
+ :password => ar_config[:password]
37
+ }
38
+ else
39
+ {}
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,121 @@
1
+ module ::Rubiks
2
+
3
+ class NamedObject
4
+ def self.define(new_name=nil, options={}, &block)
5
+ instance = new(new_name, options)
6
+ instance.instance_eval(&block) if block_given?
7
+ instances[instance.name.to_sym] = instance
8
+ instance
9
+ end
10
+
11
+ def self.instances
12
+ @instances ||= Hash.new
13
+ end
14
+
15
+ def self.find_or_create(instance_name, options={}, &block)
16
+ return instances[instance_name.to_sym] if instances.has_key?(instance_name.to_sym)
17
+
18
+ new_instance = new(instance_name.to_s, options)
19
+ new_instance.instance_eval(&block) if block_given?
20
+ new_instance
21
+ end
22
+
23
+ def self.clear!
24
+ @instances = nil
25
+ end
26
+
27
+ def self.default
28
+ if instances.has_key?('default')
29
+ instances['default']
30
+ elsif instances.present?
31
+ instances.first[1]
32
+ end
33
+ end
34
+
35
+ def self.[](instance_name)
36
+ instances[instance_name.to_sym]
37
+ end
38
+
39
+ def initialize(new_name = nil, options = {})
40
+ @name = new_name.to_s if new_name.present?
41
+ @options = options.symbolize_keys
42
+ end
43
+
44
+ def name(new_value=nil)
45
+ @name = new_value.to_s if new_value.present?
46
+ @name ||= @options[:name] || 'default'
47
+ end
48
+
49
+ def icon_type(new_value=nil)
50
+ @icon_type = new_value.to_s if new_value.present?
51
+ @icon_type ||= @options[:icon_type]
52
+ end
53
+
54
+ def description(new_value=nil)
55
+ @description = new_value.to_s if new_value.present?
56
+ @description ||= @options[:description]
57
+ end
58
+
59
+ def visible(new_value=nil)
60
+ @visible = new_value.to_s unless new_value.nil?
61
+ @visible ||= @options.key?(:visible) ? @options[:visible].to_s : nil
62
+ end
63
+
64
+ def column(new_value=nil)
65
+ @column = new_value.to_s if new_value.present?
66
+ @column ||= @options[:column] || name
67
+ end
68
+
69
+ def caption(new_value=nil)
70
+ @caption = new_value if new_value.present?
71
+ @caption ||= @options[:caption] || name.titleize
72
+ end
73
+
74
+ def table(new_value=nil)
75
+ @table = new_value if new_value.present?
76
+ @table ||= @options[:table] || "view_#{name.tableize}"
77
+ end
78
+
79
+ def to_json(options={})
80
+ MultiJson.encode(json_hash, options)
81
+ end
82
+
83
+ def default_json_attributes
84
+ json_attrs = {
85
+ :name => name,
86
+ :caption => caption,
87
+ :description => description,
88
+ :icon_type => icon_type
89
+ }
90
+ json_attrs[:visible] = visible if visible.present? && visible == 'false'
91
+ json_attrs.delete_if { |key,value| value.nil? }
92
+ json_attrs.stringify_keys!
93
+ end
94
+ alias_method :json_hash, :default_json_attributes
95
+
96
+ def default_xml_attributes
97
+ xml_attrs = {
98
+ :name => caption,
99
+ :description => description
100
+ }
101
+ xml_attrs[:visible] = visible if visible.present? && visible == 'false'
102
+ xml_attrs.delete_if { |key,value| value.nil? }
103
+ end
104
+ alias_method :xml_hash, :default_xml_attributes
105
+
106
+ def to_xml(builder = nil)
107
+ return if name.blank?
108
+
109
+ builder = builder || new_builder
110
+
111
+ builder.__send__(name)
112
+ end
113
+
114
+ private
115
+
116
+ def new_builder
117
+ builder = Builder::XmlMarkup.new(:indent => 2) if builder.nil?
118
+ end
119
+ end
120
+
121
+ end
@@ -0,0 +1,28 @@
1
+ module ::Rubiks
2
+
3
+ class Schema < NamedObject
4
+ def cubes
5
+ @cubes ||= []
6
+ end
7
+
8
+ def cube(cube_name, options={}, &block)
9
+ cubes.push ::Rubiks::Cube.find_or_create(cube_name, options, &block)
10
+ end
11
+
12
+ def json_hash
13
+ hash = default_json_attributes
14
+ hash[:cubes] = cubes.map{ |c| c.json_hash } if cubes.present?
15
+ hash.stringify_keys!
16
+ end
17
+
18
+ def to_xml(builder = nil)
19
+ builder = builder || new_builder
20
+ builder.instruct!
21
+
22
+ builder.schema(:name => caption) do
23
+ cubes.each{ |cube| cube.to_xml(builder) }
24
+ end
25
+ end
26
+ end
27
+
28
+ end
@@ -1,3 +1,3 @@
1
1
  module ::Rubiks
2
- VERSION = '0.0.6'
2
+ VERSION = '0.1.0'
3
3
  end